前言
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