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。若實際要解的是內層捲到底時牽動外層頁面,也先確認問題在捲動容器邊界,和攔截某個滾輪手勢分開處理。
參考資料: