本文的內(nèi)容是 JS 滾動(dòng)監(jiān)聽導(dǎo)航欄更新背景色標(biāo)識(shí)。
之前在 bootstrap 文檔上看到這樣一個(gè)效果,如下

因?yàn)?bootstrap 是引用了 jQuery 庫的,非常容易上手,也能方便地用在各個(gè)項(xiàng)目中,但不折騰的前端,跟咸魚有什么區(qū)別。于是就想著不用這些庫,自己用 JS 實(shí)現(xiàn)以下這個(gè)效果,所以就折騰了下。
滾動(dòng)監(jiān)聽原理
首先了解一下滾動(dòng)監(jiān)聽的原理,當(dāng)鼠標(biāo)滾輪滾動(dòng)或頁面滾動(dòng)條滑動(dòng)時(shí),每到一塊內(nèi)容區(qū)域,其上方對(duì)應(yīng)的導(dǎo)航欄狀態(tài)也將更新,那就需要知道每塊內(nèi)容區(qū)域相對(duì)于頂部的距離,將這個(gè)距離跟滾動(dòng)條滑行的距離做比較,根據(jù)比較的結(jié)果來決定是否更新導(dǎo)航欄狀態(tài)標(biāo)。所以整個(gè)大致思路就是:監(jiān)聽頁面的 scroll 事件,然后獲取當(dāng)前頁面滾動(dòng)條縱坐標(biāo)的位置,假設(shè)為 posNav;在獲取內(nèi)容區(qū)域,這里假設(shè)分為內(nèi)容1,內(nèi)容2,內(nèi)容3,獲取這些內(nèi)容塊相對(duì)于視口頂部的距離,假設(shè)是 height ,隨著滾動(dòng)條的滑動(dòng),如果 posNav > height1,那么第一個(gè)導(dǎo)航標(biāo)簽更新;如果 posNav > height2 ,那么第二個(gè)導(dǎo)航更新,...,依次遍歷直至底部,在這個(gè)過程中,還要注意當(dāng)其中一個(gè)加上了背景色之后,跟它同級(jí)的所有元素都要移除掉這個(gè)背景色。
瞄點(diǎn)定位
除了根據(jù)內(nèi)容區(qū)域滾動(dòng)更新之外,這里還涉及到一個(gè)定位的問題,即點(diǎn)擊導(dǎo)航欄,就定位到這個(gè)導(dǎo)航所對(duì)應(yīng)的內(nèi)容區(qū)域,這也是很多單頁面常有的做法。實(shí)現(xiàn)這個(gè)最簡單、粗暴的方法就是使用瞄點(diǎn)定位,給每個(gè)導(dǎo)航 a 標(biāo)簽的 href 屬性寫上 #id 的格式,然后給對(duì)應(yīng)的內(nèi)容區(qū)域加上 id名稱 ,由于 id 是唯一標(biāo)識(shí),因此點(diǎn)擊導(dǎo)航就可直接定位,簡單,無兼容問題,非常好的一個(gè)實(shí)現(xiàn)技巧。
實(shí)現(xiàn)
在開始寫 js 之前,先準(zhǔn)備一個(gè)簡單的靜態(tài)頁面,默認(rèn)給第一個(gè)導(dǎo)航加上 active 屬性,也就是有背景色的效果。
[圖片上傳失敗...(image-9a46b0-1558335997881)]
有了頁面后,先獲取所有導(dǎo)航 li 標(biāo)簽,存在數(shù)組里,等會(huì)用它更新導(dǎo)航欄。
var lis = [...document.querySelectorAll('.nav > ul > li')];
... 是一個(gè)展開運(yùn)算符,querySelectorAll 選取所有 class 為 nav 下面的 ul 的 li 標(biāo)簽元素的集合,展開運(yùn)算符將它們都存在數(shù)組 lis 里。接著獲取內(nèi)容塊區(qū)域相對(duì)于頁面頂部的距離,同樣將它們存在數(shù)組里。
var divs = [...document.querySelectorAll('.mainPage')];
獲取到后,用一個(gè) for 循環(huán)遍歷獲取每個(gè) div 的距離頂部的值,使用 getBoundingClientRect() 方法,它返回元素的大小及其相對(duì)于視口的位置,我們主要使用 top 屬性獲取頂部距離。
var divHeights = [];
for (var i = 0; i < divs.length; i++) { // 循環(huán)每個(gè) div 的距離 top 的值存放至數(shù)組
divHeights.push(divs[i].getBoundingClientRect().top);
}
現(xiàn)在所有該有的元素及其數(shù)值都有了,可以開始寫監(jiān)聽事件了。這里吐個(gè)槽,別看上面寥寥幾句話就完成了這些屬性及其元素的獲取,真正在做的時(shí)候,我花了不少世間查詢這些方法及屬性,像 getBoundingClientRect() 、document.doucmentElement.scrollTop 等屬性的用法,主要是一直使用現(xiàn)成的框架,導(dǎo)致太依賴框架的那些簡便封裝好的方法而忽視了原來方法,所以建議各位用框架久了記得回過來看看每個(gè)方法對(duì)應(yīng)的作用以及在原生 js 的實(shí)現(xiàn)。
好了,監(jiān)聽頁面滾動(dòng)事件把它寫在一個(gè)函數(shù)里,定義如下
function updateNav() {
var scop = document.documentElement.scrollTop;
var k;
for (var i = 0; i < divHeights.length; i++) {
if (scop > divHeights[i] - 50) {
k = i;
}
}
lis[k].classList.add('current');
for (var i = 0; i < lis.length; i++) {
if (i == k) continue;
else lis[i].classList.remove('current');
}
}
最后一句代碼調(diào)用函數(shù)即可
window.addEventListener('scroll', updateNav);
至此,所以代碼都完成了,來看看最終瀏覽器的效果吧:(不知道為什么,簡書上傳 gif 老是失敗,可以點(diǎn)擊下方源碼鏈接查看)
[圖片上傳失敗...(image-1448aa-1558335997881)]
想要獲取完整源代碼,請(qǐng)點(diǎn)擊 這里 由于簡書改版后鏈接出現(xiàn)了問題,請(qǐng)直接訪問 https://github.com/myLightLin/jsDemo 獲取。
(完)