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

来源:互联网 发布:学而知不足,思而得远虑 编辑:程序博客网 时间:2024/05/24 06:16

一、学无止境、温故知新

//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)(可点击此页面单独查看表格内容):

属性名\浏览器FireFoxChromeIE10IE7recordset×没有该属性×没有该属性×没有该属性nulltypeDOMMouseScrollmousewheelmousewheelmousewheelfromElement×没有该属性nullnullnulltoElement×没有该属性[object HTMLDivElement]nullnullaltLeft×没有该属性×没有该属性×没有该属性falsekeyCode×没有该属性0×没有该属性0repeat×没有该属性×没有该属性×没有该属性falsereason×没有该属性×没有该属性×没有该属性0data×没有该属性×没有该属性×没有该属性空字符串behaviorCookie×没有该属性×没有该属性×没有该属性0source×没有该属性×没有该属性×没有该属性nullcontentOverflow×没有该属性×没有该属性×没有该属性falsebehaviorPart×没有该属性×没有该属性×没有该属性0url×没有该属性×没有该属性×没有该属性空字符串dataTransfer×没有该属性null×没有该属性nullctrlKeyfalsefalsefalsefalseshiftLeft×没有该属性×没有该属性×没有该属性falsedataFld×没有该属性×没有该属性×没有该属性空字符串returnValue×没有该属性true×没有该属性undefinedqualifier×没有该属性×没有该属性×没有该属性空字符串wheelDelta×没有该属性-120-120-120bookmarks×没有该属性×没有该属性×没有该属性nullactionURL×没有该属性×没有该属性×没有该属性空字符串button0000srcFilter×没有该属性×没有该属性×没有该属性nullnextPage×没有该属性×没有该属性×没有该属性空字符串cancelBubblefalsefalsefalsefalsex×没有该属性799876839y×没有该属性283322325buttonID×没有该属性×没有该属性×没有该属性0srcElement×没有该属性[object HTMLDivElement][object HTMLDivElement][object]screenX934799876841screenY453344377382srcUrn×没有该属性×没有该属性×没有该属性空字符串origin×没有该属性×没有该属性×没有该属性空字符串boundElements×没有该属性×没有该属性×没有该属性[object]clientX1168799876841clientY456283322327propertyName×没有该属性×没有该属性×没有该属性空字符串shiftKeyfalsefalsefalsefalsectrlLeft×没有该属性×没有该属性×没有该属性falseoffsetX×没有该属性791868829offsetY×没有该属性275314310altKeyfalsefalsefalsefalseinitMouseWheelEvent×没有该属性×没有该属性function initMouseWheelEvent() { [native code] }×没有该属性layerX1168799876×没有该属性layerY456283322×没有该属性which111×没有该属性buttons0×没有该属性0×没有该属性metaKeyfalsefalsefalse×没有该属性pageX1168799876×没有该属性pageY456283322×没有该属性relatedTargetnullnullnull×没有该属性getModifierStatefunction getModifierState() { [native code] }×没有该属性function getModifierState() { [native code] }×没有该属性initMouseEventfunction initMouseEvent() { [native code] }function initMouseEvent() { [native code] }function initMouseEvent() { [native code] }×没有该属性detail300×没有该属性view[object Window][object Window][object Window]×没有该属性initUIEventfunction initUIEvent() { [native code] }function initUIEvent() { [native code] }function initUIEvent() { [native code] }×没有该属性bubblestruetruetrue×没有该属性cancelabletruetruetrue×没有该属性currentTarget[object HTMLBodyElement][object HTMLBodyElement][object HTMLBodyElement]×没有该属性defaultPreventedfalsefalsefalse×没有该属性eventPhase333×没有该属性isTrustedtrue×没有该属性true×没有该属性target[object HTMLDivElement][object HTMLDivElement][object HTMLDivElement]×没有该属性timeStamp1429693713661062751771366106216522×没有该属性initEventfunction initEvent() { [native code] }function initEvent() { [native code] }function initEvent() { [native code] }×没有该属性preventDefaultfunction preventDefault() { [native code] }function preventDefault() { [native code] }function preventDefault() { [native code] }×没有该属性stopImmediate
Propagationfunction stopImmediate
Propagation() { [native code] }function stopImmediate
Propagation() { [native code] }function stopImmediate
Propagation() { [native code] }×没有该属性stopPropagationfunction stopPropagation() { [native code] }function stopPropagation() { [native code] }function stopPropagation() { [native code] }×没有该属性AT_TARGET222×没有该属性BUBBLING_PHASE333×没有该属性CAPTURING_PHASE111×没有该属性webkitDirection
InvertedFromDevice×没有该属性false×没有该属性×没有该属性wheelDeltaY×没有该属性-120×没有该属性×没有该属性wheelDeltaX×没有该属性0×没有该属性×没有该属性webkitMovementY×没有该属性0×没有该属性×没有该属性webkitMovementX×没有该属性0×没有该属性×没有该属性charCode×没有该属性0×没有该属性×没有该属性clipboardData×没有该属性undefined×没有该属性×没有该属性initWebKitWheelEvent×没有该属性function initWebKitWheelEvent() { [native code] }×没有该属性×没有该属性NONE00×没有该属性×没有该属性MOUSEDOWN11×没有该属性×没有该属性MOUSEUP22×没有该属性×没有该属性MOUSEOVER44×没有该属性×没有该属性MOUSEOUT88×没有该属性×没有该属性MOUSEMOVE1616×没有该属性×没有该属性MOUSEDRAG3232×没有该属性×没有该属性CLICK6464×没有该属性×没有该属性DBLCLICK128128×没有该属性×没有该属性KEYDOWN256256×没有该属性×没有该属性KEYUP512512×没有该属性×没有该属性KEYPRESS10241024×没有该属性×没有该属性DRAGDROP20482048×没有该属性×没有该属性FOCUS40964096×没有该属性×没有该属性BLUR81928192×没有该属性×没有该属性SELECT1638416384×没有该属性×没有该属性CHANGE3276832768×没有该属性×没有该属性rangeParent[object HTMLDivElement]×没有该属性×没有该属性×没有该属性rangeOffset0×没有该属性×没有该属性×没有该属性isCharfalse×没有该属性×没有该属性×没有该属性mozMovementX1168×没有该属性×没有该属性×没有该属性mozMovementY576×没有该属性×没有该属性×没有该属性mozPressure0×没有该属性×没有该属性×没有该属性mozInputSource1×没有该属性×没有该属性×没有该属性initNSMouseEventfunction initNSMouseEvent() { [native code] }×没有该属性×没有该属性×没有该属性axis2×没有该属性×没有该属性×没有该属性initMouseScrollEventfunction initMouseScrollEvent() { [native code] }×没有该属性×没有该属性×没有该属性originalTarget[object HTMLDivElement]×没有该属性×没有该属性×没有该属性explicitOriginalTarget[object HTMLDivElement]×没有该属性×没有该属性×没有该属性preventBubblefunction preventBubble() { [native code] }×没有该属性×没有该属性×没有该属性preventCapturefunction preventCapture() { [native code] }×没有该属性×没有该属性×没有该属性getPreventDefaultfunction getPreventDefault() { [native code] }×没有该属性×没有该属性×没有该属性RESET65536×没有该属性×没有该属性×没有该属性SUBMIT131072×没有该属性×没有该属性×没有该属性SCROLL262144×没有该属性×没有该属性×没有该属性LOAD524288×没有该属性×没有该属性×没有该属性UNLOAD1048576×没有该属性×没有该属性×没有该属性XFER_DONE2097152×没有该属性×没有该属性×没有该属性ABORT4194304×没有该属性×没有该属性×没有该属性ERROR8388608×没有该属性×没有该属性×没有该属性LOCATE16777216×没有该属性×没有该属性×没有该属性MOVE33554432×没有该属性×没有该属性×没有该属性RESIZE67108864×没有该属性×没有该属性×没有该属性FORWARD134217728×没有该属性×没有该属性×没有该属性HELP268435456×没有该属性×没有该属性×没有该属性BACK536870912×没有该属性×没有该属性×没有该属性TEXT1073741824×没有该属性×没有该属性×没有该属性ALT_MASK1×没有该属性×没有该属性×没有该属性CONTROL_MASK2×没有该属性×没有该属性×没有该属性SHIFT_MASK4×没有该属性×没有该属性×没有该属性META_MASK8×没有该属性×没有该属性×没有该属性SCROLL_PAGE_UP-32768×没有该属性×没有该属性×没有该属性SCROLL_PAGE_DOWN32768×没有该属性×没有该属性×没有该属性MOZ_SOURCE_UNKNOWN0×没有该属性×没有该属性×没有该属性MOZ_SOURCE_MOUSE1×没有该属性×没有该属性×没有该属性MOZ_SOURCE_PEN2×没有该属性×没有该属性×没有该属性MOZ_SOURCE_ERASER3×没有该属性×没有该属性×没有该属性MOZ_SOURCE_CURSOR4×没有该属性×没有该属性×没有该属性MOZ_SOURCE_TOUCH5×没有该属性×没有该属性×没有该属性MOZ_SOURCE_KEYBOARD6×没有该属性×没有该属性×没有该属性HORIZONTAL_AXIS1×没有该属性×没有该属性×没有该属性VERTICAL_AXIS2×没有该属性×没有该属性×没有该属性

对照表格内容,可以看到,鼠标滚动事件与点击事件有很多类似的地方。比方说兼容部分:event.type,event.screenX/event.screenYevent.clientX/event.clientYevent.altKeyevent.shiftKey,event.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.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

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

其中的滚轮相关交互就是使用的上面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→滚轮的三角裤,火狐是骚狐狸,没有这个三角裤?欲知详情,请看火狐?

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

0 0
原创粉丝点击