更新时间:2022-09-08 09:11:31
前几章已经把最核心的实现都分解过了,这一章我们看看jQuery是如何实现事件模拟的
在Internet Explorer 8和更低,一些事件change
和 submit
本身不冒泡,但jQuery修改这些冒泡,创建一致的跨浏览器的行为。
blur :
在这个事件触发前,元素已经失去焦点,不冒泡,同步触发。target 指向当前失去焦点的元素。
focus:
在这个事件触发前,元素已经得到焦点,不冒泡,同步触发。target 指向当前得到焦点的元素。
与此同时DOM Level 3 事件模块 还定义了 focusin ,focusout 以及 DOMFocusIn ,DOMFocusOut 四个事件。
focusin :
在当前元素获得焦点前以及相关元素失去焦点前触发,可冒泡,同步触发。target 指向当前将要获得焦点的元素,relatedTarget 指向失去焦点的元素
focusout :
在当前失去焦点前触发,可冒泡,同步触发。target 指向当前将要失去焦点的元素,relatedTarget 指向将要失去焦点的元素。
DOMFocusIn :
在这个事件触发前,元素已经得到焦点,可冒泡,同步触发。target 指向当前得到焦点的元素。
DOMFocusOut :
在这个事件触发前,元素已经没有焦点,可冒泡,同步触发。target 指向当前失去焦点的元素
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?
这个方法在event.add,event.dispatch等几个事件的处理地方都会被调用到,jQuert.event.special 对象用于某些事件类型的特殊行为和属性
换句话说就是某些事件不是大众化的的事件,不能一概处理,比如 load 事件拥有特殊的 noBubble 属性,可以防止该事件的冒泡而引发一些错误
所以需要单独针对处理,但是如果都写成判断的形式,显然代码结构就不合理了,而且不方便提供给用户自定义扩展
在webkit下的截图,特殊事件类型
大体上针对9种事件,不同情况下处理hack,我们具体分析下焦点事件兼容冒泡处理,处理大同小异
针对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方法之前会有一个过滤分支
以前看不懂代码,现在回过来恍然大悟了,原来这个方法是这样用的
所以最终代码会跑到各种的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 = 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为了实现兼容统一,可谓煞费苦心了,把事件冒泡与捕获都统一模拟了一遍