已经没有了小学生时代过目不忘的记忆力了,很多自己折腾的东西、接触的东西,短短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)(可​​点击此页面​​单独查看表格内容):

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可以记做“滚轮的三角裤”):

JS滚轮事件(mousewheel/DOMMouseScroll)了解_html

根据​​自己的测试​​,在我的win7系统下,无论IE7, IE10, Opera12,或者是safari5.1,每次往下滚动​​event.wheelDelta​​值都是​​-120​​. //zxx:网上有说法说Safari值为​​-360​​, 我对此表示怀疑,下图为我的测试截图。

JS滚轮事件(mousewheel/DOMMouseScroll)了解_html_02

对于FireFox浏览器(Opera浏览器也有),判断鼠标滚动方向的属性为​​event.detail​​, 向下滚动值为​​3​​.

JS滚轮事件(mousewheel/DOMMouseScroll)了解_safari_03

需要注意的是,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.mozHidden !== 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​

鼠标放在图片列表区域上,鼠标滚轮下滚滚,上滚滚,就可以看到图片列表们左右平滑移动的效果了。

JS滚轮事件(mousewheel/DOMMouseScroll)了解_ide_04

其中的滚轮相关交互就是使用的上面​​exports​​​暴露的​​addEvent​​方法。

相关代码实现如下,下面这个展示的就是平滑移动的核心代码们(代号为​​slide.js​​):

JS滚轮事件(mousewheel/DOMMouseScroll)了解_safari_05JS滚轮事件(mousewheel/DOMMouseScroll)了解_safari_06

/**  * 简易的列表左右滑动切换效果  * 鼠标事件是关键,因此,一些数值写死在方法中,纯测试用  */   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();             });         }     }; });

View Code



原理很简单,滚轮改变,索引改变,也就是列表的最终位置改变,动画到目标位置即可。

然后,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。于是,结语睡觉。

五、首尾呼应的结语

JS滚轮事件(mousewheel/DOMMouseScroll)了解_safari_07鼠标滚轮相关东西,我现在各个细节历历在目,要是现在发我张卷子,考鼠标滚轮事件知识,没有个90分我自己都不信。然而,目前为止,自己并未刻意去记忆,因此,如果接下来的1年时间自己很少或不接触相关内容。估计到时别人一问,小心脏一慌,说不定就一下子想不起"DOMMouseScroll"这厮了。因此,在结尾处,我要给自己来个特殊记忆。

怎么记呢?恩……啊,抓狂了,想不出来JS滚轮事件(mousewheel/DOMMouseScroll)了解_空字符串_08。​​​event.wheelDelta​​​/​​event.detail​

​wheelDelta​​​→滚轮的三角裤,火狐是骚狐狸,没有这个三角裤?欲知详情,请看火狐?JS滚轮事件(mousewheel/DOMMouseScroll)了解_空字符串_09

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