react實(shí)現(xiàn)阻止父容器滾動(dòng)
react阻止父容器滾動(dòng)
最近在做代碼遷移的時(shí)候出現(xiàn)一個(gè)問(wèn)題,發(fā)現(xiàn)之前自己寫(xiě)好的一個(gè)自定義滾動(dòng)條組件有個(gè)bug,那就滾動(dòng)時(shí)父容器也會(huì)滾動(dòng)。
看一下代碼,代碼做了簡(jiǎn)化
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> ); }
既然父容器會(huì)滾動(dòng)那就阻止默認(rèn)行為就好e.preventDefault();
,但是沒(méi)用。
這里我猜測(cè)應(yīng)該因?yàn)閞eact的事件是合成事件的緣故,所有事件都注冊(cè)document上,所以導(dǎo)致阻止的默認(rèn)行為并沒(méi)有阻止到父容器上。
那就用原生的唄。
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事件一起用時(shí)要注意,阻止冒泡要考慮清楚,因?yàn)榭赡軙?huì)導(dǎo)致react合成事件失效。
那這么做后就可以了嗎?
確實(shí)
父容器不滾動(dòng)了
但是又掉進(jìn)了react的閉包陷阱
注冊(cè)函數(shù)并沒(méi)有及時(shí)更新,dragY 和 dragSpeed的閉包導(dǎo)致出現(xiàn)了bug。
那么又應(yīng)該怎么做呢?
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]);
又一個(gè)問(wèn)題出現(xiàn)了
每次應(yīng)該把之前的事件銷(xiāo)毀,然后在注冊(cè)才對(duì)。
不然多個(gè)事件同時(shí)觸發(fā)導(dǎo)致了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時(shí),瀏覽器執(zhí)行完回調(diào)函數(shù)才知道有沒(méi)有調(diào)用preventDefault,如果沒(méi)有調(diào)用preventDefault,再去執(zhí)行默認(rèn)行為,就是滾動(dòng)。這樣就回造成滾動(dòng)不流暢。
passive為true,就是告訴瀏覽器不會(huì)調(diào)用preventDefault,瀏覽器直接執(zhí)行滾動(dòng)就行,不用考慮回調(diào)函數(shù)了。
這時(shí),即使你在回調(diào)函數(shù)里調(diào)用preventDefault也不會(huì)生效。
mdn中說(shuō),在有些瀏覽器(特別是Chrome和Firefox)中,你監(jiān)聽(tīng)window、document或者document.body上的touchstart和touchmove,會(huì)將passive默認(rèn)設(shè)置為true。
還是要提醒大家,在你不需要調(diào)用preventDefault的時(shí)候,監(jiān)聽(tīng)scroll或者touchmove,將passive設(shè)置為true
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
react中實(shí)現(xiàn)拖拽排序react-dnd功能
這篇文章主要介紹了react中實(shí)現(xiàn)拖拽排序react-dnd功能,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02React中使用Workbox進(jìn)行預(yù)緩存的實(shí)現(xiàn)代碼
Workbox是Google Chrome團(tuán)隊(duì)推出的一套 PWA 的解決方案,這套解決方案當(dāng)中包含了核心庫(kù)和構(gòu)建工具,因此我們可以利用Workbox實(shí)現(xiàn)Service Worker的快速開(kāi)發(fā),本文小編給大家介紹了React中使用Workbox進(jìn)行預(yù)緩存的實(shí)現(xiàn),需要的朋友可以參考下2023-11-11從頭寫(xiě)React-like框架的工程搭建實(shí)現(xiàn)
這篇文章主要介紹了從頭寫(xiě)React-like框架的工程搭建實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04React 中常用的幾種路由跳轉(zhuǎn)方式小結(jié)
基本路由跳轉(zhuǎn)是最常見(jiàn)的一種方式,下面介紹React 中常用的幾種路由跳轉(zhuǎn)方式,感興趣的朋友一起看看吧2023-12-12React動(dòng)畫(huà)實(shí)現(xiàn)方案Framer Motion讓頁(yè)面自己動(dòng)起來(lái)
這篇文章主要為大家介紹了React動(dòng)畫(huà)實(shí)現(xiàn)方案Framer Motion讓頁(yè)面自己動(dòng)起來(lái),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10