監聽 passive 屬性
監聽 passive 屬性
TL;DRaddEventListener('event', handler, { passive: true }) 告訴瀏覽器「這個 listener 不會 呼叫 preventDefault()」 → 瀏覽器可以 不等 JS 執行完 直接滾動 → 滾動更順、不卡頓。
為什麼需要 passive
當你監聽 wheel / touchstart / touchmove 這類滾動相關事件時,瀏覽器不知道你的 listener 會不會呼叫 preventDefault() 來攔截滾動。
所以瀏覽器只能先暫停滾動,等你的 JS 執行完才繼續 —— 這個延遲就是「滾動卡頓」的元凶。
// ❌ 沒加 passive,瀏覽器必須等這個 listener 跑完才滾動
window.addEventListener('wheel', handleWheel);
// ✅ 加 passive,瀏覽器可以平行處理滾動 + JS
window.addEventListener('wheel', handleWheel, { passive: true });
何時用 / 不用
| 情境 | passive |
|---|---|
| 純監聽(分析、追蹤滾動位置) | ✅ true |
需要 e.preventDefault() 阻止預設行為(例:自訂縮放、下拉刷新) | ❌ false |
| 不確定 | 預設不寫,行為等同 passive: false(較舊瀏覽器) |
衝突陷阱如果在 passive: true 的 listener 裡呼叫 e.preventDefault(),瀏覽器會忽略它 + 在 console 警告: Unable to preventDefault inside passive event listener invocation.
現代瀏覽器的預設行為
Chrome 56+ / Firefox 49+ 起,touchstart 和 touchmove 的 default 已經是 passive: true,除非明確寫 passive: false。wheel 事件預設仍是 non-passive。
實際範例:自訂縮放
需要 preventDefault 才能阻止頁面預設滾動,所以不能用 passive:
canvasEl.addEventListener('wheel', (e) => {
e.preventDefault(); // 阻止頁面捲動
zoom(e.deltaY);
}, { passive: false }); // 必須明確設 false