最近用react做了一個H5端的頁面,主要實現了一個彈層滑動選擇的功能,效果如圖:
遇到了一個問題,當在底部彈出層進行滾動選擇城市區劃時,蒙版後的頁面也會隨着滾動。
這種現象在開發過程中經常會遇到,常規思路就是使用event.preventDefault
阻止父級元素的滾動:
<div className="picker-column">
<div
className="picker-scroller"
style={style}
onTouchStart={this.handleTouchStart}
onTouchMove={this.handleTouchMove}
onTouchEnd={this.handleTouchEnd}
onTouchCancel={this.handleTouchCancel}
>
{this.renderItems()}
</div>
</div>
滾動事件代碼片段
handleTouchMove = (event) => {
event.preventDefault();
...
};
但這波操作過後,卻未能如願以償,在調試的時候Chrome的告警,如冷冷的冰雨打在我的臉上:
根據告警關鍵字用Google百度了一番,等到了如下結論:
由於瀏覽器必須要在執行事件處理函數之後,才能知道有沒有調用preventDefault()
,這就導致了瀏覽器不能及時響應滾動,略有延遲。所以爲了讓頁面滾動的效果如絲般順滑,從 chrome56 開始,在 window、document 和 body 上註冊的
touchstart
和touchmove
事件處理函數,會默認爲設置passive: true
。瀏覽器忽略 preventDefault() 就可以第一時間滾動了。
細細揣測一番,其實官方的考慮還是有道理的,也是周到的。在CSS中提供了一個屬性touch-action
,用於指定某個給定的區域是否允許用戶操作,以及如何響應用戶操作。
據此,我的解決方案就是設置這個CSS屬性:
touch-action: none;
感覺總算萬事大吉利了,那個手機試一把,用iPhone的Safari瀏覽器代開後,依然並沒有什麼卵用。是的,九成是瀏覽器兼容問題,查看CanIUse,果不其然。
那麼既然如此,剩下的解決方案,就只有在綁定事件的時候顯式的設置{ passive: false }
,查了一圈React文檔也沒發現,可以支持配置這個屬性的方法。此處真心感嘆一句不如Vue方便,如果是Vue就可以這麼寫:
<div v-on:touchmove.prevent="handleTouchMove"></div>
既然如此,就只能用原生的事件綁定了
document.getElementById("picker").addEventListener('touchmove', this.handleTouchMove, { passive: false });
終於,世界和平了。