解密jQuery事件核心 - 模拟事件(四)

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

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

焦点事件

blur :浏览器

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

focus:函数

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

 


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

focusin :代理

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

focusout :htm

在当前失去焦点前触发,可冒泡,同步触发。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. …………………
相关文章
相关标签/搜索