原生 JS 實(shí)現(xiàn)滾動(dòng)監(jiān)聽

本文的內(nèi)容是 JS 滾動(dòng)監(jiān)聽導(dǎo)航欄更新背景色標(biāo)識(shí)。

之前在 bootstrap 文檔上看到這樣一個(gè)效果,如下

image

因?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 獲取。

(完)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容