進入JS30的倒數第七天,今天將透過捲動事件加上 CSS屬性的更動,讓我們的導覽列在即將滾出畫面的時候,可以固定在畫面最上方,而不會消失在頁面當中。
實作範例
在第十三天的課題Slide in on scroll中,我們有介紹到 window.scrollX
與 window.scrollY
這兩個代表目前頁面 X軸與 Y軸捲動量的屬性,element.offsetWidth
及 element.offsetHeight
代表該元素的寬與高的屬性。而一開始可以看到目前的頁面配置與 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">
<p>Lorem ipsum dol....</p>
<p>Lorem ipsum dol....</p>
<p>Lorem ipsum dol....</p>
.....
</div>
我們的導覽列上方有一塊含有背景圖示的 <header>
元素,再來是我們要製作特效的 <nav>
元素,最後則是 <div>
元素容器裏頭放了很多 <p>
元素字串。
要讓頁面在滾動的時候產生特效,首先我們要先加上監聽事件 onscroll
及觸發的函示,這邊也先將我們會用到的元素選取起來:
window.addEventListener('scroll', stick);
const navbar = document.querySelector('#main');
const header = document.querySelector('header');
const wrap = document.querySelector('.site-wrap');
const logo = document.querySelector('.logo');
在執行的函式當中,我們要讓導覽列碰到頁面上方邊界時,就讓他一直維持在該位置,也就是下圖的情形:
當下的狀態為上方 header
元素已經都被往上捲動到看不見了。換成判斷條件的話,也就是為 window.scrollY
等於 header
元素的高度,因此在我們函式中的判斷條件可以寫成如下,當Y軸方向捲動的量大於該高度,就執行讓 nav
元素黏在固定位置的程式碼:
function stick(event) {
const condition = header.offsetHeight;
if (window.scrollY >= condition) {
...
} else {
...
}
};
要讓畫面中的 DOM 元素,固定在目前頁面中可視區域的位置,我們就要使用到 position:fixed
這個 CSS 屬性。[2]
含有固定定位(position: fixed)CSS 屬性的元素,會相對於瀏覽器視窗來定位,這代表就頁面捲動時,它還是會固定在頁面上相同的位置。而固定定位的元素不會保留它原本在頁面應有的空間,也不會跟其他元素的配置互相干擾。
而當我們把 nav
元素由原本的 position:relative
改成 position:fixed
之後,他在原本的空間大小也會消失,下方的 <div>
元素位置也因此被往上移動。所以在這同時我們需要將 <div>
元素位置往下移動 nav
元素的高度,才不會出現瞬間移動的神奇事件。而這邊我們選用的方法是修改 body 的 padding top
屬性,讓所有在 body 中的元素被往下移動一定距離:
function stick (event) {
const condition = header.offsetHeight;
if (window.scrollY >= condition) {
navbar.style.position = 'fixed';
document.body.style.paddingTop = navbar.offsetHeight+'px';
} else {
navbar.style.position = 'relative';
document.body.style.paddingTop = 0;
}
};
最後還要加上一些特效。在作者的範例當中,當導覽列移動到頁面上方的時候,原本藏起來的 logo 會跑出來,但是移開的時候他又會消失不見,這邊作者是利用 max-width
這個 CSS 屬性來操作這個特效。透過 max-width
屬性,我們可以指定元素的最大寬度,可以做出更加完美的響應式網頁。
而作者在一開始的 CSS 屬性中,就將該元素的 max-width
這個 CSS 屬性設為0,並且加上 overflow:hidden
,讓裡頭的內容無法顯示出來,如此一來就成功做出他消失不見的效果,而當導覽列移動到我們指定的位置之後,再將 max-width
加上一寬度值,就能讓裡頭的內容顯示出來。
作者在 CSS 屬性中將整個 <nav>
容器指定為 display:flex
,並讓裡頭每個 li
都使用了 flex:1
屬性,所以當 logo 出來的時候,我們只要設定一個上限數值,讓他的寬度可以隨著 flex-grow
延伸即可,另外作者也在裡頭加了一些其他特效,大家可以都自己嘗試看看。完整的程式碼如下:
function stick (event) {
//取得 header 元素的高度
const condition = header.offsetHeight;
//當捲動高度大於header 元素的高度 執行下列程式碼
if (window.scrollY >= condition) {
//將 nav 元素的 position 屬性修改為 fixed
navbar.style.position = 'fixed';
//將 body 加上 paddingTop 將內容物往下移動
document.body.style.paddingTop = navbar.offsetHeight+'px';
//透過將 logo 元素的最大寬度由0變成500 讓內容物出現在頁面上
logo.style.maxWidth = `500px`;
//讓下方的 div 容器有放大的效果
wrap.style.transform = `scale(1)`;
} else {
navbar.style.position = 'relative';
document.body.style.paddingTop = 0;
wrap.style.transform = `scale(0.98)`;
logo.style.maxWidth = 0;
}
};
今天透過判斷頁面捲動的值,結合許多不同屬性的 CSS 屬性,打造出響應式互動設計的頁面,讓使用者在使用的時候,可以更加便利以及充滿驚喜。大家可以在完成今天的課題之後,嘗試加上更多不同的 CSS屬性,讓整個頁面看起來更加好玩喔!