iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0

成品連結:Sticky Nav操作前程式碼 - HTML操作前程式碼 - CSS操作後程式碼 - HTML操作後程式碼 - CSS

今天要做的是類似 CSS 中的 position: fixed 的功能,只不過需滾動至特定的位置才會開始生效。看到這裡或許你會說那就用 position: sticky 就好啦!沒錯,的確可行,但我們還是嘗試使用 JS 實作吧!

監聽滾動(scroll)事件

首先設定滾動監聽事件

function stickyNav() {
    // code here
}
window.addEventListener('scroll', stickyNav);

設定效果生效位置

接著要來設定效果生效的位置。我們要先知道當滾動至哪個距離時會開始生效,這裡有兩個辦法,

  1. header 的高度(offsetHeight
  2. nav 與網頁頂部的距離(offsetTop

今天我們會使用的是第二種方法(第一種方法請自行嘗試)。

先取得 nav 與網頁頂部的距離

const nav = document.querySelector('#main');
const topOfNav = nav.offsetTop;

function stickyNav() {
    // code here
}

接著使用 if...else... 來判斷當滾動距離大於或等於 nav.topOfNav 時生效

const nav = document.querySelector('#main');
const topOfNav = nav.offsetTop;

function stickyNav() {
    if (window.scrollY >= topOfNav) {
        // code here
    } else {
        // code here
    }
}

這裡要注意必須在 function 外面宣告及賦值給 topOfNav 而不能直接在 if...else... 中比較(if (window.scrollY >= nav.offsetTop){..});因為如果直接在 if... 中比較,nav.offsetTop 會隨著滾動越來越小,即便你向上滾動也不會變大(根本原因是設定在監聽事件內,nav.offsetTop 會持續變動,終至 0),所以要將該變數宣告在 function 外面

設定 CSS

我們要讓 Navbar 固定的方法就是使用 position: fixed(請暫時忘記 position: sticky 的存在)

/* CSS */
.fixed-nav nav {
    position: fixed;
    box-shadow: 0 5px rgba(0,0,0,0.1);
}

並加到 JS 中

這裡會把 .fixed-nav 這個 class 加到 body 而不是 nav 是因為如果是加到 nav,那晚點要操作其他元素時會很不方便(需要向上一層 -> 接著再找要設定的元素),綁在 body 並透果選擇器選到要設定樣式的元素會較恰當

function stickyNav() {
    if (window.scrollY >= topOfNav) {
        document.body.classList.add('fixed-nav');
    } else {
        document.body.classList.remove('fixed-nav');
    }
}

有效果出來了,但怎麼感覺當滾動到該位置時下方的文字區塊會向上跑動?你沒看錯,這是因為當 nav 變成 position: fixed 時它浮起來了,換另一種方式講就是 nav 變得不佔據 HTML 的空間,也因此下方的元素就跑上去了。

所以我們要把 nav 的高度加回去在 bodypadding-top,這裡提醒一下千萬不要在 CSS 寫死值,要是之後 nav 高度改變會變得很難修改。應該說當一個元素的值可能變動時都不要寫死值,而是要用變數的方式使之更有彈性。

function stickyNav() {
    if (window.scrollY >= topOfNav) {
        document.body.classList.add('fixed-nav');
        document.body.style.paddingTop = `${nav.offsetHeight}px`;
    } else {
        document.body.classList.remove('fixed-nav');
        document.body.style.paddingTop = `0px`;
    }
}

加入 logo

其實到這裡功能差不多做完了,但如果 HTML 看得仔細的話會看到 nav ul 當中第一項的 li 是沒有在畫面上的,這是因為 CSS 設定 max-width: 0 所以看不到,而我要把它加回去。

/* CSS */
.fixed-nav .logo {
    max-width: 500px;
}

透過更改 max-width 屬性,現在當滾動至一定位置,logo 出現了!

下方文字放大

最後當滾動至一定位置時,啟用效果使下方文字有放大效果。

從 CSS 可以看到 .site-wrap 有一個屬性是 transform: scale(0.98) ,而我們要將它稍微放大

/* CSS */
.fixed-nav .site-wrap {
    transform: scale(1);
}

這樣就完成啦!

這裡順便留個小任務給各位,如果使用 position: sticky 要如何做出相同效果呢?請練習看看~

如果有不清楚的地方歡迎留言詢問喔!

Reference


上一篇
JS30 Day 23 - Speech Synthesis
下一篇
JS30 Day 25 - Event Capture, Propagation, Bubbling and Once
系列文
一起挑戰 JavaScript 30 吧!30

尚未有邦友留言

立即登入留言