chrome监听touch事件无法调用preventDefault

1,179 阅读2分钟

问题

最近在做移动端项目时发现在监听的touch事件中加入event.preventDefault()时报错误:

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080

代码大体如下:

document.addEventListener('touchstart', function(event) {
    event.preventDefault();
}, false);

新的addEventListener api:

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);

如果我们在 touchstart 事件调用 preventDefault 会怎样呢?这时页面会禁止,不会滚动或缩放。那么问题来了:浏览器无法预先知道一个监听器会不会调用 preventDefault(),它需要等监听器执行完后,再去执行默认行为,而监听器执行是要耗时的,这样就会导致页面卡顿。

这段翻译的太专业了,你可以这么理解:当你触摸滑动页面时,页面应该跟随手指一起滚动。而此时你绑定了一个 touchstart 事件,你的事件大概执行 200 毫秒。这时浏览器就犯迷糊了:如果你在事件绑定函数中调用了 preventDefault,那么页面就不应该滚动,如果你没有调用 preventDefault,页面就需要滚动。但是你到底调用了还是没有调用,浏览器不知道。只能先执行你的函数,等 200 毫秒后,绑定事件执行完了,浏览器才知道,“哦,原来你没有阻止默认行为,好的,我马上滚”。此时,页面开始滚。

在 Android 版 Chrome 浏览器的 touch 事件监听器的页面中,80% 的页面都不会调用 preventDefault 函数来阻止事件的默认行为。在滑动流畅度上,有 10% 的页面增加至少 100ms 的延迟,1% 的页面甚至增加 500ms 以上的延迟。

也就是说,当浏览器等待执行事件的默认行为时,大部分情况是白等了。如果 Web 开发者能够提前告诉浏览器:“我不调用 preventDefault 函数来阻止事件事件行为”,那么浏览器就能快速生成事件,从而提升页面性能。

Chrome 51 和 Firefox 49 已经支持 passive 属性