addEventListener()
我们常写的addEventListener() 的参数是这样的:
addEventListener(type, listener, useCapture)
比如:
addEventListener("click", fun, true)
实际上,第三个参数可以是boolean 也可以是options
addEventListener(type, listener, {
capture: false,
passive: false,
once: false
})
三个属性都是布尔类型的开关,默认值都为 false。其中 capture 属性等价于以前的 useCapture 参数;once 属性就是表明该监听器是一次性的,执行一次后就被自动 removeEventListener 掉,还没有浏览器实现它;passive 属性是本文的主角,Firefox 和 Chrome 已经实现.
很多移动端的页面都会监听 touchstart 等 touch 事件,像这样:
document.addEventListener("touchstart", function(e){
... // 浏览器不知道这里会不会有 e.preventDefault()
})
由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。视频里也说了,即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。
视频里还说了,有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。
所以passive:true的意思就是告诉浏览器,不会阻止默认事件,你放心的滚动吧,不用等待了。
那么怎么移除事件呢?
以前,在第三个参数是布尔值的时候,addEventListener(“foo”, listener, true) 添加的监听器,必须用 removeEventListener(“foo”, listener, true) 才能删除掉。因为这个监听器也有可能还注册在了冒泡阶段,那样的话,同一个监听器实际上对应着两个监听器对象(通过 getEventListeners() 可看到)。
那现在 addEventListener(“foo”, listener, {passive: true}) 添加的监听器该如何删除呢?答案是 removeEventListener(“foo”, listener) 就可以了,passive 可以省略,原因是:在浏览器内部,用来存储监听器的 map 的 key 是由事件类型,监听器函数,是否捕获这三者组成的,passive 和 once 不在其中,理由显而易见,一个监听器同时是 passive 和非 passive(以及同时是 once 和非 once)是说不通的,如果你添加了两者,那么后添加的不算,浏览器会认为添加过了:
addEventListener("foo", listener, {passive: true})
addEventListener("foo", listener, {passive: false}) // 这句不算
addEventListener("bar", listener, {passive: false})
addEventListener("bar", listener, {passive: true}) // 这句不算
所以说在 removeEventListener 的时候永远不需写 passive 和 once,但 capture 可能要:
addEventListener("foo", listener, {capture: true})
removeEventListener("foo", listener, {capture: true}) // {capture: true} 必须加,当然 {capture: true} 换成 true 也可以
我当时遇到这个是在性能优化分析的时候看到的:
是说我们的页面没有使用passive来提升页面滚动,那么下面是说我们该如何使用passive来提升页面滚动的做法。