白话文
通俗一点讲, 被动事件监听器是针对移动端滚动体验的一次优化。
(以下所说的touchstart和touchmove均为绑定在document)
我们先研究下一次滚动 发生了肾么事了
:
明眼人已经瞅出来了, 滚动行为被阻塞了。
这是因为 touchstart
和 touchmove
的 preventDefault
会阻止这个滚动行为。浏览器一开始并不知道回调中是否有 preventDefault
,只能等回调执行完毕。
后来呢chrome做了个调查发现,页面注册了touchstart和touchmove的网站有80%并没有阻止滚动,这80%的事件中有10%会将滚动开始的时间推迟超过100ms,有1%的滚动会发生超过500ms的灾难性延迟。
这不能忍啊,能不能让浏览器早点知道会不会阻塞滚动,passive
应运而生。
passive使用
passive使用依赖于addEventListener
,很多人使用时只知道第3个参数是useCapture
。我们再看一下文档发现:
target.addEventListener(type, listener [, options]);
target.addEventListener(type, listener [, useCapture]);
target.addEventListener(type, listener [, useCapture, wantsUntrusted This API has not been standardized. ]);
第一种就是我们这里需要讨论的。options
提供3个参数 capture
once
passive
。
capture
是否开启事件捕获
once
是否只执行一次
passive
是否开启被动事件监听器, 如果设置为true,表明回调不会调用 preventDefault()
,如果代码中确实调用了,不会生效并且会生成一个警告。
某些浏览器(特别是Chrome和Firefox)已将文档级节点Window,Document和Document.body上的touchstart和touchmove事件的Passive选项的默认值更改为true
passive
开启后的流程对比
未开启
已开启
兼容性
chrome 51+
Edge 18+
firfox
ie不兼容
这里有个MDN给出的polyfill
let passiveIfSupported = false;
try {
window.addEventListener("test", null,
Object.defineProperty(
{},
"passive",
{
get: function() { passiveIfSupported = { passive: true }; }
}
)
);
} catch(err) {}
window.addEventListener('scroll', function(event) {
/* do something */
// can't use event.preventDefault();
}, passiveIfSupported );