JS滾輪事件(mousewheel/DOMMouseScroll)了解


一、學無止境、溫故知新

//zxx: 本段與技術無關,一些很個人的吐槽,可以跳過
正常狀態已經沒有了小學生時代過目不忘的記憶力了,很多自己折騰的東西、接觸的東西,短短1年之后就全然不記得了。比方說,完全記不得獲取元素與頁面距離的方法(getBoundingClientRect),或者是不記得現代瀏覽器下觸發DOM自定義事件的方法(dispatchEvent). 顯然,適當的溫習,翻閱以前的東西,或者自己空余時間處理相關的東西還是有必要的。其實,細想,東西記不住是自己自身原因,在折騰的時候就沒有想方設法牢記(而不是通過反復使用記住)。比方說getBoundingClientRect就是“得到客戶端矩形邊界”的意思,或者使用邪惡記法記住“割(g)逼(b)艹(c)軟(r)”。dispatchEvent方法使用“3步走”,“創建(createEvent)-初始(init*Event)-分派(dispatchEvent)”。

學習的腳步不能停止。一站到底的那些“變態”們也有不知道的東西,顯然,我們這些草輩,尤其年輕的自己,不知道的更多。誰年輕的時候沒有過或多或少的迷茫,問自己“路在何方”,問自己“該做哪個方向”,無論你選擇的是什么,學習的腳步是不能停止的。堅持着堅持着,路自然就會清晰,你就會知道接下來該怎么走了。只怕畏首畏尾,得過且過,年輕就是資本,義無反顧前行吧。

我憑着興趣走上現在的道路,完全是興趣學習(我喜歡這些,我要學),不是職業學習(做前端需要什么,我就去學什么)。工作的這些年,技術、產品的自我沉浸不知不覺限制了自己的眼界,好在意識到問題的存在其實已經解決了問題的一半。這里之所以會說這些是想提醒自己,萬萬不可矯枉過正,技術、產品的學習還是主要的,只是要多多抬頭看看辦公室之外的世界(不是刷微博獲得的淺認識)。

昨天機緣巧合遇到“滾輪事件”,以前折騰“自定義滾動條”時候使用過鼠標滾輪事件,不過這是基於MooTools已經兼容好的mousewheel事件實現的,如果要說出其中的實現機制,瀏覽器兼容差異等,就傻眼了。學無止境,因此,查閱之,實踐之,整理之。

二、兼容差異大全

滾輪事件的兼容性差異有些不拘一格,不是以往的IE8-派和其他派,而是FireFox派和其他派。

包括IE6在內的瀏覽器是使用onmousewheel,而FireFox瀏覽器一個人使用DOMMouseScroll. 經自己測試,即使現在FireFox 19下,也是不識onmousewheel

一個最簡單的使用差異(body滾動條由內部一定高div撐開):

document.body.onmousewheel = function(event) {
    event = event || window.event;
    console.dir(event);	
};
document.body.addEventListener("DOMMouseScroll", function(event) {
    console.dir(event);	
});

以上輸出差異見下面(IE7, IE10, Chrome, 以及FireFox,鼠標向下滾動, win7)(可點擊此頁面單獨查看表格內容):

