fixed-floating-element-cn-20100618

今天在一个外国网站上看到有人研究并做出了类似Apple Store 定制页面右边的 Summary 框的效果。只要简单的几句 CSS 和 jQuery 就可以实现。看到别人的研究过程和写出的代码,感到很受启发。

跟随用户漂浮的元素已经不新鲜了,10 年前就已经有这样的网页元素出现了。可是这个苹果做的效果却非常好,移动起来十分平滑流畅不跳动,而且没有多余的花哨效果,简洁而实用。

其实它的原理并不复杂。一开始这个框是相对于父元素用 position: absolute 定位的。当用户向下滚动浏览器窗口时,改为 position: fixed 即相对于浏览器窗口定位,这样就可以让这个框跟随用户滚动了。

以下是原作者给出的范例。先是CSS和Markup:

<div id="comments">
  <ol>
    <li>
      Here be the comments from visitors...
    </li>

    <li>
      etc...
    </li>

  </ol>
</div>

<div id="commentWrapper">
  <div id="comment">
  </div>

</div>

#commentWrapper 是实际漂浮的div的父元素,它始终是 position: absolute,这使得漂浮框的x方向上的定位始终由它来决定。这样就不会在将 #comment 设置为 position: fixed 时,x方向上的定位变成参照浏览器窗口而造成左右跳动的问题。

下面是JavaScript部分:

首先,载入jQuery。

然后,获取刚开始时,漂浮元素在y方向上的位置top。

var top = $('#comment').offset().top - parseFloat($('#comment').css('margin-top').replace(/auto/,0));

jQuery的offset()返回选定元素相对于文档的坐标,这里取y方向的值,减去margin-top,得到漂浮元素实际的位置。

jQuery的css(‘margin-top’)返回的值是“20px”,为了得到数字值,要使用parseFloat()来转换。这里replace(/auto/,0)的作用是,如果#comment没有设置margin-top,那么css()会返回“auto”,这无法被正确转换成数字而造成错误,所以要先把“auto”替换成“0”。

最后,获取用户浏览器滚动到的位置y,然后比较top和y。如果浏览器滚动到了漂浮框顶部以下了,就设定position: fixed;否则设定position: absolute。这个判断过程可以绑定在窗口的scroll事件上。

以下是完整的JavaScript:

$(document).ready(function () {
var top = $('#comment').offset().top - parseFloat($('#comment').css('marginTop').replace(/auto/, 0));
$(window).scroll(function (event) {
    // what the y position of the scroll is
    var y = $(this).scrollTop();    // whether that's below the form
    if (y >= top) {
        // if so, ad the fixed class
        $('#comment').addClass('fixed');
    } else {
        // otherwise remove it
        $('#comment').removeClass('fixed');
    }
});

现在,漂浮框可以跟随用户流畅平滑地移动了。看看Demo

这个实现过程有两个要点:一是要准确地计算初始时漂浮框所在的y方向的位置;二是要用一个始终是position: absolute的父级div把漂浮框的div包起来,并在这个父级div上设置x方向的定位,以防止漂浮框左右跳动。

参考:jQuery Document

更新(09/02/2011):这个方法有一个很不完善的地方。如果漂浮元素处于 position:fixed 状态时,用户更改了浏览器窗口的宽度,这时漂浮元素的位置就会出错。所以必须在浏览器窗口大小发生变化时,重新计算其与浏览器窗口左边框的距离。详见我的新文章