
我們知道滾動(dòng)響應(yīng)是至關(guān)重要的在用戶移動(dòng)端網(wǎng)站上觸摸的時(shí)候,然而觸摸事件監(jiān)聽器經(jīng)常會(huì)導(dǎo)致嚴(yán)重的滾動(dòng)性能問題。Chrome已經(jīng)通過允許觸摸事件監(jiān)聽器被動(dòng)地解決了這個(gè)問題(將{passive:true}選項(xiàng)傳遞給addEventListener())并傳遞指針事件API。這些特性可以驅(qū)動(dòng)新內(nèi)容引入到不阻止?jié)L動(dòng)的模型中,但開發(fā)人員有時(shí)會(huì)發(fā)現(xiàn)它們很難理解和采用。
我們認(rèn)為,如果開發(fā)人員不需要了解瀏覽器行為的晦澀細(xì)節(jié),web應(yīng)該在默認(rèn)情況下是快速的。在Chrome 56中,默認(rèn)情況下,我們將默認(rèn)的觸摸監(jiān)聽器設(shè)置為被動(dòng)的,而這通常與開發(fā)人員的意圖相匹配。我們相信這樣做,我們可以大大提高用戶的體驗(yàn),同時(shí)減少對(duì)站點(diǎn)的負(fù)面影響。
在極少數(shù)情況下,這種變化會(huì)導(dǎo)致非預(yù)期的滾動(dòng)。這通常是通過添加一個(gè) touch-action: none樣式使得元素滾動(dòng)不再出現(xiàn)。閱讀有關(guān)細(xì)節(jié),如何知道你是否受到影響,以及你能做些什么。
背景: Cancelable 事件降低你的頁面速度
如果在touchstart或第一個(gè)touchmove事件中調(diào)用preventDefault(),那么您將阻止?jié)L動(dòng)。問題是,大多數(shù)情況下,監(jiān)聽器不會(huì)調(diào)用preventDefault(),但是瀏覽器需要等待事件完成才能確定它。開發(fā)人員定義的“被動(dòng)事件監(jiān)聽器”解決了這個(gè)問題。當(dāng)您在事件處理程序中添加一個(gè)帶有{passive:true}對(duì)象作為第三個(gè)參數(shù)的觸摸事件時(shí),您將告訴瀏覽器touchstart監(jiān)聽器不會(huì)調(diào)用preventDefault(),而瀏覽器可以安全地執(zhí)行滾動(dòng),而不會(huì)阻塞監(jiān)聽器。例如:
window.addEventListener("touchstart", func, {passive: true} );
干預(yù) The Intervention
我們的主要目的是減少在用戶觸摸屏幕后更新顯示所需的時(shí)間。為了理解touchstart和touchmove的使用,我們添加了一些指標(biāo)來確定滾動(dòng)阻塞行為發(fā)生的頻率。
我們看了可取消觸摸事件,被發(fā)送到根目標(biāo)(窗口、文檔或主體)的的百分比,并確定大約80%的監(jiān)聽器在概念上是被動(dòng)的,但未被注冊(cè)為此類??紤]到這個(gè)問題的規(guī)模,我們注意到一個(gè)很大的機(jī)會(huì),可以在沒有任何開發(fā)人員操作的情況下,自動(dòng)地passive改進(jìn)滾動(dòng)。
這促使我們將我們的干預(yù)定義為:如果觸點(diǎn)或觸控式監(jiān)聽器的目標(biāo)是窗口、文檔或主體,我們默認(rèn)passive為true。代碼示例:
window.addEventListener("touchstart", func);
相當(dāng)于
window.addEventListener("touchstart", func, {passive: true} );
現(xiàn)在,在監(jiān)聽器內(nèi)部調(diào)用preventDefault()將被忽略。
下面的圖顯示用戶觸摸屏幕時(shí),從用戶觸摸屏幕到顯示更新時(shí)所花費(fèi)的時(shí)間。這一數(shù)據(jù)適用于所有的安卓網(wǎng)站。在干預(yù)之前,1%的卷軸使用了400多毫秒?,F(xiàn)在已經(jīng)減少到超過250ms的Chrome 56 Beta;大約減少了38%。在未來,我們希望使所有touchstart和touchmove偵聽器的默認(rèn)值都是被動(dòng)的,將其降低到50毫秒以下。

問題和指導(dǎo) Breakage and Guidance
在絕大多數(shù)情況下,沒有問題。但當(dāng)問題發(fā)生時(shí),最常見的癥狀是當(dāng)你不想要的時(shí)候滾動(dòng)。在極少數(shù)情況下,開發(fā)人員還可能注意到意外的單擊事件(在touchend偵聽器中丟失了preventDefault())。
在Chrome 56和稍后的版本中,當(dāng)您調(diào)用preventDefault()時(shí),DevTools將記錄一個(gè)警告,在這個(gè)事件中,干預(yù)是激活的。
touch-passive.html:19 Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080
您的應(yīng)用程序可以通過檢查調(diào)用preventDefault是否通過defaultPrevented屬性來確定它是否出現(xiàn)問題。
我們發(fā)現(xiàn),只要有可能,應(yīng)用touch-actionCSS屬性就可以相對(duì)容易地修復(fù)大部分受影響頁面。 如果你希望阻止瀏覽器的滾動(dòng)和縮放,可以再元素中應(yīng)用touch-action: none來實(shí)現(xiàn)。你過你需要水平的滾動(dòng),可以應(yīng)用touch-action: pan-y pinch-zoom,這樣用戶仍然可以正常使用垂直滾動(dòng)和縮放。在桌面邊緣等瀏覽器上,正確地應(yīng)用touch-action已經(jīng)是必要的了,它支持指針事件,而不是觸摸事件。對(duì)于不支持touch-action的移動(dòng)Safari和老式移動(dòng)瀏覽器,您的觸摸監(jiān)聽器必須繼續(xù)調(diào)用preventDefault,即使它將被Chrome忽略。
在更復(fù)雜的情況下,可能還需要依賴以下的一個(gè):
- 如果您的
touchstart監(jiān)聽器調(diào)用preventDefault(),確保preventDefault()也從相關(guān)的touchend監(jiān)聽器被調(diào)用,以繼續(xù)禁止單擊事件的生成和其他默認(rèn)的點(diǎn)擊行為。 - 最后(不推薦)通過
{passive:false}到addEventListener()來覆蓋默認(rèn)行為。請(qǐng)注意,如果用戶代理支持EventListenerOptions,則必須進(jìn)行功能檢測(cè)。
總結(jié) Conclusion
在Chrome 56中,在許多網(wǎng)站上滾動(dòng)的速度要快得多。這是大多數(shù)開發(fā)人員由于這種變化而引起的惟一影響。在某些情況下,開發(fā)人員可能會(huì)注意到無意識(shí)的滾動(dòng)。
盡管對(duì)于移動(dòng)Safari來說仍然需要這樣做,但網(wǎng)站不應(yīng)該依賴于在touchstart和touchmove監(jiān)聽器內(nèi)部調(diào)用preventDefault(),因?yàn)檫@不再保證在Chrome中能夠支持。開發(fā)人員應(yīng)該在滾動(dòng)和縮放的元素上應(yīng)用touch-actionCSS屬性,在發(fā)生任何觸摸事件之前通知瀏覽器。要禁止點(diǎn)擊的默認(rèn)行為(例如單擊事件生成),請(qǐng)?jiān)?code>touchend監(jiān)聽器內(nèi)部調(diào)用preventDefault()。