Unable to preventDefault inside passive event listener
瀏覽器報錯:
即
Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
根據瀏覽器報錯給的鏈接,找到這篇文章,有了詳細解釋。Making touch scrolling fast by default
簡而言之:
由於瀏覽器必須要在執行事件處理函數之後,才能知道有沒有調用過 preventDefault()
,這就導致了瀏覽器不能及時響應滾動,略有延遲。
所以爲了讓頁面滾動的效果如絲般順滑,從 chrome56 開始,在 window、document 和 body 上註冊的 touchstart
和touchmove
事件處理函數,會默認爲是 passive: true。瀏覽器忽略 preventDefault() 就可以第一時間滾動了。
舉例:
wnidow.addEventListener('touchmove', func) //效果和下面一句一樣
wnidow.addEventListener('touchmove', func, { passive: true })
這就導致了一個問題:
如果在以上這 3個元素的 touchstart
和 touchmove
事件處理函數中調用 e.preventDefault()
,會被瀏覽器忽略掉,並不會阻止默認行爲,且控制檯會報出以上錯誤信息。
那麼如何解決這個問題呢?不讓控制檯提示,而且preventDefault()
有效果呢?
兩個方案:
1、註冊處理函數時,用如下方式,明確聲明爲不是被動的
window.addEventListener('touchmove', func, { passive: false })//不推薦,因爲需要檢測USER AGENT是否支持EventListenrOption.(https://github.com/WICG/EventListenerOptions)
2、應用 CSS 屬性 touch-action
touch-action: none;// 這樣任何觸摸事件都不會產生默認行爲,但是 touch 事件照樣觸發。
使得圖片在處置方向也可以滑動和縮放。
根據谷歌的官方說法,谷歌不推薦在網頁的touchstart
和touchmove
事件處理函數中調用preventDefault()
(但在移動端 safari 中仍需要),而是通過應用CSS屬性touch-action
阻止滾動和縮放,所以這一特性將來可能會應用到所有元素中。而touchend
事件處理函數中可以調用preventDefault()
.
但實際情況可能並非如此。
關於touch-action
首先了解一下 touch-action
屬性
CSS屬性 touch-action 用於設置觸摸屏用戶如何操縱元素的區域(例如,瀏覽器內置的縮放功能)。
/* Keyword values */
touch-action: auto;//由瀏覽器處理觸控事件
touch-action: none;//不進行任何操作
//啓用單指水平平移手勢。可以與 pan-y 、pan-up、pan-down 和/或 pinch-zoom 組合使用。
touch-action: pan-x;
//啓用單指垂直方向水平平移。參考 pan-x
touch-action: pan-y;
//啓用以指定方向滾動開始的單指手勢。 一旦滾動開始,方向可能仍然相反。 請注意,滾動“向上”(pan-up)意味着用戶正在將其手指向下拖動到屏幕表面上,同樣 pan-left 表示用戶將其手指向右拖動。 多個方向可以組合,除非有更簡單的表示(例如,“pan-left pan-right”無效,因爲“pan-x”更簡單,而“pan-left pan-down”有效)。
touch-action: pan-left;
touch-action: pan-right;
touch-action: pan-up;
touch-action: pan-down;
//啓用多手指平移和縮放頁面。 這可以與任何平移值組合。
touch-action: pinch-zoom;
//瀏覽器只允許進行滾動和持續縮放操作。任何其它被auto值支持的行爲不被支持。啓用平移和縮小縮放手勢,但禁用其他非標準手勢,例如雙擊以進行縮放。 禁用雙擊可縮放功能可減少瀏覽器在用戶點擊屏幕時延遲生成點擊事件的需要。 這是“pan-x pan-y pinch-zoom”(爲了兼容性本身仍然有效)的別名。
touch-action: manipulation;
/* Global values */
touch-action: inherit;
touch-action: initial;
touch-action: unset;
更詳細的touch-action
說明請看 這裏 。
需要注意避免的坑是 touch-action
在移動端的問題:
1.iOS 移動端不支持該屬性。
2.在安卓手機中如果爲了避免chrome報錯而在全局設置touch-action:none
屬性,會禁止掉所有的觸摸操作行爲,導致頁面不能滑動。
基於以上兩個原因,即使谷歌官方推薦使用touch-action:none
來處理這個報錯,最終解決方案還是選擇用js在註冊事件處理函數時傳入{passive:false}
。
即
window.addEventListener('touchmove', func, { passive: false })
最後,由於報錯是由第三方庫引發,修改第三方庫的js源碼過於繁瑣,直接升級了第三方庫解決了這個問題(此處應有表情)。