解決滾動警告:Unable to preventDefault inside passive event listener

魚丸2312發表於2019-05-08

起因

近期更新專案,使用fullpage.js實現單頁滾動,頁面報錯:

解決滾動警告:Unable to preventDefault inside passive event listener

passive event listener

關於 passive event listener 的問題已遇到過多次。

當我們滾動頁面的時候或者需要監聽 touch 事件的時候,瀏覽器無法預先知道一個事件處理函式中會不會呼叫 preventDefault(),故而通過一個延遲,大約300ms的停頓來檢測,整個頁面就顯得卡頓。

為了優化這一點,自chrome51開始,passive event listener被引入。

通過對 addEventListener第三個引數設定 { passive : true } 來避免瀏覽器檢查是否在touch事件handler中呼叫了preventDefault

這種情形下,如果我們依舊呼叫preventDefault,就會在控制檯列印一個警告,告訴我們passive event listener 中的preventDefault會被忽略。

通常解決的方法有兩個:

  • addEventlistener第三個引數傳入{ passive : false }

    document.addEventListener(
      'touchstart',
      function(event){
        event.preventDafault();
      },
      { passive: false }
    );
    複製程式碼
  • 觸發全域性的css樣式touch-action,例如

    * { touch-action: pan-y; } 
    //表示手指頭可以垂直移動
    複製程式碼

touch-action

由於是第三方的外掛,選擇了全域性css樣式的覆蓋。

在 Chrome(版本 55 及更高版本)、Internet Explorer 和 Edge 中,PointerEvents 是建議的自定義手勢實現方法。

PointerEvents 的一大特色是,它將包括滑鼠、觸控和觸控筆事件在內的多種輸入型別合併成一個回撥集。需要偵聽的事件是 pointerdownpointermovepointeruppointercancel

其他瀏覽器中的對應項是 touchstarttouchmovetouchendtouchcancel 觸控事件,如果想為滑鼠輸入實現相同的手勢,則需實現 mousedownmousemovemouseup

使用這些事件需要對 DOM 元素呼叫 addEventListener() 方法,使用的引數為事件名稱、回撥函式和一個布林值。布林值決定是否應在其他元素有機會捕獲並解釋事件之前或之後捕獲事件。(true 表示想要先於其他元素捕獲事件)

// Check if pointer events are supported.
if (window.PointerEvent) {
  // Add Pointer Event Listener
  swipeFrontElement.addEventListener('pointerdown', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('pointermove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('pointerup', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('pointercancel', this.handleGestureEnd, true);
} else {
  // Add Touch Listener
  swipeFrontElement.addEventListener('touchstart', this.handleGestureStart, true);
  swipeFrontElement.addEventListener('touchmove', this.handleGestureMove, true);
  swipeFrontElement.addEventListener('touchend', this.handleGestureEnd, true);
  swipeFrontElement.addEventListener('touchcancel', this.handleGestureEnd, true);

  // Add Mouse Listener
  swipeFrontElement.addEventListener('mousedown', this.handleGestureStart, true);
}
複製程式碼

touch-action 支援的關鍵字有:

touch-action: auto;
touch-action: none;
touch-action: pan-x;
touch-action: pan-left;
touch-action: pan-right;
touch-action: pan-y;
touch-action: pan-up;
touch-action: pan-down;
touch-action: pinch-zoom;
touch-action: manipulation;
複製程式碼

結束語

Unable to preventDefault inside passive event listener這個問題出現有一段時間,沒想到可以通過touch-action相關css屬性去解決,邂逅touch-action相關小知識,撒一波花~

相關文章