html5实现文章阅读进度指示器例子

作者:简简单单 2015-01-03
近期我发现很多网站增加了一种能够显示出当前阅读位置的指示器(你阅读了多少依赖于你在这篇文章上向下拉动滚动条拉动了多少)。通常,这种指示器被用在博客的文章或者是长表单上,用于帮助阅读着了解他们还有多少就能完成这篇文章或者表单。

问题

要建立一个阅读位置指示器,我们需要回答如下两个问题:

  1. 网页的长度是多少?网页的长度与文档当前的长度相同,这可以通过javascript计算出来。
  2. 用户当前的阅读位置在哪?要获取用户当前的阅读位置可能需要进入用户的思维中去寻找了,在我们所能处理的给定的技术范围之内,这看起来像是人工智能和不可能实现的。
这让我们不得不去使用一种完全不同的方式去解决这个问题。

原则

这项技术的原则是基于用户不得不滚动滚到条到页面的底部的简单的一个事实。一旦用户滚动滚到条到了页面的底部,我们可以得到用户已经阅读完成了这篇文章。解决获取当前用户阅读位置这个问题的关键看起来就是解决滚动条滚动事件了。

假设用户从页面顶部开始阅读,而且在到达页面底部之前他只会一次性的滚动滚动条,我们将会回答下列问题:
  1. 用户需要滚动多久才能滚到到页面的底部?隐藏在用户视点之下的页面部分实际上就反映了用户需要滚动到页面底部的实际数量。这将成为max属性。
  2. 用户已经滚动了多少部分?这个可以通过文档内容在窗口中的位置到顶部的垂直偏移来进行计算,这个将成为我们的value属性。
html5实现文章阅读进度指示器例子
上图中的例子模拟了用户滚动窗口的行为,当用户向下滚动滚动条的时候,垂直偏移增加。
 
在浏览器的上下文中,document和window是两个不同的对象。window是一个浏览器中可见的区域(在上面例子中蓝色窗口中的部分),document实际上是window中载入的页面(例子中当前滚动的灰色框)。

标记

让我们从最基本的标记开始:

显式的指定value属性是非常重要的。否则,我们的进度条将会处于一个不可知的状态。我们不想给进度条的不可知状态添加不必要的css样式。因此,我们选择通过添加一个初始值以忽略该状态。刚开始,用户从页面的顶部开始阅读,因此,开始值被设置为0,默认情况下,最大的值是1(如果没有指定的话)。
要得到max属性正确的值,我们需要从document高度中减去window的高度。这只能通过javascript来进行处理,我们将在后面的部分再讨论它。
标记在文档中的位置是重度依赖于html文档中剩余元素是怎么放置的。通常,如果你的文档中没有固定位置(fixed)的容器的话,你可以把进度条元素放在body元素内部其它所有元素的顶部。

  

  

为指示器添加样式

我们想要让我们的指示器总是在页面的顶部显示,即使用户滚动窗口也是这样,我们将progress元素设置为fixed。另外,我们应该希望进度条的背景是transparent(透明)的,这样在页面滚动时就不会创建一个空的进度条阻碍用户视线。同时,这也会帮助我们处理好在javascript被禁用的时候浏览器的表现,后面我们会介绍这一点。

progress {
  /* Positioning */
  position: fixed;
  left: 0;
  top: 0;

  /* Dimensions */
  width: 100%;
  height: 5px;

  /* Reset the appearance */
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;

  /* Get rid of the default border in Firefox/Opera. */
  border: none;

  /* Progress bar container for Firefox/IE10+ */
  background-color: transparent;

  /* Progress bar value for IE10+ */
  color: red;
}
对于Blink/Webkit/Firefox, 我们需要使用厂商指定的前缀添加到进度条样式上,这里用来给进度条添加颜色。
progress::-webkit-progress-bar {
  background-color: transparent;}progress::-webkit-progress-value {
  background-color: red;
}
progress::-moz-progress-bar {
  background-color: red;
}

交互

用javascript计算浏览器的width/height属性是非常麻烦的,同时,在不同内核的浏览器中的表现是非常可怕的。幸运的是,jquery抽象了这些复杂的操作,使得我们可以以清晰的方法去计算window和document的度量。因此,在接下来的内容中,我们将会通过jquery来处理与用户之间的交互。

在开始之前,不要忘记在文档中添加jquery的类库。
我们需要用jquery去获取进度条元素的max和value属性。
max-它的值是通过document的高度减去window的高度获取的页面未显示出来的部分的高度。
var winHeight = $(window).height(),
  docHeight = $(document).height();
  max = docHeight - winHeight;
