本文復制地址:http://www.zhangxinxu.com/wordpress/?p=3175
一、學無止境、溫故知新
//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.type
, event.screenX/event.screenY
, event.clientX/event.clientY
, event.altKey
, event.shiftKey
, event.cancelBubble
都是一樣的,不兼容的部分,IE6-8的event.srcElement
與其他瀏覽器的event.target
.
進口的蘋果分外甜,滾輪事件顯然也是有額外的差異的,想想也知道,是與滾輪相關的,也是我們實際應用最常用的。
在除了FireFox之外的瀏覽器下,滾動的上下滾動與否是下面這個-event.wheelDelta
(//zxx: 本文發布后補充:Delta讀音對應希臘字母△,形狀就像三角褲,因此,wheelDelta可以記做“滾輪的三角褲”):
根據自己的測試,在我的win7系統下,無論IE7, IE10, Opera12,或者是safari5.1,每次往下滾動event.wheelDelta
值都是-120
. //zxx:網上有說法說Safari值為-360
, 我對此表示懷疑,下圖為我的測試截圖。
對於FireFox瀏覽器(Opera瀏覽器也有),判斷鼠標滾動方向的屬性為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
→滾輪的三角褲,火狐是騷狐狸,沒有這個三角褲?欲知詳情,請看火狐?
……得,睡了,夢里再想吧~~