550 字
3 分鐘

wheel 的 preventDefault 無效?檢查 passive 與 cancelable

驗證於

本文目錄

Chrome 出現 Unable to preventDefault inside passive event listener 時,先找到註冊 wheel 的程式。Chrome 對掛在 window、document 或 body 的 wheel 監聽器,省略選項時會按 passive 處理。如果元件確實要接管滾輪,將監聽器掛在該元素並明確設定 { passive: false },再檢查當次事件是否可取消。Chrome 滾輪事件說明

三組對照找出取消卡在哪裡#

把這段存成 HTML 並以 Chrome 開啟。範例以合成 WheelEvent 隔離兩個條件,直接讀出事件旗標,方便重複驗證。

<!doctype html>
<meta charset="utf-8">
<title>wheel 取消測試</title>
<div id="surface"></div>
<pre id="results"></pre>
<script>
const surface = document.querySelector("#surface");
const results = document.querySelector("#results");
function measure(target, options, cancelable) {
const cancel = (event) => event.preventDefault();
target.addEventListener("wheel", cancel, options);
const event = new WheelEvent("wheel", { bubbles: true, cancelable });
surface.dispatchEvent(event);
target.removeEventListener("wheel", cancel);
return { cancelable: event.cancelable, prevented: event.defaultPrevented };
}
results.textContent = JSON.stringify({
rootDefault: measure(document, undefined, true),
elementOptOut: measure(surface, { passive: false }, true),
nonCancelable: measure(surface, { passive: false }, false),
}, null, 2);
</script>

rootDefault 可取消但沒有成功取消,對應 Chrome 的根節點 passive 預設。elementOptOut 兩個值都應為 true。nonCancelable 則兩個值都為 false,即使已明確註冊為非 passive,這一筆事件也無法取消。事件規格要同時滿足「可取消」及「不在 passive 監聽器內」才會設置取消旗標。DOM 事件規格

只讓需要接管滾輪的元件取消事件#

若元件只是追蹤捲動位置,被動監聽即可。只在元件有自己的滾輪動作,且目前輸入確實要執行該動作時,才呼叫 preventDefault()。例如時間軸可以只接管特定手勢,滑鼠移出時間軸仍可正常捲動頁面。元件移除時可沿用 AbortController 清理監聽器。

以上三組結果已在 macOS 的 Chrome 157.0.8103.78 headless 用原範例驗證。合成事件只驗證取消旗標。整合回元件後,再用實際滑鼠或觸控板檢查元件內外的捲動和縮放,並觀察連續事件的 cancelable。若實際要解的是內層捲到底時牽動外層頁面,也先確認問題在捲動容器邊界,和攔截某個滾輪手勢分開處理。

參考資料:

Chrome 滾輪監聽器預設行為

WHATWG DOM:事件取消旗標

MDN:wheel event

wheel 的 preventDefault 無效?檢查 passive 與 cancelable
https://laplusda.com/posts/javascript-wheel-passive-preventdefault/
作者
Zero
發佈於
2026-11-15
許可協議
CC BY-NC-SA 4.0