$(progress).attr('max', max);
value-刚开始,value属性的值是0(已经在标记中指出了)。然后,随着用户滚动滚动条,垂直方向文档到到窗口的高度将会增加,如果滚动条在页面的最顶部,或者是不可滚动的,偏移将会是0。
var value = $(window).scrollTop();
$(progress).attr('value', value);
提示: 代替使用$(document).height(),我们可以使用诸如section,article,或者是div放置文章的内容进行计算,这样可以获取更高的阅读位置的精确度。当我们的文章含有评论内容和页面的其它组成部分占用了页面超过50%的比例的时候,这一点是非常有用的。
现在,每一次用户滚动滚动条,我们需要重新计算value的值,并设置进度条的value属性。注意的是,max属性是不变的。
$(document).on('scroll', function() {
  value = $(window).scrollTop();
  progressBar.attr('value', value);
});
用户滚动的方向是不重要的,因为我们每次都会重新计算value的值(y方向的偏移)。
我们的代码需要在dom载入之后执行,这一点是非常重要的,否则,过早的计算window/document的高度会产生不可预知的结果。
$(document).on('ready', function() {  
  var winHeight = $(window).height(), 
      docHeight = $(document).height(),
      progressBar = $('progress'),
      max, value;

  /* Set the max scrollable area */
  max = docHeight - winHeight;
  progressBar.attr('max', max);

  $(document).on('scroll', function(){
     value = $(window).scrollTop();
     progressBar.attr('value', value);
  });
});
(或者是可以让这段代码在页面的底部加载,这样可以跳过使用document的ready调用。

浏览器兼容性

这需要我们建立一个能够在不同浏览器中能够拥有相同表现的阅读位置指示器,我们建立的这个阅读位置指示器能够在所有良好支持html5进度条元素的浏览器中正常工作。但是,这种支持仅在firefox16+,opera 11+, chrome,safari 6+,ie 10+的浏览器上可用。opera 11和12不支持改变进度条的颜色。因此,我们的进度条将会是默认的绿色。

边界问题

在很多情况下,我们上述的代码可能会崩溃或者是不能够正确的指示出用户阅读的位置,让我们看看这些情况。

document高度<=window高度

我们的代码假设了document的高度总是大于window的高度,但是,实际情况并不总是这样的。幸运的是,浏览器会帮助我们处理这种情况,当document的高度比window的高度小的时候,浏览器会返回window的高度。因此,docHeight和winHeight是相同的。

max = docHeight - winHeight; // equal to zero.
这会让进度条的max和value属性都是0。
因此,我们的进度条仍然是空的,而且我们的背景是透明的,这样页面中就不会出现进度条。这产生一种感觉就是当用户的视点能够充满整个页面的时候,我们没有必要再添加一个指示器了。
而且,因为文档的高度不会超过window的高度,所以滚动事件也是不会被触发的。因此,不需要做任何处理,我们的代码再处理这个边界问题的时候是足够健壮的。

用户改变window的尺寸

当用户改变window的尺寸的时候,window和document的高度将会发生改变。这意味着我们必须要重新计算max和value属性以反映出指示器当前正确的位置。我们将会通过resize事件处理器重新计算正确的位置。

$(window).on('resize', function() {
  winHeight = $(window).height(),
  docHeight = $(document).height();

  max = docHeight - winHeight;
  progressBar.attr('max', max);

  value =  $(window).scrollTop();
  progressBar.attr('value', value);
});

javascript被禁用

当javascript被禁用的时候,我们的进度条将会有一个最大值为1,当前默认值为0。

这意味着我们的进度条将会依旧是空的,而且不会影响到页面的其它部分。这样做是很好的,因为页面中没有指示器对于读者来说并不是一个很大的缺失。

旧的浏览器兼容

旧的不支持html5的进度条元素的浏览器将会忽略掉progress标记。然而,对于一些站点来说,提供始终如一的体验是非常重要的(该方案略,详见原文)。

性能

通常,给滚动事件添加事件处理器被认为是非常不好的实践,因为每次滚动的时候,浏览器都会试图去重绘出现的内容。在我们的例子中,dom的结构和样式是比较简单的,在页面滚动的时候不会有任何明显会注意到的延迟或者是滞后。然而,当我们放大这里,在我们的拥有复杂的dom结构的站点中实现的时候,滚动的体验将会是产生很多性能的缺失。

如果滚动性能成为了你面临的一个难以客服的大问题的话,你应该尽可能避免这个特性的使用或者尝试优化代码以避免不必要的重绘。

造成的困惑

我不是一个UX专家,但是在一些情况下,我们的指示器的位置和外观可能会是模棱两可的并且会误导用户。ajax驱动的站点比如Medium,Youtube等,使用相似的进度条指示下一个页面载入的进度。Chrome移动浏览器本地使用蓝色的进度条作为页面加载进度。现在,如果你在框架上添加一个进度指示器,我保证肯定有很多人需要花费很长时间去理解这个顶部的指示器是有什么作用的。

你必须权衡这个是否对你的用户是有好处的。
 
优点:
  1. 语义准确
  2. 没有引入书math库或者是复杂的计算
  3. 最小化必要的标记
  4. 无缝的兼容不支持html5进度条的浏览器
  5. 无缝的兼容禁用javascript的浏览器
缺点:
  1. 跨浏览器样式是复杂的
  2. 老浏览器的兼容性依赖于传统的div/span(s)标记技术去实现
  3. 性能影响(复杂的dom结构情况下)
  4. 与页面加载进度的进度条容易混淆,让用户很难理解

相关文章

精彩推荐