iT邦幫忙

0

How to hide nav when scroll down and show nav when scroll up? (要怎麼實現當滾輪往下隱藏nav,往上時顯示nav?)

前言:
Hi 大家好,我是Steven,今天想用react實作一個特別的nav實現方式(因為第一次做,所以說特別XD),我記得Youtuber好像有用這種nav呈現方式,那這種特別的nav就是當我滾輪往下移動,會隱藏nav,但是往上移動時會顯示nav,那話不多說,我們就開始吧!

實現:

我們先把nav切出來,這邊我不會切一個漂亮的nav bar,重點是了解nav bar做動的理由。

初始狀態:記得把nav的css部分設定position:fixed,這樣才能跟著網頁捲動來移動nav
目標:
https://ithelp.ithome.com.tw/upload/images/20210503/20126057TAlloEbjHV.jpg

當我滾輪往下移:(nav bar消失)
https://ithelp.ithome.com.tw/upload/images/20210503/20126057vlsZkOAeNa.jpg

當我稍微往上移一點:(nav bar又浮現出來)
https://ithelp.ithome.com.tw/upload/images/20210503/20126057wZ9y3QD3LQ.jpg

在react控制dom的話,最好使用 Imperative Programming的方式來操作(跟js去抓dom元素的方法不一樣),所以我們先來設定控制navbar style的內容,在react中用state來控制dom元素的style呈現方式是最方便的。

設定nav的移動程度預設為0
const [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)
  },[])

這樣以來,便能達成我要的目標,有興趣的朋友可以複製程式碼觀察看看變化。

結尾:
這是我第一次寫正式的技術文章,文筆和排版還不太行請大家多多包涵,也希望大家可以跟我多多交流,有錯誤的部分也盡請指出,那我們下一篇技術文章見啦!


1 則留言

3
DanSnow
iT邦新手 1 級 ‧ 2021-05-03 14:59:01

一般 scroll 會建議用 passive listener ,主要是因為效能問題,如果不是 passive 的,瀏覽器就要考慮到呼叫 event.preventDefault() 的可能性,於是實際繪圖的動作就要等到 event listener 跑完才能做,而 scroll 又是個很常觸發的 event

D大您好,感謝指教菜鳥新手!
剛剛爬了一下文,想跟您確認一下我的想法是不是正確的。

  1. 只要有設定事件監聽的話,瀏覽器都會去檢查監聽函式裡是否有阻止預設行為(e.preventDefault()),但執行這段監聽函式本身就會花費一點時間,這裡假設花200ms,假設沒有設定e.preventDefault(),瀏覽器才會了解,哦,你可以開始滾動了,但問題在於不要讓瀏覽器花費這200ms去確認是否有沒有阻止預設行為對吧,因此需要用到passive的特性。

  2. 第二個疑問是,我的程式碼應該這樣改嗎?
    window.addEventListener('scroll', handleScroll, {passive:true})

第三個參數代入passive:true,直接告訴瀏覽器,我不調用e.preventDefault()來阻止預設行為。

再麻煩有錯誤的觀念的話,幫我指證一下,感恩!

DanSnow iT邦新手 1 級 ‧ 2021-05-03 20:28:41 檢舉

對的

我要留言

立即登入留言