【分享】如何正确的获取scrollTop/scrollLeft的值

WebAdvocate 2010-07-16 02:57:32
前些日子,在做一个类似信息提示窗口的时候,其中有个需求是这个样子的:
提示的窗口要一直位于浏览器的右下角,随页面滚动而滚动。

看起来很简单,当时想到了一种较为简单的方案,那就是使用 “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
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);
}
...
}

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

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


更多兼容性问题:【分享】浏览器兼容性问题目录
...全文
3090 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
cricket7th 2011-11-24
  • 打赏
  • 举报
回复
好东西,,正好能用上。谢谢
o0cheer0o 2011-05-30
  • 打赏
  • 举报
回复
百度了一下,到了这里,哈哈。研究ing~
nightgo11 2011-01-07
  • 打赏
  • 举报
回复
太感谢您了 收藏中
秀逗 2010-11-23
  • 打赏
  • 举报
回复
请问DIV的scrollLeft与scrollTop又该如何获取?
sean20032000 2010-09-14
  • 打赏
  • 举报
回复
学习到了谢谢
Milk- 2010-09-14
  • 打赏
  • 举报
回复
学习,山高水长,奋斗不止。
  • 打赏
  • 举报
回复
好,仔细研究
亥亥 2010-08-09
  • 打赏
  • 举报
回复
学习研究
mochimo 2010-07-20
  • 打赏
  • 举报
回复
楼主写的很好!
但是,这个问题的深层原因没有说明。

这牵扯到body和HTML元素哪个是视口的问题。

在这个问题上IE6 混杂模式下用body当做视口是错误的,因此才有了document.body.scroll... 方法。
阿非 2010-07-19
  • 打赏
  • 举报
回复
怎么取消推荐了?
hookee 2010-07-16
  • 打赏
  • 举报
回复
~~~~~~~~jF
jbz001 2010-07-16
  • 打赏
  • 举报
回复
路过,看看,不错~!
daniao77 2010-07-16
  • 打赏
  • 举报
回复
支持楼主
sijin 2010-07-16
  • 打赏
  • 举报
回复
学习到了谢谢

xyc20080109 2010-07-16
  • 打赏
  • 举报
回复
学习学习
ycdbd2009 2010-07-16
  • 打赏
  • 举报
回复
不错不错
lvgeneral 2010-07-16
  • 打赏
  • 举报
回复
好,大家好进步
ZJXIAOHUU 2010-07-16
  • 打赏
  • 举报
回复
学uu
lvgeneral 2010-07-16
  • 打赏
  • 举报
回复
学习,山高水长,奋斗不止。
dkmuss 2010-07-16
  • 打赏
  • 举报
回复
学习 研究用用
加载更多回复(3)

5,006

社区成员

发帖
与我相关
我的任务
社区描述
解读Web 标准、分析和讨论实际问题、推动网络标准化发展和跨浏览器开发进程,解决各种兼容性问题。
社区管理员
  • 跨浏览器开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