addEventListener 中的 passive 用法
引出问题
一个很简单的需求 页面有一张小图 点击是个 swiper 实现的图集 同时有一个灰色的蒙层 蒙层底部页面不可滑动 关闭蒙层 页面可恢复正常
实现方式
1 | function bodyScroll(event){ |
移动端的效果如下
android
ios
我不知道为什么无效 直到我在模拟器上看到了
啊哦 报错了🦢
Unable to preventDefault inside passive event listener due to target being treated as passive
来自 google 的解释 https://developers.google.com/web/updates/2017/01/scrolling-intervention
大概的意思是说
1 | 由于浏览器必须要在执行事件处理函数之后,才能知道有没有调用过 preventDefault() ,这就导致了浏览器不能及时响应滚动,略有延迟。 |
这就导致了这个问题
1 | 如果在以上这 3 个元素的 touchstart 和 touchmove 事件处理函数中调用 e.preventDefault() ,会被浏览器忽略掉,并不会阻止默认行为 |
所以出现了以上视频中问题
解决方案
那么我们如何来解决这个问题 即不要让浏览器忽略掉 e.preventDefault ()?
- window.addEventListener(‘touchmove’, func, { passive: false })
设置 passive: false 之后的结果
android
ios
浏览器
问题完美解决
1 | 你看到这里可以结束了 如果你还想再了解一点点 |
addEventListener(type, listener[, useCapture ])
addEventListener(type, listener[, options ])
1 | 扩展新的选项,从而自定义更多的行为,目前规范中 options 对象可用的属性有三个: |
addEventListener(type, listener, {
capture: false, 等价于 useCapture 默认值 false
passive: false, 是否让阻止默认事件失效 true: 失效 false:不失效
once: false // 表明该监听器是一次性的,执行一次后就被自动 removeEventListener 掉,还没有浏览器实现它 默认值 false
})
1 |
|
你可以直接省略第三个参数
window.removeEventListener(‘touchmove’, func)
如果添加了 第一个参数 capture 可以这样移除
window.removeEventListener(‘touchmove’, func, true)
window.removeEventListener(‘touchmove’, func, {capture :true})
为什么会有 passive 这个概念
像这样儿的代码
1 | document.addEventListener("touchstart", function(e){ |
由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault () 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault (),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。视频里也说了,即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。
有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是 “顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了