博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于passive event listener的一次踩坑
阅读量:6892 次
发布时间:2019-06-27

本文共 3347 字,大约阅读时间需要 11 分钟。

你真的会用addEventListener吗?

没错,的确很标题党,但是我最近发现我的一个朋友(谁说是我的!)真的不太会用addEventListener

起因

我最近参与了一个开源项目,,Vue生态圈里的一个轮播组件,由加拿大的一个电商公司SSENSE开源的。然后刚好有人提了一个

Unable to preventDefault inside passive event listener due to target being treated as passive

也许有人在开发移动端应用的时候遇到过这个问题,我来为大家复现一下这个场景:

当我们给document添加了touch事件的监听器的时候,如果同时在handler内部调用了event.preventDefault(),这时候浏览器(Chrome 56+)就会报一个warning:Unable to preventDefault inside passive event listener due to target being treated as passive

这句警告翻译过来就是:不能给passive(被动的)事件监听器preventDefault,因为它被认为是passive。

什么意思呢?不着急,首先,我们来看一下什么叫passive event listener。但是在这之前,我们还是得复习一下addEventListener的第三个参数。

不为所知的第三个参数

当我们使用addEventListener的时候,我们一般的写法是以下:

target.addEventListener(event, handler)复制代码

相信还有人使用更完整的写法:

target.addEventListener(event, handler, false)复制代码

是的,相信很多人也已经知道了,addEventListener方法是有第三个参数的,我们时常传入一个false来作为这第三个参数。但是我们不传其实也一样,因为这第三个参数默认就是false,是不是觉得自己不明所以地写了很多冤枉代码还自以为很严谨?

哈哈,所以我们得来看看,这第三个参数到底是做什么的。

常规操作下,这第三个参数是一个布尔值,叫useCapture,也就是指在DOM树中,注册了该listener的元素,是否会先于它下方的任何事件目标,接收到该事件。

我们知道,DOM事件流(event flow)存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。如果useCapture设置为false,当前eventTarget就不会在捕获阶段接收该事件。浏览器默认我们不会在捕获阶段触发绑定事件的handler。

但是我相信还是有很多人没有认真看过addEventListener的文档,第三个参数其实并不一定是一个布尔值。他也可以是一个对象,一组配置。

{	capture: Boolean, // 表示`listener`会在该类型的事件捕获阶段传播到该`EventTarget`时触发	once: Boolean, // 表示`listener`在添加之后最多只调用一次。如果是`true`,`listener`会在其被调用之后自动移除	passive: Boolean, // 表示`listener`永远不会调用`preventDefault()`。如果`listener`仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告}复制代码

除以上之外,还有一个mozSystemGroup,我们暂时不讨论。

所以其实我们还可以在useCapture的基础上另外配置两个配置项。

once表示listener在添加之后最多只调用一次。如果是truelistener会在其被调用之后自动移除,这跟我们在jQuery时代的once方法比较像。

passive表示listener永远不会调用preventDefault()。如果listener仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。具体的我们接着讨论。

passive event listener

在具体讨论passive event listener之前,我们先普及一个。大家可以自己去看看英文文档,不过需要科学上网。

简而言之就是当我们在滚动页面的时候(通常是我们监听touch事件的时候),页面其实会有一个短暂的停顿(大概200ms),浏览器不知道我们是否要preventDefault,所以它需要一个延迟来检测。这就导致了我们的滑动显得比较卡顿。

从Chrome 51开始,passive event listener被引进了Chrome,我们可以通过对addEventListener的第三个参数设置{ passive: true }来避免浏览器检测这个我们是否有在touch事件的handler里调用preventDefault。在这个时候,如果我们依然调用了preventDefault,就会在控制台打印一个警告。告诉我们这个preventDefault会被忽略。

当我们给addEventListener的第三个参数设置了{ passive: true },这个事件监听器就被称为passive event listener

从Chrome 56开始,如果我们给document绑定touchmove或者touchstart事件的监听器,这个passive是会被默认设置为true以提高性能,具体。但是我们大多数人并不知道这点,并且依旧调用了preventDefault。这并不会导致什么页面崩溃级的错误,但是这可能导致我们忽略了一个页面性能优化的点,特别是在移动端这种更加重视性能优化的场景下。

兼容性

第三个参数是在近段时间才被调整为一组配置项,如果我们需要兼容旧版浏览器,我们需要写一些检测代码。

var passiveSupported = false;try {  var options = Object.defineProperty({}, "passive", {    get: function() {      passiveSupported = true;    }  });  window.addEventListener("test", null, options);} catch(err) {}复制代码

这段代码为passive属性创建了一个带有getter函数的options对象;getter设定了一个标识,passiveSupported,被调用后就会把其设为true。那意味着如果浏览器检查options对象上的passive值时,passiveSupported将会被设置为true;否则它将保持false。然后我们调用addEventListener()去设置一个指定这些选项的空事件处理器,这样如果浏览器将第三个参数认定为对象的话,这些选项值就会被检查。

你可以利用这个方法检查options之中任一个值。只需使用与上面类似的代码,为选项设定一个getter。然后,当你想实际创建一个是否支持options的事件侦听器时,你可以这样做:

someElement.addEventListener("mouseup", handleMouseUp, passiveSupported ? { passive: true } : false);复制代码

我们在someElement这里添加了一个mouseup。对于第三个参数,如果passiveSupportedtrue,我们传递了一个passive值为trueoptions对象;如果相反的话,我们知道要传递一个布尔值,于是就传递false作为useCapture的参数。

PS: 在vue-carousel的那个bug的最开始的PR里,我将addEventListenerpassive设置为了false,但是这其实不是最优解,最优解应该是去掉监听器里的preventDefault,我已经提了另一个PR来解决这个问题。

参考:

转载地址:http://lshbl.baihongyu.com/

你可能感兴趣的文章
Android学习笔记之如何使用圆形菜单实现旋转效果...
查看>>
Codeforces 461B. Appleman and Tree[树形DP 方案数]
查看>>
『PyTorch』第二弹_张量
查看>>
Oracle+PL+SQL从入门到精通.丁士锋.清华大学出版社.2012
查看>>
领扣-121/122 最佳买卖时机 Best Time to Buy and Sell MD
查看>>
客户机容易随机出现自动重启、游戏卡问题?不妨优化下BIOS中节能技术!
查看>>
Java数据类型BooleanDemo
查看>>
Android使用AttributeSet自定义控件的方法
查看>>
tomcat如何配置环境变量
查看>>
Java读取Unicode文件(UTF-8等)时碰到的BOM首字符问题,及处理方法
查看>>
用wget递归下载
查看>>
高刷新率大屏“吃鸡”更轻松,ROG枪神2 Plus游戏本体验
查看>>
还剩1天!全面屏手机就要步入90%屏占比时代
查看>>
昨天在杭州云栖小镇,发生了这件事
查看>>
百度智能运维的技术演进之路
查看>>
左耳朵耗子:分布式系统架构经典资料
查看>>
MOBA游戏从普通玩家到职业选手你需要什么
查看>>
助力职场诚信生态圈建设同盾科技发布“职信查”
查看>>
厦门长庚医院揭牌成为清华大学医学院教学医院
查看>>
外卖小哥莫名成10家公司监事 企业登记存监管漏洞
查看>>