屬性名\瀏覽器 FireFox Chrome IE10 IE7
recordset ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 null
type DOMMouseScroll mousewheel mousewheel mousewheel
fromElement ×沒有該屬性 null null null
toElement ×沒有該屬性 [object HTMLDivElement] null null
altLeft ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 false
keyCode ×沒有該屬性 0 ×沒有該屬性 0
repeat ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 false
reason ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 0
data ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
behaviorCookie ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 0
source ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 null
contentOverflow ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 false
behaviorPart ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 0
url ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
dataTransfer ×沒有該屬性 null ×沒有該屬性 null
ctrlKey false false false false
shiftLeft ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 false
dataFld ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
returnValue ×沒有該屬性 true ×沒有該屬性 undefined
qualifier ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
wheelDelta ×沒有該屬性 -120 -120 -120
bookmarks ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 null
actionURL ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
button 0 0 0 0
srcFilter ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 null
nextPage ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
cancelBubble false false false false
x ×沒有該屬性 799 876 839
y ×沒有該屬性 283 322 325
buttonID ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 0
srcElement ×沒有該屬性 [object HTMLDivElement] [object HTMLDivElement] [object]
screenX 934 799 876 841
screenY 453 344 377 382
srcUrn ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
origin ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
boundElements ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 [object]
clientX 1168 799 876 841
clientY 456 283 322 327
propertyName ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 空字符串
shiftKey false false false false
ctrlLeft ×沒有該屬性 ×沒有該屬性 ×沒有該屬性 false
offsetX ×沒有該屬性 791 868 829
offsetY ×沒有該屬性 275 314 310
altKey false false false false
initMouseWheelEvent ×沒有該屬性 ×沒有該屬性 function initMouseWheelEvent() { [native code] } ×沒有該屬性
layerX 1168 799 876 ×沒有該屬性
layerY 456 283 322 ×沒有該屬性
which 1 1 1 ×沒有該屬性
buttons 0 ×沒有該屬性 0 ×沒有該屬性
metaKey false false false ×沒有該屬性
pageX 1168 799 876 ×沒有該屬性
pageY 456 283 322 ×沒有該屬性
relatedTarget null null null ×沒有該屬性
getModifierState function getModifierState() { [native code] } ×沒有該屬性 function getModifierState() { [native code] } ×沒有該屬性
initMouseEvent function initMouseEvent() { [native code] } function initMouseEvent() { [native code] } function initMouseEvent() { [native code] } ×沒有該屬性
detail 3 0 0 ×沒有該屬性
view [object Window] [object Window] [object Window] ×沒有該屬性
initUIEvent function initUIEvent() { [native code] } function initUIEvent() { [native code] } function initUIEvent() { [native code] } ×沒有該屬性
bubbles true true true ×沒有該屬性
cancelable true true true ×沒有該屬性
currentTarget [object HTMLBodyElement] [object HTMLBodyElement] [object HTMLBodyElement] ×沒有該屬性
defaultPrevented false false false ×沒有該屬性
eventPhase 3 3 3 ×沒有該屬性
isTrusted true ×沒有該屬性 true ×沒有該屬性
target [object HTMLDivElement] [object HTMLDivElement] [object HTMLDivElement] ×沒有該屬性
timeStamp 14296937 1366106275177 1366106216522 ×沒有該屬性
initEvent function initEvent() { [native code] } function initEvent() { [native code] } function initEvent() { [native code] } ×沒有該屬性
preventDefault function preventDefault() { [native code] } function preventDefault() { [native code] } function preventDefault() { [native code] } ×沒有該屬性
stopImmediate
Propagation
function stopImmediate
Propagation() { [native code] }
function stopImmediate
Propagation() { [native code] }
function stopImmediate
Propagation() { [native code] }
×沒有該屬性
stopPropagation function stopPropagation() { [native code] } function stopPropagation() { [native code] } function stopPropagation() { [native code] } ×沒有該屬性
AT_TARGET 2 2 2 ×沒有該屬性
BUBBLING_PHASE 3 3 3 ×沒有該屬性
CAPTURING_PHASE 1 1 1 ×沒有該屬性
webkitDirection
InvertedFromDevice
×沒有該屬性 false ×沒有該屬性 ×沒有該屬性
wheelDeltaY ×沒有該屬性 -120 ×沒有該屬性 ×沒有該屬性
wheelDeltaX ×沒有該屬性 0 ×沒有該屬性 ×沒有該屬性
webkitMovementY ×沒有該屬性 0 ×沒有該屬性 ×沒有該屬性
webkitMovementX ×沒有該屬性 0 ×沒有該屬性 ×沒有該屬性
charCode ×沒有該屬性 0 ×沒有該屬性 ×沒有該屬性
clipboardData ×沒有該屬性 undefined ×沒有該屬性 ×沒有該屬性
initWebKitWheelEvent ×沒有該屬性 function initWebKitWheelEvent() { [native code] } ×沒有該屬性 ×沒有該屬性
NONE 0 0 ×沒有該屬性 ×沒有該屬性
MOUSEDOWN 1 1 ×沒有該屬性 ×沒有該屬性
MOUSEUP 2 2 ×沒有該屬性 ×沒有該屬性
MOUSEOVER 4 4 ×沒有該屬性 ×沒有該屬性
MOUSEOUT 8 8 ×沒有該屬性 ×沒有該屬性
MOUSEMOVE 16 16 ×沒有該屬性 ×沒有該屬性
MOUSEDRAG 32 32 ×沒有該屬性 ×沒有該屬性
CLICK 64 64 ×沒有該屬性 ×沒有該屬性
DBLCLICK 128 128 ×沒有該屬性 ×沒有該屬性
KEYDOWN 256 256 ×沒有該屬性 ×沒有該屬性
KEYUP 512 512 ×沒有該屬性 ×沒有該屬性
KEYPRESS 1024 1024 ×沒有該屬性 ×沒有該屬性
DRAGDROP 2048 2048 ×沒有該屬性 ×沒有該屬性
FOCUS 4096 4096 ×沒有該屬性 ×沒有該屬性
BLUR 8192 8192 ×沒有該屬性 ×沒有該屬性
SELECT 16384 16384 ×沒有該屬性 ×沒有該屬性
CHANGE 32768 32768 ×沒有該屬性 ×沒有該屬性
rangeParent [object HTMLDivElement] ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
rangeOffset 0 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
isChar false ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
mozMovementX 1168 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
mozMovementY 576 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
mozPressure 0 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
mozInputSource 1 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
initNSMouseEvent function initNSMouseEvent() { [native code] } ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
axis 2 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
initMouseScrollEvent function initMouseScrollEvent() { [native code] } ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
originalTarget [object HTMLDivElement] ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
explicitOriginalTarget [object HTMLDivElement] ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
preventBubble function preventBubble() { [native code] } ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
preventCapture function preventCapture() { [native code] } ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
getPreventDefault function getPreventDefault() { [native code] } ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
RESET 65536 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
SUBMIT 131072 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
SCROLL 262144 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
LOAD 524288 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
UNLOAD 1048576 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
XFER_DONE 2097152 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
ABORT 4194304 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
ERROR 8388608 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
LOCATE 16777216 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOVE 33554432 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
RESIZE 67108864 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
FORWARD 134217728 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
HELP 268435456 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
BACK 536870912 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
TEXT 1073741824 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
ALT_MASK 1 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
CONTROL_MASK 2 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
SHIFT_MASK 4 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
META_MASK 8 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
SCROLL_PAGE_UP -32768 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
SCROLL_PAGE_DOWN 32768 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_UNKNOWN 0 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_MOUSE 1 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_PEN 2 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_ERASER 3 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_CURSOR 4 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_TOUCH 5 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
MOZ_SOURCE_KEYBOARD 6 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
HORIZONTAL_AXIS 1 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性
VERTICAL_AXIS 2 ×沒有該屬性 ×沒有該屬性 ×沒有該屬性

