我們在做前端開發的時候,曾遇到一些非常炫酷的宣傳頁。例如每一個蘋果產品的主頁面。我們會發現,這樣炫酷的頁面,總是跟隨我們鼠標滾輪的操作,在頁面中響應不同的事件。
一、瀏覽器的不同
我們都知道,需要在前端頁面中監聽到鼠標滾輪的事件,不同瀏覽器內核提供的方法是不同的。所以,每當我們需要監聽鼠標滾輪事件,就需要先判斷使用終端是用的什麼瀏覽器。
在前端,我們可以通過 window.navigator.userAgent 這個瀏覽器自帶的 API 來判斷瀏覽器類型。其中,Chrome、FireFox、Edge 這三個如今最主流的瀏覽器,返回的格式如下:
| 瀏覽器 | userAgent 返回值 |
|---|---|
| Chrome | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 |
| FireFox | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0 |
| Edge | Mozilla/5.0 (Windows NT 10.0; Win64; x64; ServiceUI 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134 |
由於鼠標滾輪事件,只有 FireFox 瀏覽器比較特殊,其它瀏覽器都是一樣的。所以我們只需要判斷是否是 FireFox 瀏覽器即可。
const browser = window.navigator.userAgent; // 獲取瀏覽器信息
const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器
二、監聽滾動事件
在 Chrome 等主流瀏覽器中,我們可以直接通過監聽 mousewheel 事件。其對應事件處理函數中的事件對象 event 包含一個 wheelDelta 屬性。當鼠標滾輪向上滾動的時候,wheelDelta 的值為 120;反之,當鼠標滾輪向下滾動的時候,wheelDelta 的值為 -120。
因此,我們可以直接通過判斷 wheelDelta 的值,獲取對應的操作邏輯:
document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
const ev = e || event;
ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
});
在 FireFox 瀏覽器中,我們需要通過監聽 DOMMouseScroll 事件。其對應事件處理函數中的事件對象 event 並不包含 wheelDelta 屬性,而是使用 detail 代替。並且,detail 屬性的值也是和事件反過來的。當鼠標滾輪向上滾動的時候,detail 的值為 -3;反之,當鼠標滾輪向下滾動的時候,detail 的值為 3。
因此,在 FireFox 瀏覽器中,我們需要通過判斷 detail 的值來獲取對應的操作邏輯:
document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
const ev = e || event;
ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
});
三、完整的監聽滾動函數
通過以上兩步,我們可以簡單的將其構建成一個完整的監聽鼠標滾動的通用函數:
function scroll() {
const browser = window.navigator.userAgent; // 獲取瀏覽器信息
const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器
if (isFirefox) {
document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
const ev = e || event;
ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
});
} else {
document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
const ev = e || event;
ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
});
}
}
一般來説,函數寫成,大功告成。但是咱們不妨思考這樣一個問題:
對於同一個瀏覽器來説,每次進來都要判斷瀏覽器類型,並做出判斷,這樣做是必須的嗎?
比如一個 Chrome 瀏覽器,在第一次訪問的時候,程序需要判斷瀏覽器類型,並根據判斷執行以上程序中 else 的部分。那麼,從第二次開始,同一個 Chrome 瀏覽器進行訪問的時候,可以直接執行 else 部分中的代碼即可,何樂而不為呢?
這樣,掌聲有請我們今天的主角 —— 惰性函數。
四、惰性函數
通過打印,我們可以看到。以上函數在每一次執行的時候,均會完整執行:
於是,我們巧妙的藉助 JavaScript 中函數聲明的特點,對函數名變量進行重新賦值,就可以巧妙地減少執行函數的大小:
function scroll() {
const browser = window.navigator.userAgent; // 獲取瀏覽器信息
const isFirefox = browser.toLowerCase().includes('firefox'); // 判斷是不是 FireFox 瀏覽器
if (isFirefox) {
scroll = function () {
document.addEventListener('DOMMouseScroll', function(e) { // FireFox 瀏覽器使用 DOMMouseScroll 監聽
const ev = e || event;
ev.detail < 0 ? console.log('向上滾動') : console.log('向下滾動'); // detail 為 3,向下滾動;detail 為 -3,向上滾動
});
}
} else {
scroll = function () {
document.addEventListener('mousewheel', function(e) { // 其它瀏覽器使用 mousewheel 監聽
const ev = e || event;
ev.wheelDelta > 0 ? console.log('向上滾動') : console.log('向下滾動'); // wheelDelta 為 -120,向下滾動;wheelDelta 為 120,向上滾動
});
}
}
}
在這裏,我們分別把 if 和 else 中的代碼都放到一個匿名函數中,並且把匿名函數賦值給一個變量。需要注意的是,這個變量並不是我們自己聲明的,而是直接寫了父函數的函數名 scroll。
這裏牽扯到 JavaScript 代碼在瀏覽器加載的底層原理,不再過多展開。感興趣的小夥伴們可以關注我以後的文章,我會在不久的將來做更加詳細的講解。
通過打印,我們發現從第二次開始,執行函數 scroll 不再是整個函數,而僅僅是我們需要執行的匿名函數。
這樣處理之後,程序的執行部分得到了大大的簡化,也可以提升程序的執行效率。
這樣,在函數體中巧用覆蓋賦值的方式寫的匿名函數方法,我們就叫做惰性函數。
關注我,給您更多精彩內容!