被动事件监听器(Passive Event Listeners)

2,708 阅读2分钟

这是原文

这是译文

这是Chromium Blog

白话文

通俗一点讲, 被动事件监听器是针对移动端滚动体验的一次优化。

(以下所说的touchstart和touchmove均为绑定在document)

我们先研究下一次滚动 发生了肾么事了 :

明眼人已经瞅出来了, 滚动行为被阻塞了。

这是因为 touchstarttouchmovepreventDefault 会阻止这个滚动行为。浏览器一开始并不知道回调中是否有 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 );