對照表格內容,可以看到,鼠標滾動事件與點擊事件有很多類似的地方。比方說兼容部分:event.typeevent.screenX/event.screenYevent.clientX/event.clientYevent.altKeyevent.shiftKeyevent.cancelBubble都是一樣的,不兼容的部分,IE6-8的event.srcElement與其他瀏覽器的event.target.

進口的蘋果分外甜,滾輪事件顯然也是有額外的差異的,想想也知道,是與滾輪相關的,也是我們實際應用最常用的。

在除了FireFox之外的瀏覽器下,滾動的上下滾動與否是下面這個-event.wheelDelta(//zxx: 本文發布后補充:Delta讀音對應希臘字母△,形狀就像三角褲,因此,wheelDelta可以記做“滾輪的三角褲”):
event.wheelDelta與滾動示意

根據自己的測試,在我的win7系統下,無論IE7, IE10, Opera12,或者是safari5.1,每次往下滾動event.wheelDelta值都是-120//zxx:網上有說法說Safari值為-360, 我對此表示懷疑,下圖為我的測試截圖。
Safari瀏覽器下wheelDelta截圖

對於FireFox瀏覽器(Opera瀏覽器也有),判斷鼠標滾動方向的屬性為event.detail, 向下滾動值為3.
FireFox瀏覽器下event.detail, 值為3

需要注意的是,FireFox瀏覽器的方向判斷的數值的正負與其他瀏覽器是相反的。FireFox瀏覽器向下滾動是正值,而其他瀏覽器是負值。

三、兼容的滾輪事件方法

知己知彼百戰百勝,知道了差異就知道如何處理這些差異。畢竟不是寫JS庫,我們這里只處理滾動方向這塊的差異。

整合我們通常事件添加方法,於是有(下代碼代號為addEvent.js):

/**
 * 簡易的事件添加方法
 */
 
define(function(require, exports, module) {
    exports.addEvent = (function(window, undefined) {        
        var _eventCompat = function(event) {
            var type = event.type;
            if (type == 'DOMMouseScroll' || type == 'mousewheel') {
                event.delta = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
            }
            //alert(event.delta);
            if (event.srcElement && !event.target) {
                event.target = event.srcElement;    
            }
            if (!event.preventDefault && event.returnValue !== undefined) {
                event.preventDefault = function() {
                    event.returnValue = false;
                };
            }
            /* 
               ......其他一些兼容性處理 */
            return event;
        };
        if (window.addEventListener) {
            return function(el, type, fn, capture) {
                if (type === "mousewheel" && document.mozFullScreen !== undefined) {
                    type = "DOMMouseScroll";
                }
                el.addEventListener(type, function(event) {
                    fn.call(this, _eventCompat(event));
                }, capture || false);
            }
        } else if (window.attachEvent) {
            return function(el, type, fn, capture) {
                el.attachEvent("on" + type, function(event) {
                    event = event || window.event;
                    fn.call(el, _eventCompat(event));    
                });
            }
        }
        return function() {};    
    })(window);        
});

於是,我們就可以很從容使用mousewheel事件了。例如:

addEvent(dom, "mousewheel", function(event) {
    if (event.delta < 0) { alert("鼠標向上滾了!"); }
});

四、簡單的實例、上面方法驗證

本想做個完備的幻燈平滑移動效果(左右有點擊按鈕之類),結果一不小心,都凌晨了,於是,改變主意了,就只做了個鼠標滾動,圖片列表左右移動的效果。您可以狠狠地點擊這里:滾輪事件下圖片列表左右滑動demo

鼠標放在圖片列表區域上,鼠標滾輪下滾滾,上滾滾,就可以看到圖片列表們左右平滑移動的效果了。
鼠標滾輪事件下的平滑切換效果

其中的滾輪相關交互就是使用的上面exports暴露的addEvent方法。

相關代碼實現如下,下面這個展示的就是平滑移動的核心代碼們(代號為slide.js):

/**
 * 簡易的列表左右滑動切換效果
 * 鼠標事件是關鍵,因此,一些數值寫死在方法中,純測試用
 */
 
define(function(require, exports, module) {
    var Event = require("/study/201304/addEvent.js");
    var _move = function(ele, to, from) {
        // 動畫實現
        // ...
    };
    return {
        index: 0,
        visible: 4,
        init: function(box) {
            // box指滾動的列表容器
            var self = this
              , length = box.getElementsByTagName("li").length;
            Event.addEvent(box.parentNode, "mousewheel", function(event) {
                 if (event.delta > 0 && self.index > 0) {
                    // 往上滾
                    self.index--;
                 } else if (event.delta < 0 && self.index < length - self.visible) {
                     // 往下
                     self.index++;                     
                 } else {
                    return; 
                 }
                 _move(box, -1 * self.index * 140);    
             
                 event.preventDefault();
            });
        }
    };
});
 

原理很簡單,滾輪改變,索引改變,也就是列表的最終位置改變,動畫到目標位置即可。

然后,demo頁面使用seajs簡單調用就可以了!

var $ = function(id) {
    return document.getElementById(id);
};
seajs.use("/study/201304/slide.js", function(slide) {
    slide.init($("slideBox"));
});

就結束了,一些具體細節,例如關於HTML部分,或者動畫的實現等,可以去demo等查看代碼展示。

不過從效果來看,IE6以及IE7瀏覽器下的滾動並沒有hold頁面的滾動條,多番其他嘗試也是如此,希望可以有相關經驗的同行指點下,優化IE7/IE7瀏覽器下的體驗效果。

原本還想再添加一個自定義滾動條的demo的,一看時間,我勒個去,已經1:11:11了,好不吉利的數字啊,看了下程序員運勢萬年歷,今天不適宜寫demo。於是,結語睡覺。

五、首尾呼應的結語

正常狀態鼠標滾輪相關東西,我現在各個細節歷歷在目,要是現在發我張卷子,考鼠標滾輪事件知識,沒有個90分我自己都不信。然而,目前為止,自己並未刻意去記憶,因此,如果接下來的1年時間自己很少或不接觸相關內容。估計到時別人一問,小心臟一慌,說不定就一下子想不起"DOMMouseScroll"這廝了。因此,在結尾處,我要給自己來個特殊記憶。

怎么記呢?恩……啊,抓狂了,想不出來event.wheelDelta/event.detail

wheelDelta→滾輪的三角褲,火狐是騷狐狸,沒有這個三角褲?欲知詳情,請看火狐?

……得,睡了,夢里再想吧~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM