問題
移動端 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});