scroll
事件最熟悉不过的,我自己其实一直有一些疑问:
scroll
事件不支持冒泡,那 window 上的scroll
事件又是如何捕获到的?- 为何可以对 document 添加
scroll
事件,怎么又冒泡到了 window 上? - window scrollTop 的值到底在
html
还是body
上 ? - …
原本想找到一些官方的规范或者说明文档,但是并没有 …
以下所有测试都是在DOCTYPE
声明为html5
的情况下,旧的不在关注
Scroll Bubbles
关于 Scroll
事件冒泡机制这里先做个说明:
Element
元素 scroll 事件确实不冒泡
传播事件,也就是我们的日常体验,比如一个div元素内的滚动事件你无法去委托事件。Ducument
文档 scroll 事件是冒泡事件
,所以在window上去监听scroll
事件,eventtarget
为document
这里要注意的就是 Ducument文档
的scroll事件是支持冒泡的,可以在 测试Demo 中代码运行。
测试Demo
下面做一个简单的例子:(可以点击代码里面的运行悬浮按钮,可在浏览器直接运行当前代码)
分别监听了 div
body
html
document
window
的 scroll事件
1 | <!--GLOBAL html--> |
js: 滚动时会创建会在屏幕底部创建一个高亮元素显示正在滚动的元素 NodeName
1 | /* GLOBAL js */ |
Document scroll 事件来自哪里
这个问题我很想找到相对官方的说明,但是没找到,下面最要记录下日常工作中的体验。
这里在前面已经说了,window scroll
的事件是由 document scroll
事件所冒泡上去的,那弄清 document scroll
来自于哪里,实际上问题就清楚了。
onscroll 事件
如果直接在 html
、body
上绑定 onscroll
事件,是不是就可以确定scroll事件是来自哪里?
如下点击 运行单个
1 | // body onscroll |
这里在ie下会触发 html onscroll
,非ie都统一为 body onscroll
,target
依旧为 document
触发的 onscroll
事件和 window.onscroll
相同引用,也就是说:
ie: window.onscroll === document.documentElement.onscroll
非ie: window.onscroll === document.body.onscroll
body
或 html
可以通过 onscroll
捕获滚动事件,但 addEventListener
注册的 scroll
事件却捕获不了,啊哈太棒了,还是没找到说明文档
这里对触发
onscroll
的元素,暂且理解为滚动元素也就是滚动条所在的元素 (后面还是会解释不通…)
document scrollTop
scrollTop
如果正常理解哪里触发滚动事件,那就在哪里取 scrollTop
的值。
如果按上面描述的 非ie
在 body
上触发,那就应该是 document.body.scrollTop
,但实际并不是这样,这里 ie 和 非ie 都是在 html
上取值。
有些场景会在body上,据说不申明 DOCTYPE
时,所以看你需不需要兼容:
1 | document.documentElement.scrollTop || document.body.scrollTop |
另外还有一个 window.pageYOffset
来直接获取页面滚动条高度,兼容性也是妥妥的,只不过是只读,如果需要设置滚动条高度还是要用上面的。
不同浏览器处理方式不太一样,(
target
已经很清楚就是在document
文档触发的滚动事件了 orz…)
最后
原本是觉得这些事件触发和scrollTop取值有些怪怪的,想弄清楚,不过没找到相关的 官方文档
,那就这样吧…
- ie:
window.onscroll === document.documentElement.onscroll
- 非ie:
window.onscroll === document.body.onscroll
- 获取页面滚动条高度:
window.pageYOffset
||document.documentElement.scrollTop
- 设置页面滚动条高度:
document.documentElement.scrollTop = x
,使用window.scrollTo(x, y)
兼容不太行 window
捕获的scroll
事件是由document
冒泡的
其它补充
把html元素作为滚动元素而不触发document的滚动事件,好像做不到。
把body元素作为滚动元素而不触发document的滚动事件,可以如下。
运行同组运行全部运行单个1
2
3html, body {height: 100%;}
html {overflow: hidden;}
body {overflow: auto;}移动端烦人的弹窗滚动穿透,如果body没有滚动条就可以了,所以如果新项目可以直接让body没有滚动条,直接顶层div是做滚动,弹窗弹在body下规避这个问题。
关于元素尺寸计算,点击这里查看 浏览器视窗或者元素宽高计算