React禁止頁面滾動踩坑實踐與方案梳理

最近在使用 React 技術棧重構(gòu)一個單頁應用,其中有個頁面是實現(xiàn)城市選擇功能,主要是根據(jù)城市的首字母來快速跳轉(zhuǎn)到相應位置,比較類似原生 APP 中的電話聯(lián)系人查找功能,頁面如圖


功能界面

主要問題

在上下滑動右側(cè) fixed 定位的元素時,頁面會跟著一起滑動

滾動右側(cè)整個頁面跟著滾動

當然這個現(xiàn)象在開發(fā)過程中應該會經(jīng)常遇到,比如彈起 modal 框時,如果 modal框的內(nèi)容高度小于框高度,滑動內(nèi)容也會導致頁面跟著滑動, 那么在 React 中像往常一樣處理

<div className="nonius"
  id="nonius"
  onTouchStart={this.sidebarTouchStart.bind(this)}
  onTouchMove={this.sidebarTouchMove.bind(this)}
  onTouchEnd={this.sidebarTouchEnd.bind(this)}
>

使用 React 提供的事件綁定機制,分別綁定三個 handler ,在 onTouchMove 事件中,我希望通過 preventDefault 能夠阻止父級元素的滾動

sidebarTouchMove(e) {
  e.preventDefault();
  ...
}

但實際的反饋卻事與愿違,在調(diào)試中,我發(fā)現(xiàn) Chrome 是有警告的,并且沒有達到想要的效果


chorme 開發(fā)工具警告提示

根據(jù)警告提示,找到的原因是

AddEventListenerOptions defaults passive to false. With this change touchstart and touchmove listeners added to the document will default to passive:true (so that calls to preventDefault will be ignored)..
If the value is explicitly provided in the AddEventListenerOptions it will continue having the value specified by the page.
This is behind a flag starting in Chrome 54, and enabled by default in Chrome 56. See https://developers.google.com/web/updates/2017/01/scrolling-intervention

來源: https://www.chromestatus.com/features/5093566007214080

根據(jù) chrome 的提示得知,是因為 Chrome 現(xiàn)在默認把通過在 document 上綁定的事件監(jiān)聽器 passive 屬性(后面細說)默認置為 true,這樣就會導致我設置的 e.preventDefault() 被忽視了。當然 Chrome 的這個做法是有道理,是為了提高頁面滾動的性能,那么為了防止帶來的副作用,官方考慮的很周到,給我們提供了一個 CSS 屬性專門用來解決這個問題

#fixed-div {
  touch-action: none;
}

In rare cases this change can result in unintended scrolling. This is usually easily addressed by applying a touch-action: nonestyle to the element where scrolling shouldn't occur.

https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action

加上了這個屬性,感覺世界總算和平了,But!在 ios 系統(tǒng)上測試發(fā)現(xiàn),這個屬性 x 用沒有,查了下 Can I Use


can i use 截圖

確定無誤,就是不支持,所以這個屬性只在 Chrome 安卓等機型下是支持的,ios 這個就用不了,理想很豐滿,顯示很骨感。既然不兼容,那只能降級處理了,為了保證良好的功能體驗,感覺是還要從 passive 上做處理,說到 passive 根據(jù) MDN文檔:addEventListener 的介紹,為了提高頁面滾動性能,大多瀏覽器都默認把 touchstart 和 touchmove 在文檔元素上直接注冊的這個事件監(jiān)聽器屬性設置成 passive:true ,而通過 AddEventListener 注冊的事件依然沒有變化

既然現(xiàn)在默認將事件 passive 的屬性默認設置為 true ,那我就顯式設置為 false 好了,查遍 React 的文檔,也沒發(fā)現(xiàn)事件監(jiān)聽器可以支持配置這個屬性的,在 github 上發(fā)現(xiàn)這個帖子 Support Passive Event Listeners #6436 目前看依然是 open 狀態(tài)的,現(xiàn)在不確定有沒有支持這個屬性

解決方案

既然這樣,只能單獨對 touchmove 通過 AddEventListener 方法去注冊事件監(jiān)聽了

// 為元素添加事件監(jiān)聽   
document.getElementById('nonius').addEventListener("touchmove", (e) => {
  // 執(zhí)行滾動回調(diào)
  this.sidebarTouchMove(e)
}, {
  passive: false //  禁止 passive 效果
})

加上這個方法后,this.sidebarTouchMove(e) 方法中的 e.preventDefault() 方法就可以正常使用了,而且沒有警告提示,問題到此就算解決了

總結(jié)

總結(jié)下,這里的坑主要是 chrome 和 safari 平臺的標準不統(tǒng)一導致的,新的標準出臺,其它宿主環(huán)境不能很好的支持,當然 react 官方對這個屬性的支持也比較慢,同樣的前端 UI 框架 Vue 就處理的很棒


vue對passive屬性支持的相關語法

不小心暴露了,我是個 Vue粉,233
ok,完 ~

原文地址: http://w3cay.com/post/dc49b55.html?title=React-scroll&from=jianshu

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的。 ??事件,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,678評論 1 11
  • 我們知道滾動響應是至關重要的在用戶移動端網(wǎng)站上觸摸的時候,然而觸摸事件監(jiān)聽器經(jīng)常會導致嚴重的滾動性能問題。Chro...
    loushumei閱讀 2,268評論 0 4
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標準。 注意:講述HT...
    kismetajun閱讀 28,774評論 1 45
  • 光匆匆流逝,記憶永遠不老,每每翻開這些照片,與你們一同度過的歡樂時光仿佛還在昨天,時光無情,記憶永存,歡樂的時光永...
    A顏妍閱讀 285評論 0 0
  • 創(chuàng)業(yè)是一個苦逼的事情,不是每個人都可以創(chuàng)業(yè),不是創(chuàng)業(yè)的人都會成功,很多的細節(jié)決定了你是否真正的創(chuàng)業(yè),是否能夠成功。...
    管理老馬閱讀 229評論 1 1

友情鏈接更多精彩內(nèi)容