前言:
Hi 大家好,我是Steven,今天想用react實作一個特別的nav實現方式(因為第一次做,所以說特別XD),我記得Youtuber好像有用這種nav呈現方式,那這種特別的nav就是當我滾輪往下移動,會隱藏nav,但是往上移動時會顯示nav,那話不多說,我們就開始吧!
實現:
我們先把nav切出來,這邊我不會切一個漂亮的nav bar,重點是了解nav bar做動的理由。
初始狀態:記得把nav的css部分設定position:fixed,這樣才能跟著網頁捲動來移動nav
目標:
當我滾輪往下移:(nav bar消失)
當我稍微往上移一點:(nav bar又浮現出來)
在react控制dom的話,最好使用 Imperative Programming的方式來操作(跟js去抓dom元素的方法不一樣),所以我們先來設定控制navbar style的內容,在react中用state來控制dom元素的style呈現方式是最方便的。
設定nav的移動程度預設為0const [moveNav, setMoveNav] = useState(0);
接下來把它插入dom元素的style<nav style={{top:
${moveNav}px}} className='move-nav'>Test Nav</nav>
這樣一來,這個nav的top就會根據moveNav這個state的數值為多少來變更及渲染出正確的位置
設定好state,那我們來看看要如何控制moveNav 的數值比較恰當,因為要根據滾動頁面來呈現是否顯示nav,那勢必跟監聽scroll有很大的關西,所以我們先來設定監聽函式吧!
我們把監聽函式放在useEffect中(有關useEffect的使用方式這邊不會詳細解說,也許你可以看看其他文章,網路上很多或之後我在寫篇文章分享!)。
這邊設定好scroll監聽函式要記得在執行下一個生命周期時移除這個scroll 監聽函式(useEffect裡的return部分),為了不佔用記憶體空間。這邊scroll是監聽handleScroll這個函式,所以接下來看看怎麼編寫handleScroll內容。
useEffect(()=>{
window.addEventListener('scroll', handleScroll);
return ()=>
window.removeEventListener('scroll', handleScroll)
},[])
這邊我把useEffect內容全部擺上來再一一解說
useEffect(()=>{
*在函式外先定義一個變數用來存放最後一次滾動的scrollTop為多少,這是用來跟新舊scrollTop做比較用的。*
var lastScrollTop = 0;
const handleScroll = () => {
*這邊的每次滾動,scrollTop是跟上一次滾動的lastScrollTop作比較*
*scrollTop這麼設定是為了確保有值,你會發現window.pageYOffset和*
*document.documentElement.scrollTop 值是一樣的*
*window.pageYOffset是離視窗頂部距離,
*document.documentElement.scrollTop是根據html標籤滾輪 目前所在位置*
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
*程式由上往下讀,這邊是當新的scrollTop 大於 舊的scrollTop時,表示網頁往下滾動,對吧?*
*因此應該是要收起nav bar才對,所以給賦值moveNav -80這個值。*
if(scrollTop > lastScrollTop){
setMoveNav(-80)
}
*這邊是相反情況,如果新的scrollTop 小於 舊的scrollTop時,表示網頁網上滾動,便會顯示nav bar*
else{
setMoveNav(0)
}
最後這邊是設定每次滾動後的scrollTop,也就是所謂的舊的scrollTop,為了跟下次滾動時的新的scrollTop
做比較而定義
lastScrollTop = scrollTop;
}
window.addEventListener('scroll', handleScroll, {passive:true});
return ()=>
window.removeEventListener('scroll', handleScroll)
},[])
這樣以來,便能達成我要的目標,有興趣的朋友可以複製程式碼觀察看看變化。
結尾:
這是我第一次寫正式的技術文章,文筆和排版還不太行請大家多多包涵,也希望大家可以跟我多多交流,有錯誤的部分也盡請指出,那我們下一篇技術文章見啦!
一般 scroll 會建議用 passive listener ,主要是因為效能問題,如果不是 passive 的,瀏覽器就要考慮到呼叫 event.preventDefault()
的可能性,於是實際繪圖的動作就要等到 event listener 跑完才能做,而 scroll 又是個很常觸發的 event
D大您好,感謝指教菜鳥新手!
剛剛爬了一下文,想跟您確認一下我的想法是不是正確的。
只要有設定事件監聽的話,瀏覽器都會去檢查監聽函式裡是否有阻止預設行為(e.preventDefault()),但執行這段監聽函式本身就會花費一點時間,這裡假設花200ms,假設沒有設定e.preventDefault()
,瀏覽器才會了解,哦,你可以開始滾動了
,但問題在於不要讓瀏覽器花費這200ms去確認是否有沒有阻止預設行為對吧,因此需要用到passive的特性。
第二個疑問是,我的程式碼應該這樣改嗎?window.addEventListener('scroll', handleScroll, {passive:true})
第三個參數代入passive:true,直接告訴瀏覽器,我不調用e.preventDefault()來阻止預設行為。
再麻煩有錯誤的觀念的話,幫我指證一下,感恩!
對的