iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 24
1
Modern Web

JS30 錄系列 第 24

Day 24 - Sticky Nav

  • 分享至 

  • xImage
  •  

任務目標

讓網頁的導覽列一碰到瀏覽器視窗的頂部時便會黏在上面,移開時便會恢復原狀。
範例連結

作法

HTML架構如下:

<!-- 導覽列上方有固定高度的大圖 -->
<header>
  <h1>A story about getting lost.</h1>
</header>
<!-- 導覽列 -->
<nav id="main">
  <ul>
    <li class="logo"><a href="#">LOST.</a></li>
    <li><a href="#">Home</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Images</a></li>
    <li><a href="#">Locations</a></li>
    <li><a href="#">Maps</a></li>
  </ul>
</nav>
<div class="site-wrap">
  <!--許多文章段落-->
</div>

想法很簡單,向下滾動頁面捲軸時,當瀏覽器觀景窗的頂端位置值大於導覽列元素頂端位置時,把導覽列從文檔流 (Normal Flow) 中抽出,讓導覽列定位在瀏覽器視窗頂端。

反之,向上滾動頁面捲軸時,當瀏覽器觀景窗頂端位置小於導覽列元素頂端位置時,恢復導覽列原來的位置。

下面為導覽列原來的 CSS。

nav {
  background:black;
  top:0;
  width: 100%;
  transition:all 0.5s;
  position: relative;
  z-index: 1;
}

position: relative 意思為讓該元素根據自己原來的位置而定位。假設在 relative 的情況下讓 left: 500px , 該元素就會距離自己原來位置的左邊 500px ,以此類推。

這裡 top:0navrelative 的情況下沒有額外的位移。當導覽列碰到觀景窗頂部時,我們讓導覽列加上 CSS 類別 fixed-nav

.fixed-nav nav {
  position: fixed;
}

position: fixed 時,該元素會從原來的文檔流中被抽出,並根據瀏覽器觀景窗來定位。

意思是原本相對於周邊的其他元素,該元素可能佔有固定的體積讓其他元素會被排開,一旦被抽離文檔流,其他元素會當作沒看到該元素一樣,自動填補該空缺。而該元素本身會以觀景窗左上角為原點判斷自己的定位,即使滾動頁面也不會影響其位置。

決定導覽列 nav 是否沾黏觀景窗的條件完全藉由比較「導覽列元素頂端位置」及「觀景窗頂部位置」判斷,所以 JS 寫法如下:

// 取得導覽列元素
const nav = document.querySelector('#main');
// 取得導覽列元素頂端位置
const topOfNav = nav.offsetTop;

function fixNav() {
  // 如果觀景窗頂部位置值大於導覽列頂部就沾黏,反之取消沾黏
  if(window.scrollY >= topOfNav) {
    document.body.style.paddingTop = nav.offsetHeight + 'px';
    document.body.classList.add('fixed-nav');
  } else {
    document.body.style.paddingTop = 0;
    document.body.classList.remove('fixed-nav');
  }
}

// 監聽滾動事件
window.addEventListener('scroll', fixNav);

值得注意的是 document.body.style.paddingTop = nav.offsetHeight + 'px' 這行。剛有提到 position:fixed 時,導覽列會被抽出文檔流而失去其體積。因此下方的元素會往上填補,視覺上會出現跳動。因此這個 paddingTop 只是為了填補該體積缺口罷了。

我們還可以讓其他 CSS 根據這個 JS 所觸發的條件變動。例如以下 CSS :

li.logo {
  max-width:0;
  overflow: hidden;
  background: white;
  transition: all .5s;
  font-weight: 600;
  font-size: 30px;
}

.fixed-nav li.logo {
  max-width: 500px;
}

logofixed-nav 被加到父元素上以前是隱藏的,加上去後就顯現了。這就是特意把 fixed-nav 加到父元素上的好處,只要添加子元素的類別就能同時控制效果。

以上就是 JS30 第二十四篇!

Reference

CSS 各種定位
完整程式碼


上一篇
Day 23 - Speech Synthesis
下一篇
Day 25 - Event Flow - Bubble and Capture
系列文
JS30 錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
JasonYang
iT邦新手 5 級 ‧ 2018-01-12 14:10:07

我喜歡這篇!
iT 鐵人邦幫忙就是這樣!

我要留言

立即登入留言