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

2023-05-16

最近做项目经常在 chrome 的控制台看到如下提示:

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

于是 Google 了一番,找到这篇文章,有了详细解释。Making touch scrolling fast by default

简而言之:

  • 由于浏览器必须要在执行事件处理函数之后,才能知道有没有掉用过 preventDefault(),这就导致了浏览器不能及时响应滚动,略有延迟。
  • 所以为了让页面滚动的效果如丝般顺滑,从 chrome56 开始,在 window、documentbody 上注册的touchstarttouchmove 事件处理函数,会默认为是 passive: true。浏览器忽略 preventDefault() 就可以第一时间滚动了。

举例:

wnidow.addEventListener('touchmove', func) 效果和下面一句一样
wnidow.addEventListener('touchmove', func, { passive: true })

这就导致了一个问题:

如果在以上这 3 个元素的 touchstarttouchmove 事件处理函数中调用 e.preventDefault() ,会被浏览器忽略掉,并不会阻止默认行为。

测试:

    body {
      margin: 0;
      height: 2000px;
      background: linear-gradient(to bottom, red, green);
    }
// 在 chrome56 中,照样滚动,而且控制台会有提示,blablabla
window.addEventListener('touchmove', e => e.preventDefault())

那么如何解决这个问题呢?不让控制台提示,而且 preventDefault() 有效果呢?
两个方案:
1、注册处理函数时,用如下方式,明确声明为不是被动的

window.addEventListener('touchmove', func, { passive: false })

2、应用 CSS 属性 touch-action: none; 这样任何触摸事件都不会产生默认行为,但是 touch 事件照样触发。
touch-action 还有很多选项,详细请参考touch-action

[注]未来可能所有的元素的 touchstart touchmove 事件处理函数都会默认为 passive: true


相信如果用谷歌浏览器做移动端页面的时候

touch事件的时候应该遇到过这个东东吧

documet.addEventListener("touchstart",function(){
  console.log(123);
});
[Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive.

翻译过来就是

违反:没有添加被动事件监听器来阻止’touchstart‘事件,请考虑添加事件管理者’passive’,以使页面更加流畅。

出现如上提示这可能是由于console的过滤器选择了Verbose

于是你检查了代码 发现并没有问题 那么这到底是啥呢

强迫症的我 上网百度了 一下

于是就有所了解

以前的监听器都是这样的

element.addEventListener("touchstart",fn,true|false); 

true 是事件捕获阶段执行

false 是事件冒泡阶段执行

这里不细说

没有第三个参数的时候默认为false

第三个参数还可以是对象

element.addEventListener("touchstart",fn,
{
capture: Boolean, passive: Boolean, once: Boolean}
}); 

第一个参数的意思 true|false 事件捕获阶段冒泡阶段

第二个参数 true|flase 不能调用 | 可以调用preventDefault()

第三个参数 once true|false 只能执行一次fn | 不限制

那问题来了 为什么要使用对象 并且要用passive呢 是因为事件里面的fn执行时需要时间滴

你想呀 执行代码的时候 比如 mousewheel 的时候 鼠标滚轮让滚动条动 可是你调用

preventDefault() 取消了事件的默认行为 那你说 它到底该动还是不动,浏览器一脸懵逼

它只有在fn里面的代码执行完之后才会知道到底要不要取消默认行为 这样等待的时间不就

白白浪费掉了吗 是性能低下 在执行fn之前就告诉 它 是否取消默认行为

这不就你知我长短 我知你深浅了吗

由于这个只有谷歌有 所以兼容处理 不认识的大神写的

try{
 var passiveSupported=false;

 var opts=Object.defineProperty({},"passive",{
 
   get:function(){
  passiveSupported=true;
}
});
   document.addEventListener("自己决定",null,opts);
}
catch(e){

}
document.addEventListener("touchstart",fn,passiveSupported?{"passive":true}:false);

这么看不得劲

挨张图片
在这里插入图片描述
有的人可能不知道 Object.defineProperty()

我就说在这需要用知道的

就是当访问{}passive 属性的时候 执行get方法

{} 不就是new Object() 的语法糖吗

console.log(options) 就是;
在这里插入图片描述
所以你明白了吧
在这里插入图片描述
当触发这个的时候 就是访问optionspassive 属性 然后passiveSupported=true

test” 你随意设置


1.滑动时候警告[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
在这里插入图片描述
2.解决方案

解决办法1

touch的事件监听方法上绑定第三个参数{ passive: false }
通过传递 passivefalse 来明确告诉浏览器:事件处理程序调用 preventDefault 来阻止默认滑动行为。

elem.addEventListener(
  'touchstart',
  fn,
  { passive: false }
);

解决办法2

* { touch-action: pan-y; }
// 使用全局样式样式去掉
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unable to preventDefault inside passive event listener due to target being treated as passive. 的相关文章

随机推荐