touchstart 事件中 ev.preventdefault 失效
问题补充:touchstart 事件中 ev.preventdefault 不能阻止默认滚动事件?
事件执行机制
js事件执行分为三个阶段,捕获阶段,获取目标,冒泡阶段。
- 捕获阶段 (从根节点开始顺着目标节点构建一条事件路径,
即事件由页面元素接收,逐级向下,到具体的元素
) - 目标阶段 (到达目标节点,
即元素本身
) - 冒泡阶段 (从目标节点顺着捕获阶段构建的路径回去,
即跟捕获相反具体元素本身,逐级向上,到页面元素
)
举个例子说明一下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv">Click me!</div>
</body>
<script>
document.body.addEventListener('click', function(ev){
console.log("捕获阶段,点中了body")
}, true)
document.querySelector("#myDiv").addEventListener('click', function(ev){
console.log("捕获阶段,点中了div#myDiv")
}, true)
document.body.addEventListener('click', function(ev){
console.log("冒泡阶段,点中了body")
})
document.querySelector("#myDiv").addEventListener('click', function(ev){
console.log("冒泡阶段,点中了div#myDiv")
})
</script>
</html>
得到结果:
捕获阶段,点中了body
捕获阶段,点中了div#myDiv
冒泡阶段,点中了div#myDiv
冒泡阶段,点中了body
事件监听器
DOM规范初期,定义了三个参数
type: 监听事件
listener:监听事件执行函数
useCapture:捕获阶段/冒泡阶段(true/false) 执行
addEventListener(type, listener, useCapture)
之后因为事件在捕获阶段执行的概率少之又少,useCapture在事件中也可以省略
addEventListener(type, listener[, useCapture])
2015年W3C对DOM事件监听器做了一次修改
addEventListener(type, listener[, useCapture ||, options])
options配置项
options: {
capture: false,
passive: false,
once: false
}
capture(等同useCapture)
默认 false
表示 listener
会在该类型的事件捕获阶段传播到该 EventTarget
时触发
passive
默认false
设置为true时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端 将会忽略它并抛出一个控制台警告,passive为true时,执行e.preventDefault()方法,报错(由于目标被视 为被动,无法在被动事件侦听器中阻止默认)
once
是否只执行一次
问题解决(touchstart 事件中 ev.preventdefault 失效)
目前已经了解了事件部分机制,再来说说 preventdefault
与 passive
在浏览器中,即使给触摸事件添加一个空函数监听器也会导致浏览器卡顿,而实际操作过程中调用preventDefault()概率占有20%,由于浏览器无法事先知道监听器是否会调用preventDefault(),它不得不执行完整的监听函数才能决定是否滚动页面,passive的实现是让浏览器无需考虑事件是否会阻止其默认行为。
在之前的描述中,passive的默认值为false,这个并不准确,在firefox进行了两类测试。
// a标签preventDefault测试
document.querySelector('a.element').addEventListener('click',function(ev){ev.preventDefault()}, false)
// 此时ev.preventDefault()对该元素默认点击行为进行了阻止
// 表现为 passive: false
// 页面滚动测试
document.addEventListener('DOMMouseScroll', function(ev){ev.preventDefault()}, false)
// 此时会报出警告忽略一个注册为 ‘passive’ 的监听器的事件类型为 ‘DOMMouseScroll’ 的 ‘preventDefault()’ 调用。
// 表现为 passive: true
// 页面滚动阻止测试
document.addEventListener('DOMMouseScroll', function(ev){ev.preventDefault()}, {passive: false});
// 此时ev.preventDefault()对该元素默认滚动行为进行了阻止
// 表现为 passive: false
可以看出,页面滚动已忽略preventDefault事件监听,passive 默认为 true
touchstart 事件中 ev.preventdefault 失效问题,则需要开发人员对页面监听事件手动添加{passive: false},目前已经可以阻止页面滚动事件,但新的问题又出现了,添加滚动行为阻止会导致总是阻止页面滚动。
阻止页面滚动以及移除页面滚动
阻止页面滚动我们已经做的很好,通过配置{passive: false},即可阻止滚动,但是我们实际情况可能是在弹出框存在时阻止页面滚动,关闭弹出框可继续滚动,因此,不能使用匿名函数设置监听事件。
举个例子:
/**
* 使用匿名函数
* 阻止页面滚动 > Y
* 还原页面滚动 > N
*/
document.addEventListener('DOMMouseScroll', function(ev){ev.preventDefault()}, {passive: false});
document.removeEventListener('DOMMouseScroll', function(ev){ev.preventDefault()}, {passive: true});
/**
* 使用具名函数
* 阻止页面滚动 > Y
* 还原页面滚动 > Y
*/
function stopScroll(ev){ev.preventDefault()}
document.addEventListener('DOMMouseScroll', stopScroll, {passive: false});
document.removeEventListener('DOMMouseScroll', stopScroll, {passive: true});
若有好的想法或意见,请不吝赐教~
参考文档:
https://www.cnblogs.com/wxcbg/p/10452985.html
https://www.cnblogs.com/ziyunfei/p/5545439.html
https://blog.csdn.net/qq_38280242/article/details/108411169
https://blog.csdn.net/qq_42098849/article/details/104582160