提示的窗口要一直位于浏览器的右下角,随页面滚动而滚动。


看起来很简单,当时想到了一种较为简单的方案,那就是使用 “position:fixed;”,利用固定定位实现,很简单,但是,存在兼容性问题,IE6(QS)/IE7(Q)/IE8(Q)下是不支持的。


那个东西还必须兼容IE6,于是我就想到了使用 JS来控制一个绝对定位的元素。通过改变它的位置来使它始终在右下角。


……


后来,用到了一个属性:scrollTop,这个值可以设置或获取位于页面最顶端和窗口中可见内容最顶端之间的距离。


如下图所示:



关于这个值的获取,我一开始是使用的 document.documentElement.scrollTop,后来,发现使用此值在标准模式下还行,但到了兼容性模式下,就必须使用 document.body.scrollTop来获取了。再后来,,发现document.documentElement.scrollTop在 Webkit浏览器Chrome和Safari下取到的值是0。


我决定好好的研究一下这个属性。



scrollTop的兼容性问题



Element.scrollTop 不是 W3C 规范的标准属性,最初被 IE 的 DHTML Object Model 引入,但已被目前各主流浏览器所支持。更多参见 MSDN:

scrollTop Property


Element.scrollTop 属性获取或者设置一个元素的内容已经滚动到其上边界的像素数。只有在元素具备垂直滚动条的时候此属性才有效。


而无论是 MSDN 还是 Mozilla Developer Center,均没有明确提及对于页面(即视口元素)的滚动条,其垂直与水平的位置需要通过哪一个 DOM 对象获取。


分析以下代码:  


<     script 
    > 
      
    var 
     d, str; window.onload  
    = 
     window.onscroll  
    = 
      
    function 
     () { d  
    = 
     document.getElementById( 
    " 
    d 
    " 
    ); str  
    = 
      
    " 
    <strong> 
    " 
      
    + 
     ((document.compatMode.toLowerCase().indexOf( 
    " 
    back 
    " 
    )  
    >= 
      
    0 
    )  
    ? 
      
    " 
    Quirks 
    " 
     :  
    " 
    Standards 
    " 
    )  
    + 
      
    " 
    </strong><br /> 
    " 
      
    + 
      
    " 
    document.documentElement.scrollTop: 
    " 
      
    + 
     document.documentElement.scrollTop  
    + 
      
    " 
    <br /> 
    " 
      
    + 
      
    " 
    document.body.scrollTop: 
    " 
      
    + 
     document.body.scrollTop; d.innerHTML  
    = 
     str; }  
    </ 
    script 
    > 
      
    < 
    body  
    style 
    ="font:12px Arial; _background-attachment:fixed; _background-image:url(about:blank);" 
    > 
      
    < 
    div  
    style 
    ="height:10000px;" 
    ></ 
    div 
    > 
      
    < 
    div  
    id 
    ="d" 
     style 
    ="position:fixed; top:0; left:0; _position:absolute; _top:expression(offsetParent.scrollTop); _left:expression(offsetParent.scrollLeft); background:#ddd;" 
    ></ 
    div 
    > 
      
    </ 
    body 
    >
当浏览器窗口滚动的时候,会在左上角显示出 scrollTop的值。


在Opera,IE,Firefox,Chrome和Safari中测试的汇总表:  


可见,scrollTop可以从document.body和 document.documentElement中获取。

在混杂模式下,所有浏览器均使用 document.body.scrollTop 获取页面的垂直滚动条的位置。所以,在混杂模式下不会出现兼容性问题。

而在标准模式下,Chrome 与 Safari 仍然使用 document.body.scrollTop,同时document.documentElement.scrollTop 的返回为 0。这时如果仅仅使用document.documentElement.scrollTop 获取页面垂直滚动条顶端位置,在 Chrome 和 Safari 中就会因为永久返回 0 导致页面功能异常(如,绝对定位的元素不能随页面的滚动条而滚动或位置有误)。


解决方式


所以,在获取这个值的时候,可以采用以下方法避免兼容性问题:


var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

另外,scrollLeft的情况跟scrollTop类似,有兴趣的童鞋可以研究研究。


引申阅读:


后来请较了一位朋友,把源码给挖出来了。

我们通过 WebKit 内核与 Gecko 内核的源代码中也可以看出 Chrome、Safari 与 Firefox 对页面的 scrollTop、scrollTop 获取方式的不同:

WebKit 内核: /WebCore/html/HTMLBodyElement.cpp  



int      HTMLBodyElement::scrollTop()  
    const 
     {  
    // 
     Update the document's layout. 
      
     Document 
    * 
     doc  
    = 
     document(); doc 
    -> 
    updateLayoutIgnorePendingStylesheets(); FrameView 
    * 
     view  
    = 
     doc 
    -> 
    view();  
    return 
     view  
    ? 
     adjustForZoom(view 
    -> 
    scrollY(), view) :  
    0 
    ; }  
    int 
     HTMLBodyElement::scrollLeft()  
    const 
     {  
    // 
     Update the document's layout. 
      
     Document 
    * 
     doc  
    = 
     document(); doc 
    -> 
    updateLayoutIgnorePendingStylesheets(); FrameView 
    * 
     view  
    = 
     doc 
    -> 
    view();  
    return 
     view  
    ? 
     adjustForZoom(view 
    -> 
    scrollX(), view) :  
    0 
    ; }


Gecko 内核: /content/base/src/nsGenericElement.cpp


Firefox 的 Gecko 内核源代码中通过对文档模式的判断决定通过哪个对象获取和设置 scrollTop、scrollLeft 属性。从注释中可以看到,Firefox 这么做是为了兼容 IE6 的标准模式与混杂模式对 scrollTop、scrollLeft 的处理。

nsNSElementTearoff::GetScrollInfo(nsIScrollableView      ** 
    aScrollableView, nsIFrame  
    ** 
    aFrame) { ...  
    if 
     ((quirksMode  
    && 
     mContent 
    -> 
    NodeInfo() 
    -> 
    Equals(nsGkAtoms::body))  
    || 
     ( 
    ! 
    quirksMode  
    && 
     mContent 
    -> 
    NodeInfo() 
    -> 
    Equals(nsGkAtoms::html))) {  
    // 
     In quirks mode, the scroll info for the body element should map to the  
    // 
     scroll info for the nearest scrollable frame above the body element  
    // 
     (i.e. the root scrollable frame). This is what IE6 does in quirks  
    // 
     mode. In strict mode the root scrollable frame corresponds to the  
    // 
     html element in IE6, so we map the scroll info for the html element to  
    // 
     the root scrollable frame. 
      
      
    do 
     { frame  
    = 
     frame 
    -> 
    GetParent();  
    if 
     ( 
    ! 
    frame) {  
    break 
    ; } scrollFrame  
    = 
     do_QueryFrame(frame); } ... }


而 WebKit 内核则没有针对文档模式进行判断,对页面滚动条信息通过 [Object HTMLBodyElement] 对象获取与设置。