Hike News
Hike News

Scrollspy 滾動監控

前言

當網頁內有需要快速移動到的位置,或是需要用到選單提示使用者捲軸移動到的位置時,為了不會影響到效能,找到一個使用 CSS 就能達到的方法,所以筆記記錄下來。

CSS Scrollspy

在 HTML 內放入網頁內容的每個 section 都要給予一個 id ,不使用 class 是因為 id 可以利用 a 連結快速跳轉到網頁 id 所在位置的功能。

選單 nav 的部分是使用 a 連結

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 網頁內容 -->
<section id="one">section one</section>
<section id="two">section two</section>
<section id="three">section three</section>
<section id="four">section four</section>
<!-- 選單 -->
<nav>
<a href="#one">one</a>
<a href="#two">two</a>
<a href="#three">three</a>
<a href="#four">four</a>
</nav>

接著是使用 CSS 的 :hover 的功能

:hover 是當滑鼠滑到標籤元素上時,才會觸發套用樣式

相反的也可以當網頁移動到網頁內容的時候,改變選單的樣式。

1
2
3
4
5
6
7
8
nav a:hover,
section#one:hover ~ nav a[href="#one"],
section#two:hover ~ nav a[href="#two"],
section#three:hover ~ nav a[href="#three"],
section#four:hover ~ nav a[href="#four"] {
background-color: #fff;
color: #000;
}

最後再隱藏捲軸

1
2
3
::-webkit-scrollbar {
width: 0;
}

就大功告成了 !效果就會像這樣

意示圖

還有要注意,這作法如果只用滑鼠滾輪滾動時是會沒有反應的!
因為是使用 :hover 作的效果,所以也需要移動滑鼠到在元素上時才會觸發。

JavaScript Scrollspy

HTML 和 CSS 的樣式一樣是上方的,但這裡不是使用 :hover 做變化,而是會在捲軸滑到的元素相對的連結加上一個新的 class 名稱,讓連結的樣式有變化

所以 CSS 只需把 :hover 改成是 .active 就可以了,所以 ::-webkit-scrollbar 也可以刪除

1
2
3
4
.active {
background-color: #fff;
color: #000;
}

在 HTML 的選單 <a href="#one">one</a> 需先加上class="active"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(function() {
'use strict';

var section = document.querySelectorAll(".section");
var sections = {}; // 要存元素的資料

// 一一取出元素的 id 和元素上緣的位置值
Array.prototype.forEach.call(section, function(e) {
sections[e.id] = e.offsetTop;
});

// 當滑動捲軸時
window.onscroll = function() {
// 取捲軸上緣的位置值,不然就取 body 上緣的位置值
var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;

// 判斷每一個元素的位置,當捲軸捲到時
// 移除 classname 是 active 的,並在目前位置的相對 A 連結加上 active 的 classname
for (var i in sections) {
if (sections[i] <= scrollPosition) {
document.querySelector('.active').setAttribute('class', ' ');
document.querySelector('a[href*=' + i + ']').setAttribute('class', 'active');
}
}
};
})();

參考: Vanilla js ScrollSpy

但這也只可以用在連結的元素不多時,若是有幾十個連結在需要跳到目的地時,就可能會影響效能,這就可以使用相關的插件使用。

Scrollspy.js

產生的效果會根據移動到的位置 自動更新選單連結樣式 變化。

置頂選單

在指定要監聽的元素作為使用者在滾動網頁時監聽的範圍,通常會是整個網頁的內容 <body> ,加上 data-spy="scroll 屬性值,以及可以讓 JavaScript 取用的 data-target 屬性,屬性的值需要和選單內的 a 連結元素的 idclass 名稱一致,還有一個偏移值的屬性 data-offset 預設是 10 ( px 省略 ) ,當點擊 a 連結元素時跳到指定的位置的偏移值

1
2
3
4
<!-- 監聽的範圍 -->
<body data-spy="scroll" data-target=".navbar" data-offset="50">
<!-- 內容 -->
</body>

內容的元素可和前一個的範例一樣使用 <section><nav> 標籤,或是只使用 <div>

在 CSS 要對監聽的元素使用 position: relative;

JavaScript 作法是參考至 Bootstrap JS Scrollspy Bootstrap 滾動監控 (Scrollspy),需事先載入 Scrollspy.js

  • 初始化
1
2
3
4
5
6
7
8
9
10
// javascript
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.scrollspy');
var instances = M.ScrollSpy.init(elems, options);
});

// jQuery
$(document).ready(function(){
$('body').scrollspy({target: ".navbar"});
});
  • scrollspy 可用的方法

    1
    2
    // 尋找目標元素
    var instance = M.ScrollSpy.getInstance(elem);

// 移除插件的功能
instance.destroy();

1
2
3
4
5
6
7

`getInstance(elem)` 是使用預設 **尋找目標元素** `getActiveElement` 的方法

```javascript
function(id) {
return 'a[href="#' + id + '"]';
}

而目標元素必需有 id 的名稱

側邊選單

這是參考 makotot/scrollspy

Scrollspy.js 除了直接下載也可以用 NPM

1
$ npm install --save scrollspy-js

在 HTML 內同樣是使用到 A 連結和目標 ID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 範圍 -->
<div id="scrollspy">
<!-- 選單 -->
<ul class="scrollspy-nav nav">
<li><a href="#one">one</a></li>
<li><a href="#two">two</a></li>
<li><a href="#three">three</a></li>
<li><a href="#four">four</a></li>
</ul>

<!-- 內容 -->
<ul class="content">
<li id="one">one</li>
<li id="two">two</li>
<li id="three">three</li>
<li id="four">four</li>
</ul>
</div>

CSS 樣式

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 固定選單 */
.nav {
position: fixed;
/* 靠右,也可改成置頂 top */
right: 0;
padding: 30px;
...
}
/* 動態加入的 ClassName 樣式 */
.is-inview {
background-color: #4c5eff;
color: #fff;
}

配合 JavaScript 的方法,當 ScrollSpy() 內的路徑 nav

1
2
3
4
5
6
var spy = new ScrollSpy('#scrollspy', {
// 當移動到目標 ID 時,取得連到目標 ID 的 A 連接
nav: '.scrollspy-nav > li > a',
// 動態加入的 ClassName
className: 'is-inview'
});