react實現(xiàn)阻止父容器滾動
react阻止父容器滾動
最近在做代碼遷移的時候出現(xiàn)一個問題,發(fā)現(xiàn)之前自己寫好的一個自定義滾動條組件有個bug,那就滾動時父容器也會滾動。
看一下代碼,代碼做了簡化
export default ()=>{ return return ( <div className={classNames(getCls('container'), isDragRef.current ? 'active' : '', className)} ref={scrollDOMRef} onWheelCapture={(e: any) => { e.preventDefault(); if (e.deltaY < 0) { // 向上 setDragY(dragY - dragSpeed); } else { // 向下 setDragY(dragY + dragSpeed); } }}> </div> ); }
既然父容器會滾動那就阻止默認行為就好e.preventDefault();
,但是沒用。
這里我猜測應該因為react的事件是合成事件的緣故,所有事件都注冊document上,所以導致阻止的默認行為并沒有阻止到父容器上。
那就用原生的唄。
useEffect(() => { if (scrollDOMRef.current) { scrollDOMRef.current.addEventListener('wheel', (e) => { e.preventDefault(); if (e.deltaY < 0) { // 向上 setDragY(dragY - dragSpeed); } else { // 向下 setDragY(dragY + dragSpeed); } }); } }, [scrollDOMRef.current]);
原生事件和react事件一起用時要注意,阻止冒泡要考慮清楚,因為可能會導致react合成事件失效。
那這么做后就可以了嗎?
確實
父容器不滾動了
但是又掉進了react的閉包陷阱
注冊函數(shù)并沒有及時更新,dragY 和 dragSpeed的閉包導致出現(xiàn)了bug。
那么又應該怎么做呢?
useEffect(() => { if (scrollDOMRef.current) { scrollDOMRef.current.addEventListener('wheel', (e) => { e.preventDefault(); if (e.deltaY < 0) { // 向上 setDragY(dragY - dragSpeed); } else { // 向下 setDragY(dragY + dragSpeed); } }); } }, [scrollDOMRef.current, dragY, dragSpeed]);
又一個問題出現(xiàn)了
每次應該把之前的事件銷毀,然后在注冊才對。
不然多個事件同時觸發(fā)導致了bug。
useEffect(() => { const handle = (e: any) => { e.preventDefault(); if (e.deltaY < 0) { // 向上 setDragY(dragY - dragSpeed); } else { // 向下 setDragY(dragY + dragSpeed); } }; if (scrollDOMRef.current) { scrollDOMRef.current.addEventListener('wheel', handle, { passive: false, }); } return () => { if (scrollDOMRef.current) { scrollDOMRef.current.removeEventListener('wheel', handle); } }; }, [scrollDOMRef.current, dragY, dragSpeed]);
passive
passive為false時,瀏覽器執(zhí)行完回調(diào)函數(shù)才知道有沒有調(diào)用preventDefault,如果沒有調(diào)用preventDefault,再去執(zhí)行默認行為,就是滾動。這樣就回造成滾動不流暢。
passive為true,就是告訴瀏覽器不會調(diào)用preventDefault,瀏覽器直接執(zhí)行滾動就行,不用考慮回調(diào)函數(shù)了。
這時,即使你在回調(diào)函數(shù)里調(diào)用preventDefault也不會生效。
mdn中說,在有些瀏覽器(特別是Chrome和Firefox)中,你監(jiān)聽window、document或者document.body上的touchstart和touchmove,會將passive默認設置為true。
還是要提醒大家,在你不需要調(diào)用preventDefault的時候,監(jiān)聽scroll或者touchmove,將passive設置為true
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
React中使用Workbox進行預緩存的實現(xiàn)代碼
Workbox是Google Chrome團隊推出的一套 PWA 的解決方案,這套解決方案當中包含了核心庫和構(gòu)建工具,因此我們可以利用Workbox實現(xiàn)Service Worker的快速開發(fā),本文小編給大家介紹了React中使用Workbox進行預緩存的實現(xiàn),需要的朋友可以參考下2023-11-11React 中常用的幾種路由跳轉(zhuǎn)方式小結(jié)
基本路由跳轉(zhuǎn)是最常見的一種方式,下面介紹React 中常用的幾種路由跳轉(zhuǎn)方式,感興趣的朋友一起看看吧2023-12-12React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來
這篇文章主要為大家介紹了React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10