讓網頁的導覽列一碰到瀏覽器視窗的頂部時便會黏在上面,移開時便會恢復原狀。
範例連結
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:0
, nav
在 relative
的情況下沒有額外的位移。當導覽列碰到觀景窗頂部時,我們讓導覽列加上 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;
}
logo
在 fixed-nav
被加到父元素上以前是隱藏的,加上去後就顯現了。這就是特意把 fixed-nav
加到父元素上的好處,只要添加子元素的類別就能同時控制效果。
以上就是 JS30 第二十四篇!