changed 4 years ago
Linked with GitHub

JS30 讀書會 Day 24 - Sticky Nav

摘要

固定導覽列

當 scroll 超過 Nav 時,把 Nav fix 在最上方的效果。

流程

主要流程:

  1. 確定要比對的數值:滑動距離(scrollY) vs Nav 與頂端距離(topOfNav)
  2. 比對數值,並增加 fix 效果
  3. 處理 jerky jump 效果
  4. 優化,加入動畫效果

確定要比對的數值

  • window.scrollY:已經 scroll 的距離
  • element.offsetTop:元素到頂端的距離

加上事件監聽,監聽對象為 scroll

const nav = document.querySelector('#main') // 選擇 nav 的 id const topOfNav = nav.offsetTop // 選擇 nav 到畫面頂端的距離 function fixNav() { if(window.scrollY >= topOfNav) { console.log(topOfNav, window.scrollY) } } window.addEventListener('scroll', fixNav)

可以觀察 window.scrollY 以及 topOfNav 的變化

比對的數值,增加 fix 效果

window.scrollY 超過 topOfNav ,加上 fix 的 CSS,反之則刪除

function fixNav() { if(window.scrollY >= topOfNav) { document.body.classList.add('fixed-nav') } else { document.body.classList.remove('fixed-nav') } }
.fixed-nav nav { position: fixed; box-shadow: 0 10px 10px 0px rgba(0, 0, 0, 0.5); }

處理 jerky jump 效果

因為這邊 fix 的效果是先把 nav 從原本的畫面拿出,再固定到最上面,所以原本放置 nav 的位置會消失而由下面的元素遞補上去,造成畫面突然往上跳 (jerky jump) 的效果。

所以在跳動之前,我們可以同樣的加上一塊空白,讓原本 nav 的高度被填滿,使畫面不要往上跳躍。

這邊的做法是在 body 最上方加上 padding-top 填滿。

  • element.offsetHeight:可以知道元素的高度(不包含 margin 的總高度,回傳為一整數)
function fixNav() { if(window.scrollY >= topOfNav) { document.body.classList.add('fixed-nav') document.body.style['padding-top'] = `${nav.offsetHeight}px` } else { document.body.classList.remove('fixed-nav') document.body.style['padding-top'] = 0 } }

補充

使用 position: sticky 的話就不會有 jerky jump 的問題,感謝 Allen 同學的補充~

不同於 position: fixedposition: sticky 會「黏」在滾動過來的條列上並且保留原本的空間,並不是像 position: fixed 整個抽離離開畫面。


優化,加入動畫效果

  • 在 fix nav 時,使 nav 的 logo 也一起跳出
  • 在 fix nav 時,放大文本的內容,從 0.98 跳到 1

因為我們的 fixed-nav 是放在 body 上面,所以可以藉由是否有 fixed-nav 來實現以上這兩個效果

li.logo { max-width: 0; overflow: hidden; background: white; transition: all .5s; font-weight: 600; font-size: 30px; } .fixed-nav li.logo { // 如果有 fixed-nav 那 li.logo 便會出現 max-width: 200px; }
.site-wrap { max-width: 700px; margin: 70px auto; background: white; padding: 40px; text-align: justify; box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.05); transform: scale(0.98); transition: transform 0.5s; } .fixed-nav .site-wrap{ // 如果有 fixed-nav 那 scale 就變大為 1 transform: scale(1); }

謝謝大家~

參考資料

offsetTop MDN

offsetHeight MDN
返回元素的高度(不包含 margin 的總高度,回傳為一整數)

@dustinhsiao21 dustinhsiao21 筆記

@dashnowords dashnowords 筆記

Select a repo