问题
移动端 web 开发中,使用 addEventListener 阻止了 touchstart 事件的默认行为却发现没有生效
描述
再移动端 web 开发中,我们一般会用 addEventListener
给 document 节点的 touchstart
事件设置 preventDefault()
方法以达到禁用事件默认行为的目的,但有时候会发现在 chrome 移动端模拟器或者手机浏览器上事件的默认行为并没有成功禁用。
document.addEventListener("touchstart", function (ev) {
ev = ev || event;
ev.preventDefault();
});
原因
首先来看下 MDN 中 addEventListener()
方法的描述:
EventTarget.addEventListener()
EventTarget.addEventListener() 方法将指定的监听器注册到
EventTarget
上,当该对象触发指定的事件时,指定的回调函数就会被执行。 事件目标可以是一个文档上的元素Element
,Document
和Window
或者任何其他支持事件的对象 (比如XMLHttpRequest
)。
addEventListener()
的工作原理是将实现EventListener
的函数或对象添加到调用它的EventTarget
上的指定事件类型的事件侦听器列表中。语法
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
target.addEventListener(type, listener, useCapture, wantsUntrusted ); // Gecko/Mozilla only
addEventListener()
除了事件类型 type 和 回调函数 listenner 外,还有一个可选参数 options
,options
传入一个可选参数对象,主要包括四个参数,其中preventDefault()
不生效问题就是有 passive
这个参数引起的。
tartget.addEventListenner(type, listener, {
capture: Booolean,
once: Boolean,
passive: Boolean,
signal: AbortSignal
})
passive
用于控制是否调用 preventDefault()
,在以前,passive
默认是为 true,即 addEventListener()
中写了 event.preventDefault()
会被正常调用。后来,人们发现 passive
设置为 true 会降低滚屏性能。为什么呢?事件监听器在监听事件时,并不能提前知道回调函数中是否会阻止默认行为,因此若想知道是否会阻止就需要等待函数执行完,这段时间虽然很短,但等待仍会让人感到卡顿。卡顿对比请看下图。而大部分事件监听器是不会阻止默认行为的,所以大部分情况下页面因为等待是否会有 preventDefault()
是完全没必要的,会影响大部分情况下的体验。为解决卡顿问题,某些浏览器就将一些节点事件的 passive
默认为 true,即使函数中使用了 preventDefault()
也会被无视,因为看到 preventDefault()
时浏览器可能已经执行了默认行为,总不能撤回吧(doge) 。
解决方案
前面扯了这么多,但解决很简单,把 passive
设置为 false 传进监听器就行了
document.addEventListener("touchstart", function (ev) {
ev = ev || event;
ev.preventDefault();
}, {passive: false});