阻止滚轮滚动事件
事件冒泡与事件捕获
- 事件触发过程分为 2 个阶段
- MDN[1]
- 事件捕获 事件捕获定义了事件首先由最外层的元素(window)接收,然后才是下级元素。
- 事件冒泡 事件冒泡是 IE 的开发团队提出的,它定义了事件首先由最具体的元素接收,然后逐级冒泡到上级元素。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>event</title>
<style>
body{
padding-top: 100px;
}
div {
border: 1px solid;
width: 50%;
height: 50%;
padding: 10px;
margin: auto;
text-align: center;
}
#d2 {
background-color: #ddd;
}
#d3 {
background-color: #888888;
}
</style>
</head>
<body>
<div id="d1">
1
<div id="d2">
2
<div id="d3">
3
</div>
</div>
</div>
</body>
<script>
function clickFn(e, msg) {
console.log('msg: ', msg);
}
const getById = (id) => document.getElementById(id);
const d1 = getById('d1');
const d2 = getById('d2');
const d3 = getById('d3');
// capture: Boolean,表示 listener 在那个阶段时触发。 默认值false表示冒泡阶段触发,true 表示捕获阶段触发
d1.addEventListener('click', (event) => clickFn(event, '1捕获阶段'), { capture: true })
d1.addEventListener('click', (event) => clickFn(event, '1冒泡阶段'), { capture: false })
d2.addEventListener('click', (event) => clickFn(event, '2捕获阶段'), { capture: true })
d2.addEventListener('click', (event) => clickFn(event, '2冒泡阶段'), { capture: false })
d3.addEventListener('click', (event) => clickFn(event, '3捕获阶段'), { capture: true })
d3.addEventListener('click', (event) => clickFn(event, '3冒泡阶段'), { capture: false })
</script>
</html>
总结简单回顾下事件触发流程
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
preventDefault 与 stopPropagation
- preventDefault :阻止默认事件,如
a标签的跳转,滚轮的滚动。 - stopPropagation : 阻止事件冒泡。
未阻止事件冒泡的情况
function clickFn(e, msg) {
console.log('msg: ', msg);
}
const getById = (id) => document.getElementById(id);
const d1 = getById('d1');
const d2 = getById('d2');
const d3 = getById('d3');
// capture: Boolean,表示 listener 在那个阶段时触发。 默认值false表示冒泡阶段触发,true 表示捕获阶段触发
d1.addEventListener('click', (event) => clickFn(event, '1冒泡阶段'), { capture: false })
d2.addEventListener('click', (event) => clickFn(event, '2冒泡阶段'), { capture: false })
d3.addEventListener('click', (event) => clickFn(event, '3冒泡阶段'), { capture: false })
未阻止事件冒泡的情况 点击 div3 事件触发顺序是 从 3->2->1 一次触发,也就是每个 div 上的相同事件都会被触发
阻止事件冒泡的情况
function clickFn(e, msg) {
stop(e)
console.log('msg: ', msg);
}
function stop(e) {
const id = e.target.id;
switch (id) {
case 'd3':
console.log(id);
e.stopPropagation();
break;
case 'd2':
console.log(id);
e.stopPropagation();
break;
}
}
const getById = (id) => document.getElementById(id);
const d1 = getById('d1');
const d2 = getById('d2');
const d3 = getById('d3');
d1.addEventListener('click', (event) => clickFn(event, '1冒泡阶段'), { capture: false })
d2.addEventListener('click', (event) => clickFn(event, '2冒泡阶段'), { capture: false })
d3.addEventListener('click', (event) => clickFn(event, '3冒泡阶段'), { capture: false })
阻止事件冒泡的情况 点击 div3 事件触发顺序是 div3 上所绑定的事件,div2,div1 上的事件没有被触发
modal 滚轮事件
问题:在 modal 弹窗中滚动引起页面滚动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>wheel</title>
<style>
.modal {
position: absolute;
left: 50%;
top:50%;
transform: translate(-50%,-50%);
width: 400px;
height: 300px;
border: 1px solid;
overflow: scroll;
text-align: center;
}
</style>
</head>
<body>
<!-- 60个br -->
<div>
<br>
<!-- br 省略 -->
...
</div>
<!-- 20个br -->
<div class="modal">
<!-- br 省略 -->
<br>
123
<br>
</div>
</body>
</html>
解决 1: 阻止滚轮事件冒泡 ,触发事件的 dom 元素不会向上传播事件,页面就不会触发滚动事件了
const modal = document.querySelector('.modal');
modal.onmousewheel = function (e) {
console.log('modal');
e.stopPropagation();
}
并未成功阻止页面滚动,滚动事件还是向上传播
解决 2: 阻止默认事件
const modal = document.querySelector('.modal');
modal.onmousewheel = function (e) {
console.log('modal');
e.preventDefault();
}
modal 不再响应滚动,页面也不再滚动,阻止了滚动默认事件。 完成 50%,剩下需要让 modal 滚动
event.deltaY,deltaY属性在向下滚动时返回正值,向上滚动时返回负值,否则为 0。event.target.scrollTop此属性可以获取或者设置对象的最顶部到对象在当前窗口显示的范围内的顶边的距离,也就是元素滚动条被向下拉动的距离。 通过e.target.scrollTop = e.deltaY + e.target.scrollTop设置 scrollTop 来实现页面滚动
const modal = document.querySelector('.modal');
modal.onmousewheel = function (e) {
console.log('modal');
const t = e.target.scrollTop
const deltaY = e.deltaY;
e.target.scrollTop = e.deltaY + e.target.scrollTop;
e.preventDefault();
}
阻止弹出层 带动页面滚动完成。当然还可以继续完善兼容性。节流等
参考资料
[1]MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener