一、滾動條滾動事件簡介

在 JavaScript 中,滾動條滾動事件是一類在滾動條滾動時觸發(fā)的事件哦。簡單來說,當我們操作頁面的滾動條,讓頁面內(nèi)容上下滾動或者在有滾動條的元素內(nèi)部進行滾動操作時,相應的滾動事件就會被觸發(fā)啦。它的應用場景可是挺廣泛的呢,比如我們常見的在網(wǎng)頁中實現(xiàn) “返回頂部” 的功能時,就需要監(jiān)聽網(wǎng)頁滾動條滾動事件,這個事件通常就是 window.onscroll。當這個 onscroll 事件發(fā)生了,我們可以通過 JavaScript 獲取頁面的 scrollTop 值,也就是滾動條向下滾動的位置,然后判斷這個 scrollTop 達到我們設定的某個值時,就可以讓 “返回頂部” 的按鈕顯示出來啦,方便用戶快速回到頁面頂部。再比如在做一些商品推薦展示頁面的時候,為了讓頁面可以有較長的展示空間,就可以創(chuàng)建一個能夠一直下拉的滾動條,也就是利用滾動條滾動事件來實現(xiàn)啦。具體實現(xiàn)時,先是在 CSS 里創(chuàng)建一個合適的高度,像可以設置 body{padding-bottom: 2000px;} 這樣先搭建出一個大的框架,接著通過 JavaScript 代碼去捕獲頁面的總高度(可以用 document.documentElement.scrollHeight 獲取)、被卷去的高度(也就是從頂部到當前滾動位置的距離,用 document.documentElement.scrollTop 獲取)以及頁面的可視高度(通過 document.documentElement.clientHeight 獲?。?,然后用總高度減去被卷去的高度再減去可視高度,就能得到頁面距離底部的距離啦。之后用 if 語句進行判斷,當這個距離達到某個設定值的時候,進行相應的操作,比如增加頁面的高度等,再不斷進行這樣的判斷,就能完成一個簡易的滾動條事件啦。像要監(jiān)聽整個頁面滾動的話,可以使用 window.addEventListener('scroll',function(){// 執(zhí)行的具體操作}) 這樣的語句哦,給 window 或者 document 添加 scroll 事件都是可行的呢。要是想監(jiān)聽某個元素內(nèi)部的滾動情況呀,直接給對應的那個元素添加相應的滾動事件就好啦??傊?,JavaScript 滾動條滾動事件在優(yōu)化網(wǎng)頁交互體驗等方面起著挺重要的作用呢。
二、常見應用場景
(一)商品推薦場景
在電商平臺或者各類商品展示的網(wǎng)頁中,商品推薦頁面常常會利用到 JavaScript 滾動條滾動事件哦。我們都知道,商品眾多的時候,有限的頁面空間很難將所有商品一次性展示出來呀。這時候,滾動條滾動事件就派上大用場啦。比如說,我們可以先在 CSS 里設置好頁面的基礎樣式,像 body{padding-bottom: 2000px;} 這樣,為頁面搭建出一個足夠長的大框架,來容納更多商品信息哦。然后通過 JavaScript 代碼去獲取頁面的一些關鍵高度值,像用 document.documentElement.scrollHeight 獲取頁面的總高度,用 document.documentElement.scrollTop 獲取從頂部到當前滾動位置的被卷去的高度,以及通過 document.documentElement.clientHeight 拿到頁面的可視高度呢。接下來,用總高度減去被卷去的高度再減去可視高度,就能算出頁面距離底部的距離啦。再利用 if 語句進行判斷,當這個距離達到我們預先設定的某個值的時候,就可以進行相應的操作,比如增加頁面的高度,也就是讓商品展示區(qū)域進一步向下延伸啦,這樣用戶下拉滾動條的時候,就能不斷看到更多的商品內(nèi)容哦。如此一來,既增加了商品的展示空間,又能讓用戶按照自己的瀏覽節(jié)奏,方便地查看各種商品,大大提升了用戶的瀏覽體驗呢。
(二)固定導航欄與返回頂部功能
當我們?yōu)g覽一些內(nèi)容比較長的網(wǎng)頁時,會發(fā)現(xiàn)有時候隨著頁面滾動,導航欄卻能固定在某個位置,方便我們隨時進行各種操作,還有那個返回頂部的按鈕,在頁面滾下去很多后出現(xiàn),點擊就能快速回到頁面頂部,這背后其實都離不開 JavaScript 滾動條滾動事件呀。舉個例子呀,先通過 window.addEventListener('scroll',function(){// 執(zhí)行的具體操作}) 這樣的語句來監(jiān)聽整個頁面的滾動情況哦。獲取元素的時候,比如要獲取導航欄元素,像 let navBar = document.querySelector('#navBar') 這樣去拿到對應的 DOM 節(jié)點,同時還可以獲取導航欄的高度等信息,像 let navBarH = navBar.offsetHeight 哦。接著,隨著頁面滾動,也就是滾動條滾動事件觸發(fā)時,我們可以獲取當前滾動出屏幕的距離,比如用 let offset = document.documentElement.scrollTop 來獲取。然后把這個距離和導航欄原本距離頁面頂部的距離(事先獲取好的哦)進行比較,如果滾動出屏幕的距離大于等于導航欄離頂部的距離了,就通過 navBar.classList.add('fixed') 這樣的代碼給導航欄添加固定的樣式,讓它固定在頂部啦。不過要注意哦,導航欄固定后可能會造成內(nèi)容往上移從而重疊,這時候還得給內(nèi)容添加一個 margin-top 樣式,樣式的值通常就是導航欄的高度,像 mainPart.style.marginTop = navBarH + 10 + 'px' 這樣去調(diào)整呢。而對于返回頂部按鈕呀,同樣是監(jiān)聽滾動條滾動事件,當頁面滾動到一定位置,也就是 scrollTop 值達到我們設定的某個程度時,就讓返回頂部按鈕顯示出來,方便用戶點擊它,通過代碼讓頁面快速滾動回頂部,給用戶操作帶來極大的便利哦。
(三)樓層顯示切換
在一些內(nèi)容豐富并且采用分樓層展示的頁面里,JavaScript 滾動條滾動事件可以創(chuàng)造出很有意思的交互效果哦。比如在一些大型的資訊網(wǎng)站、樓盤介紹頁面等場景中經(jīng)常會看到呢。頁面里會有不同的樓層板塊,每個樓層都有各自對應的內(nèi)容哦。我們可以先獲取各個樓層元素,像用 document.querySelectorAll('.layer') 這樣的方式把所有樓層元素都選出來存好。然后同樣是監(jiān)聽滾動條滾動事件啦,當滾動條滾動到對應樓層區(qū)間的時候,通過事件來切換不同樓層內(nèi)容的顯示樣式哦。具體來說,隨著頁面滾動,也就是滾動條滾動事件觸發(fā)時,獲取當前滾動條滾動的距離,比如 var scrollTop = document.documentElement.scrollTop 。接著去遍歷每個樓層元素,判斷如果某個樓層的 clientHeight + offsetTop 大于當前滾動距離并且當前滾動距離又大于這個樓層的 offsetTop 時,那就意味著滾動到這個樓層的頂部范圍啦,這時候就可以通過代碼去改變對應的樓層導航按鈕等元素的樣式,像給對應的樓層導航按鈕添加一個 active 類名,表示當前樓層處于激活狀態(tài)呀,也就是 lis[i].classList.add("active") 這樣哦,讓用戶清晰地知道當前所在樓層呢。要是滾動離開了這個樓層范圍,就把這個 active 類名移除掉,切換顯示樣式,實現(xiàn)了樓層之間隨著滾動條滾動的交互切換效果,讓用戶能更方便地瀏覽不同樓層的內(nèi)容哦。
三、獲取滾動相關位置信息
(一)scrollLeft 屬性
在 JavaScript 中,scrollLeft屬性有著重要的作用哦。它可以獲取或者設置對象的最左邊到對象在當前窗口顯示的范圍內(nèi)的左邊的距離,簡單來講,就是獲取元素被滾動條向左拉動的距離呀,也就是元素內(nèi)容往左滾出去看不到的那部分距離呢。比如說,當我們在一個內(nèi)容寬度超過其自身容器寬度的元素中,拉動水平滾動條時,這個屬性就能告訴我們元素往左滾動了多遠啦。而且值得注意的是,scrollLeft屬性是可讀寫的哦。這意味著我們不僅可以通過它獲取當前的滾動距離,還能對它進行賦值操作,從而改變元素的滾動位置呢。舉個例子,如果我們有一個div元素,里面的內(nèi)容比較寬,出現(xiàn)了水平滾動條,我們可以通過代碼document.getElementById('yourDivId').scrollLeft = 100;(這里的yourDivId要替換成實際的div元素的id哦),就可以讓這個div的內(nèi)容往左滾動一段距離,將原本看不到的部分顯示出來啦,在很多網(wǎng)頁的圖片輪播、橫向內(nèi)容展示等場景中都會用到這個屬性來實現(xiàn)各種滾動效果呢。
(二)scrollTop 屬性
scrollTop屬性同樣很關鍵呀,它用于獲取或者設置對象的最頂部到對象在當前窗口顯示的范圍內(nèi)的頂邊的距離,也就是元素滾動條被向下拉動的距離哦,通俗地說就是獲取被卷去的頭部距離,即從頂部到當前滾動位置的距離呢。比如在一個網(wǎng)頁頁面或者有滾動條的元素中,隨著我們滾動鼠標滾輪或者拖動滾動條往下滾動,scrollTop的值就會相應地發(fā)生變化啦。它也是可讀寫的屬性哦,在實際應用中,用處可不少呢。像我們常見的制作 “返回頂部” 按鈕的功能,就會利用到它啦。通過監(jiān)聽頁面的滾動事件,也就是像window.addEventListener('scroll', function() {})這樣的代碼來監(jiān)聽整個頁面滾動情況哦,然后在事件處理函數(shù)中獲取scrollTop的值,當這個值達到我們預先設定的某個數(shù)值時,就讓 “返回頂部” 按鈕顯示出來,方便用戶點擊后可以通過代碼document.documentElement.scrollTop = 0(對于大部分瀏覽器來說這樣設置能讓頁面快速回到頂部哦,不過有些瀏覽器可能要使用document.body.scrollTop = 0,實際開發(fā)中還可以寫兼容代碼呢)將頁面滾動條快速回到頂部位置呀。再比如在一些內(nèi)容很長的頁面中,根據(jù)scrollTop的值來判斷當前滾動到了哪個部分,然后去改變對應區(qū)域的樣式或者加載相關的內(nèi)容等等,通過它來實現(xiàn)很多有意思的滾動交互效果呢。
四、代碼示例展示
(一)簡易滾動條創(chuàng)建代碼
以下是一個創(chuàng)建簡易滾動條的基礎代碼示例,幫助大家理解其實現(xiàn)邏輯哦。首先,在 HTML 中我們需要有一個元素來作為滾動條出現(xiàn)的容器,比如一個div元素在上述代碼中,我們先在 CSS 里定義了.scroll-container這個類對應的元素樣式,設置了寬度、高度以及overflow: auto,這樣當內(nèi)容超出設定的高度時就會出現(xiàn)滾動條啦。然后在 JavaScript 部分,通過addEventListener給這個容器添加了scroll事件監(jiān)聽。在事件處理函數(shù)中,先是獲取了頁面的幾個關鍵高度信息,像clientHeight表示可視高度,scrollTop是滾動的距離,scrollHeight則是內(nèi)容總高度。接著用if語句進行條件判斷,比如判斷滾動條滾動到接近底部(這里設定為距離底部還有 10 像素左右)的時候打印提示信息,同時還根據(jù)滾動條滾動的scrollTop值來改變?nèi)萜鞯谋尘邦伾?,大?50 像素時變?yōu)闇\灰色,否則為白色,以此來展示如何通過不斷更新頁面高度相關的狀態(tài)來實現(xiàn)一些滾動條滾動時的交互效果哦。
(二)功能實現(xiàn)代碼舉例
1. 固定元素顯示隱藏代碼
以固定頂部導航欄為例,來看下相應的代碼實現(xiàn)哦。在這段代碼里,首先在 CSS 中定義了導航欄和頁面主要內(nèi)容的基礎樣式,導航欄設置了背景色、文字顏色以及內(nèi)邊距等,主要內(nèi)容區(qū)域設置了一個較大的高度來模擬長頁面哦。然后在 JavaScript 部分,先通過document.getElementById獲取到導航欄這個元素,同時定義了prevScrollTop來記錄上一次滾動條的滾動位置。接著利用window.addEventListener監(jiān)聽scroll事件,在事件處理函數(shù)中獲取當前滾動條滾動的位置currentScrollTop。通過比較當前滾動位置和上一次滾動位置,如果頁面向上滾動(也就是currentScrollTop < prevScrollTop),就把導航欄往上移隱藏起來(通過設置top為負的導航欄高度值);而當頁面向下滾動且滾動距離大于 100px 時(currentScrollTop > 100),就讓導航欄固定在頂部(設置top為 0 且position為fixed),并且不斷更新prevScrollTop的值,這樣就能隨著滾動條滾動準確控制導航欄的顯示和隱藏啦。
2. 樓層樣式切換代碼
下面針對樓層顯示切換功能展示具體的代碼實現(xiàn)哦。在上述代碼中,先是在 CSS 里定義了樓層元素.floor的樣式,包括高度、邊框等,同時設置了有active類名時的背景色變化,還定義了樓層導航欄.floor-nav的位置以及里面列表項的樣式哦。然后在 JavaScript 部分,通過document.querySelectorAll獲取到所有樓層元素和樓層導航欄的列表項元素。接著利用window.addEventListener監(jiān)聽scroll事件,在事件處理函數(shù)中獲取當前滾動條滾動的距離scrollTop,然后遍歷每個樓層元素,判斷如果當前樓層的offsetTop小于scrollTop并且scrollTop小于當前樓層的offsetTop加上clientHeight(也就是滾動到了這個樓層的區(qū)間范圍),就給對應的樓層元素和樓層導航欄的對應列表項添加active類名,實現(xiàn)樣式切換,讓用戶知道當前所在樓層;要是滾動離開了這個樓層范圍,就移除active類名。此外,還給樓層導航欄的每個列表項添加了點擊事件監(jiān)聽,當點擊某個列表項時,獲取對應的目標樓層id,然后通過window.scrollTo方法讓頁面平滑滾動到目標樓層的頂部位置,方便用戶快速切換樓層瀏覽哦。
五、常見問題及解決辦法
(一)移動端原生滾動影響其他元素問題
在移動端進行頁面開發(fā)時,使用原生滾動有時候會遇到一個比較頭疼的問題,那就是當我們在某個區(qū)域進行滾動操作時,會連帶滾動其他元素,這往往不是我們期望的效果呀。比如說,在一個包含多個模塊的移動端頁面里,我們只想讓其中一個內(nèi)容較多、有滾動需求的模塊能夠獨立滾動查看詳情,可實際操作時,一滾動這個模塊,整個頁面或者其他相鄰模塊也跟著一起滾動了,嚴重影響了用戶體驗呢。那怎么解決這個問題呢?其實可以通過設置 preventDefault 屬性來屏蔽默認事件哦。原理就是原生滾動在移動端默認的行為會影響到相關聯(lián)的元素,而 preventDefault 能夠阻止這種默認行為的發(fā)生,讓滾動操作僅僅局限在我們期望的那個元素區(qū)域內(nèi)呀。像在使用一些滾動插件(例如 IScroll)時,初始化的時候就可以這樣設置:this.myScroll = new IScroll('#wrapper', {preventDefault: true }); 這里的 #wrapper 就是我們想要讓其獨立滾動的那個元素對應的選擇器啦,通過這樣的設置,就能避免滾動當前區(qū)域的時候連帶滾動其他元素了哦。
(二)滾動監(jiān)聽數(shù)值獲取滯后問題
在給頁面綁定滾動監(jiān)聽事件時,可能會碰到獲取到的滾動數(shù)值是上一次的值這種情況哦,這就導致基于滾動數(shù)值去做的一些交互效果出現(xiàn)偏差啦。比如我們想實現(xiàn)一個隨著頁面滾動,某個元素根據(jù)滾動位置實時改變透明度的功能,要是獲取的滾動數(shù)值不準確、有滯后,那這個元素的透明度變化就會和預期不符,看起來就很奇怪呀。解決這個問題可以嘗試更換綁定對象哦。有時候我們可能習慣給 window 或者某個大的容器元素綁定滾動事件,但這樣可能會由于一些瀏覽器的渲染機制或者事件觸發(fā)順序等原因,導致獲取數(shù)值不及時。不妨試著給更具體的、直接關聯(lián)滾動區(qū)域的元素去綁定滾動監(jiān)聽事件呀。另外,還可以考慮使用防抖(debounce)或者節(jié)流(throttle)的方法來優(yōu)化滾動事件的觸發(fā)頻率,減少不必要的重復獲取數(shù)值等操作,從而提高獲取滾動數(shù)值的準確性和及時性呢,像下面這樣的防抖函數(shù)就可以通過合理運用這些思路和方法,就能盡量避免滾動監(jiān)聽數(shù)值獲取滯后的問題,讓頁面滾動交互效果更順暢準確啦。
(三)判斷滾動條是否到底部問題
在實際開發(fā)中,常常需要判斷滾動條是否滾動到底部哦,比如實現(xiàn) “加載更多” 的功能,當滾動條到底部了,就自動加載新的數(shù)據(jù)展示給用戶呀。那怎么判斷滾動條是否到底部呢?這就需要用到 DOM 的幾個屬性值啦,以縱向滾動條為例(橫向滾動條方法類似哦),主要涉及 scrollTop、clientHeight、scrollHeight 這幾個屬性哦。scrollTop 指的是滾動條在 Y 軸上的滾動距離,也就是從頂部已經(jīng)滾動了多遠;clientHeight 是內(nèi)容可視區(qū)域的高度,就是我們在瀏覽器窗口中能直接看到的那部分高度;scrollHeight 則是內(nèi)容可視區(qū)域的高度加上溢出(滾動)的距離,也就是整個內(nèi)容的總高度啦。從這三個屬性的含義就能看出來,滾動條到底部的條件就是 scrollTop + clientHeight === scrollHeight 哦。要是判斷在某一個元素中的滾動條是否到底部呀,根據(jù)類似的思想,將 document.body 等換成特定的元素即可,獲取 scrollTop 和 scrollHeight 的方式是一樣