zoukankan      html  css  js  c++  java
  • JS滚轮事件(mousewheel/DOMMouseScroll)了解

    by zhangxinxu from http://www.zhangxinxu.com
    本文地址: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)(可点击此页面单独查看表格内容):

    属性名浏览器FireFoxChromeIE10IE7
    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可以记做“滚轮的三角裤”):
    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→滚轮的三角裤,火狐是骚狐狸,没有这个三角裤?欲知详情,请看火狐?

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

    原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
    本文地址:http://www.zhangxinxu.com/wordpress/?p=3175

  • 相关阅读:
    [转载]Axis2 and CXF的比较
    [转载]Axure RP 7.0下载地址及安装说明
    Eclipse配置总结
    201505大事记
    Gmail收不到邮件咋办?
    Mybatis-There is no getter for property named 'id' in 'class java.lang.String'
    EXTjs+SpringMVC+Mybatis实现照片的上传,下载,查看关键技术整理
    Ext如何Gird中如何显示时间类型的列?
    glibc
    圆形头像CircleImageView和Cardview使用
  • 原文地址:https://www.cnblogs.com/koleyang/p/4648700.html
Copyright © 2011-2022 走看看