• 解密jQuery事件核心


    前几章已经把最核心的实现都分解过了,这一章我们看看jQuery是如何实现事件模拟的

    在Internet Explorer 8和更低,一些事件changesubmit本身不冒泡,但jQuery修改这些冒泡,创建一致的跨浏览器的行为。

    焦点事件

    blur :

    在这个事件触发前,元素已经失去焦点,不冒泡,同步触发。target 指向当前失去焦点的元素。

    focus:

    在这个事件触发前,元素已经得到焦点,不冒泡,同步触发。target 指向当前得到焦点的元素。


    与此同时DOM Level 3 事件模块 还定义了 focusin ,focusout 以及 DOMFocusIn ,DOMFocusOut 四个事件。

    focusin :

    在当前元素获得焦点前以及相关元素失去焦点前触发,可冒泡,同步触发。target 指向当前将要获得焦点的元素,relatedTarget 指向失去焦点的元素

    focusout :

    在当前失去焦点前触发,可冒泡,同步触发。target 指向当前将要失去焦点的元素,relatedTarget 指向将要失去焦点的元素。

    DOMFocusIn :

    在这个事件触发前,元素已经得到焦点,可冒泡,同步触发。target 指向当前得到焦点的元素。

    DOMFocusOut :

    在这个事件触发前,元素已经没有焦点,可冒泡,同步触发。target 指向当前失去焦点的元素


    事件的兼容性支持

    image


    1, 所有 IE 版本均支持focusin/focusout事件(注意:IE6/7/8中不支持el.addEventListener方法)。
    2, Opera 最强悍即支持attachEvent,又支持addEventListener。且这两种方式添加事件均支持focusin/focusout事件。
    3, Safari/Chrome  给人一个惊喜,虽然el.onfocusin方式不支持,但 addEventListener方式却支持。因此想让Safari/Chrome中支持focusin事件,只能使用addEventListener方式添加事件。
    4, Firefox 任何一种添加事件方式都不支持 focusout/focuso

    那么如何在所有的平台上都兼容focusin/focusout?


    jQuery.event.special方法

    这个方法在event.add,event.dispatch等几个事件的处理地方都会被调用到,jQuert.event.special 对象用于某些事件类型的特殊行为和属性

    换句话说就是某些事件不是大众化的的事件,不能一概处理,比如 load 事件拥有特殊的 noBubble 属性,可以防止该事件的冒泡而引发一些错误

    所以需要单独针对处理,但是如果都写成判断的形式,显然代码结构就不合理了,而且不方便提供给用户自定义扩展

    在webkit下的截图,特殊事件类型

    image

    大体上针对9种事件,不同情况下处理hack,我们具体分析下焦点事件兼容冒泡处理,处理大同小异


    jQuery.event 事件机制 focusin/ focusout 事件

    image

    针对focusin/ focusout 事件jQuery.event.special扩充2组处理机制,

    special.setup方法主要是来在Firefox中模拟focusin和focusout事件的,因为各大主流浏览器只有他不支持这两个事件。

    由于这两个方法支持事件冒泡,所以可以用来进行事件代理

    var attaches = 0,
        handler = function( event ) {
            jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
        };
    
    jQuery.event.special[ fix ] = {
        setup: function() {
            if ( attaches++ === 0 ) {
                document.addEventListener( orig, handler, true );
            }
        },
        teardown: function() {
            if ( --attaches === 0 ) {
                document.removeEventListener( orig, handler, true );
            }
        }
    };

    前面的分析我们就知道通过事件最终都是通过add方法绑定的,也就是addEventListener方法绑定的,但是在add方法之前会有一个过滤分支

    image

    以前看不懂代码,现在回过来恍然大悟了,原来这个方法是这样用的

    所以最终代码会跑到各种的Hack中了,

    可见对focusin/ focusout 的处理,没有用通用的方法,而且是直接用的special.setup中的绑定

    几个重点

    1 绑定的是focusin/ focusout 事件,内部确换成了focus/blur事件

    2 document.addEventListener( orig, handler, true );事件绑在document上,最后是true,用的捕获绑定

    3 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );方法


    为什么用捕获?

    因为火狐不支持focusin/ focusout事件,所以要找个所有浏览器都兼容类似事件,对了那就是focus/blur,

    但是focus/blur不能冒泡丫,怎么办?

    咱不是还有捕获吗?

    那么利用捕获怎么模拟出冒泡呢?


    jQuery.event.simulate方法

    jQuery.event.simulate = function( type, elem, event, bubble ) {
        // 重写事件
        var e = jQuery.extend(
            new jQuery.Event(),
            event,
            { type: type,
                isSimulated: true,
                originalEvent: {}
            }
        );
        // 如果要冒泡
        if ( bubble ) {
            // 利用jQuery.event.trigger模拟触发事件
            jQuery.event.trigger( e, null, elem );
        } else {
            // 否则利用jQuery.event.dispatch来执行处理
            jQuery.event.dispatch.call( elem, e );
        }
        // 如果需要阻止默认操作,则阻止
        if ( e.isDefaultPrevented() ) {
            event.preventDefault();
        }
    }

    可以看到focusin/ focusout 可冒泡事件实现原理是

    1 focusin 事件添加事件处理程序时,jQuery 会在 document 上会添加 handler 函数

    2 在事件捕获阶段监视特定元素的 focus/ blur 动作,捕获行为发生在 document 对象上,这样才能有效地实现所有元素都能可以冒泡的事件。

    3 程序监视到存在 focus/ blur 行为,就会触发绑定在 document 元素上的事件处理程序,该事件处理程序在内部调用 simulate 逻辑触发事件冒泡,以实现我们希望的可以冒泡事件。

    4 之后利用jQuery.event.trigger模拟触发事件,把从target-document的元素都过滤出来,分析每个节点上是否绑定了事件句柄,依次处理,按照一定的规范,比如是否有事件阻止之类的,这里就不再重复分析了jQuery.event.trigger  http://www.cnblogs.com/aaronjs/p/3452279.html


    总结

    咋一看其实原理都挺简单的, 但是jQuery为了实现兼容统一,可谓煞费苦心了,把事件冒泡与捕获都统一模拟了一遍

    1. jQuery为统一原生Event对象而封装的jQuery.Event类,封装了preventDefault,stopPropagation,stopImmediatePropagation原生接口,可以直接捕获到用户的行为
    2. 由核心组件 jQuery.cache 实现注册事件处理程序的存储,实际上绑定在 DOM元素上的事件处理程序只有一个,即 jQuery.cache[elem[expando]].handle 中存储的函数,该函数在内部调用 jQuery.event.dispatch(event) 实现对该DOM元素特定事件的缓存的访问,并依次执行这些事件处理程序。
    3. jQuery.event.add(elem, types, handler, data, selector) 方法用于给特定elem元素添加特定的事件 types([type.namespace, type.namespace, ...])的事件处理程序 handler, 通过第四个参数 data 增强执行当前 handler 事件处理程序时的 $event.data 属性,以提供更灵活的数据通讯,而第五个元素用于指定基于选择器的委托事件
    4. namespace 命名空间机制,namespace 机制可以对事件进行更为精细的控制,开发人员可以指定特定空间的事件,删除特定命名空间的事件,以及触发特定命名空间的事件。这使得对事件处理机制的功能更加健
    5. jQuert.event.special 对象用于某些事件类型的特殊行为和属性。比如 load 事件拥有特殊的 noBubble 属性,可以防止该事件的冒泡而引发一些错误。总的来说,有这样一些方法和属性:
    6. jQuery.event.simulate(type, elem, event, bubble)模拟事件并立刻触发方法,可用于在DOM元素 elem 上模拟自定义事件类型 type,参数 bubble用于指定该事件是否可冒泡,event 参数表示 jQuery 事件对象 $event。 模拟事件通过事件对象的isSimulated属性为 true 表示这是模拟事件。该方法内部调用 trigger() 逻辑 或 dispatch() 逻辑立刻触发该模拟事件。该方法主要用于修正浏览器事件的兼容性问题,比如模拟出可冒泡的 focusin/ focusout 事件,修正IE中 change 事件的不可冒泡问题,修正IE中 submit事件不可冒泡问题
    7. jQuery.event.dispatch(event) 方法在处理事件委托机制时,依赖委托节点在DOM树的深度安排优先级,委托的DOM节点层次越深,其执行优先级越高。而其对于stopPropagation的处理有些特殊,在事件委托情况下并不一定会调用绑定在该DOM元素上的该类型的所有事件处理程序,而依赖于委托的事件处理程序的执行结果,如果低层委托的事件处理程序声明了停止冒泡,那么高层委托的事件以及自身绑定事件就不会被执行,这拓展了 DOM 委托机制的功能。
    8. jQuery.event.trigger(event | type, data, elem, onlyHandlers) 方法提供开发人员以程序方式触发特定事件的接口,该方法的第一个参数可以是 $event/ event 对象 ,也可以是某个事件类型的字符串 type; 第二个参数 data 用于扩展该事件触发时事件处理程序的参数规模,用于传递一些必要的信息。 elem参数表示触发该事件的DOM元素;最后该方法在默认情况下,其事件会冒泡,并且在有默认动作的情况下执行默认行为,但是如果指定了 onlyHandlers 参数,该方法只会触发绑定在该DOM元素上的事件处理程序,而不会引发冒泡和默认动作,也不会触发特殊的 trigger 行为。
    9. …………………
  • 相关阅读:
    JAVA List对象与json串互相转换
    git错误提示:fatal: remote origin already exists.
    eclipse中js中ctrl+鼠标点击方法出现 the resource is not on the include path of a javaScript project
    JS根据日期获取这周的周一
    jquery基本操作
    redux+react-redux+示例的快速上手体验
    用 async/await 来处理异步
    Git使用详细教程
    SVN里直接把本地目录纳入管理
    修改npm安装的全局路径和配置环境变量的坑
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3481075.html
Copyright © 2020-2023  润新知