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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53<!--GLOBAL html-->
<div id="js_parent" style="width: 400px; height: 200px;overflow: scroll;">
<div id="js_child" style="background-color: rgba(1,1,1,0.4);height: 400px;padding: 10px;">
<div>scroll element</div>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
<p>child</p>
</div>
</div>
<div class="placeholerBox">
<div style="padding: 30px 0;font-size: 12px;">滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 滚动鼠标 ................ </div>
</div>
<div class="showContainer js_showContainer"></div>
<style>
.showContainer {
position: fixed;
bottom: 10px;
padding: 0 20px 20px 0;
background-color: rgba(0,0,0,0.1);
font-size: 0;
}
.showBox_item {
width: 180px;
height: 100px;
padding: 10px;
display: inline-block;
vertical-align: top;
font-size: 13px;
line-height: 2;
margin-left: 20px;
margin-top: 20px;
background-color: #FFFFFF;
box-shadow: 0 0 5px 1px rgba(0,0,0,0.2);
transition: box-shadow 1s ease;
}
.showBox_item.scroll-active {
box-shadow: 0 0 10px 4px rgba(247, 14, 14, 0.7);
}
.placeholerBox {
height: 200vh;
}
#__vconsole {
display: none;
}
</style>
js: 滚动时会创建会在屏幕底部创建一个高亮元素显示正在滚动的元素 NodeName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71/* GLOBAL js */
function debounce(func, wait) {
var timer = null;
wait = wait || 200;
return function() {
var args = arguments;
var self = this;
timer && clearTimeout(timer);
// 先执行一次
// !timer && func.apply(self, args);
timer = setTimeout(function() {
func.apply(self, args);
timer = null;
}, wait);
};
}
function getSingleShowBox(fn, type) {
var result;
return function () {
if (!result) {
result = fn.apply(this, [type].concat(Array.prototype.slice.call(arguments)));
}
return result;
};
}
function createShowBox(type) {
var $html = $('<div class="showBox_item"><p>[ ' + type + ' ] scroll event - target:<span class="js_box"></span></p></div>');
$('.js_showContainer').append($html);
return $html.find('.js_box');
}
function scrollEvent(type) {
var getDivDom = getSingleShowBox(createShowBox, type);
var scrollEndFn = debounce(function($dom) {
$dom.closest('.showBox_item').removeClass('scroll-active');
}, 600);
return function(event) {
var $dome = getDivDom();
$dome.html(event.target.nodeName + '<br>' + new Date().toISOString())
.closest('.showBox_item').addClass('scroll-active');
scrollEndFn($dome);
// console.log('\n\n-------------------');
// console.log(type + ' scroll event');
// console.log('target:', event.target);
// console.log('currentTarget:', event.currentTarget);
// console.log('-------------------');
}
}
window.scrollEvent = scrollEvent;
// div scroll
document.getElementById('js_parent').addEventListener('scroll', scrollEvent('div'), false);
// body scroll
document.body.addEventListener('scroll', scrollEvent('body'), false);
// html scroll
document.documentElement.addEventListener('scroll', scrollEvent('html'), false);
// document scroll
document.addEventListener('scroll', scrollEvent('document'), false);
// window scroll
window.addEventListener('scroll', scrollEvent('window'), false)
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下规避这个问题。
关于元素尺寸计算,点击这里查看 浏览器视窗或者元素宽高计算