前言
JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
本日目標
今天我們要做的是置頂導覽列,當使用者拉下捲軸而視窗頂部碰到導覽列頂部時,若往後繼續下拉捲軸則將導覽列始終固定在最上方。

解析程式碼
HTML 部分
header是網頁上方的標題。
#main是網頁的導覽列,包含.logo(初始隱藏)還有其他五個項目。
.site-wrap放的是一大堆假文還有圖片。
1 | <header> |
JS 部分
宣告常數nav取得網頁導覽列。
宣告常數topOfNav取得導覽列頂部離視窗最上方的距離。
1 | const nav = document.querySelector('#main'); |
我們置頂導覽列的機制主要是透過觀測捲軸的"捲動量"來決定是否置頂捲軸。為此首先要在window註冊scroll event listener在捲軸捲動時不斷觸發,然後用fixNav()進行事件處理。
在fixedNav()裡面,我們可以判斷現在視窗的 y 方向捲動量(window.scrollY)是否超過導覽列的頂部(topOfNav),如果超過的話就在body上添加fixed-nav這個 class,反之若沒有超過則移除fixed-nav。
那為什麼我們還要特別設定body的padding-top呢? 因為在導覽列(#main)的 CSS 設定中,我們使用了position: fixed;,這樣就會造成導覽列(#main)原本佔的空間突然空出來,之後下方的.site-wrap看上面還有空間就會擠上來,但擠上來的速度過快,因此在視覺上就會出現詭異的彈跳。
為避免這種狀況發生,我們可以在置頂導覽列的同時,指定body的padding-top: nav.ofsetHeight;,讓原本導覽列佔有的空間,被padding-top補上。不置頂導覽列的同時,理所當然我們就要將body的padding-top改回 0。
1 | function fixNav(){ |
CSS 部分
下面是在body上有.fixed-nav這個 class 時的特殊設定。
設定導覽列position: fixed,在預設上是固定在left: 0;、top: 0;的位置(視窗左上角),然後加上一點陰影。
1 | .fixed-nav nav{ |
把原本在導覽列隱藏的logo顯示出來,設定max-width(最大寬度)為500px,這裡原本也可以用width: 500px;就好,但是設定width會讓transition產生的動畫效果失效。
1 | .fixed-nav li.logo{ |
在置頂導覽列的同時,把下方的文章區塊略為放大。
1 | .fixed-nav .site-wrap{ |
補充資料:
HTMLElement.offsetTop
HTMLElement.offsetHeight
Element.classList
CSS Transform
Element: scroll event
Window.scrollY