读jQuery之十四 (触发事件核心方法)


Posted in Javascript onAugust 23, 2011

在 事件模块的演变 我使用了dispatchEvent(标准) 和fireEvent(IE)来主动触发事件。如下

... 
dispatch = w3c ? 
function(el, type){ 
try{ 
var evt = document.createEvent('Event'); 
evt.initEvent(type,true,true); 
el.dispatchEvent(evt); 
}catch(e){alert(e)}; 
} : 
function(el, type){ 
try{ 
el.fireEvent('on'+type); 
}catch(e){alert(e)} 
}; 
...

jQuery则完全没有用到dispatchEvent/fireEvent方法。它采用的是另外一种机制。
jQuery触发事件的核心方法是jQuery.event.trigger。它提供给客户端程序员使用的触发事件方法有两个:.trigger/.triggerHandler
读jQuery之十四 (触发事件核心方法)
一个事件的发生在某些元素中可能会导致两种动作,一个是默认行为,一个是事件handler。如链接A
<a href="http://mail.sina.com.cn" onclick="alert(1);">新浪邮箱</a>
点击后,弹出1(事件handler),点确定跳转(默认行为)到了mail.sina.com.cn。因此,设计的触发事件的函数要考虑到这两种情况。
jQuery使用.trigger和.triggerHandler区分了这两种情况:
.trigger 执行事件hanlder/执行冒泡/执行默认行为
.triggerHandler 执行事件handler/不冒泡/不执行默认行为
.trigger/.triggerHandler的源码如下 
trigger: function( type, data ) { 
return this.each(function() { 
jQuery.event.trigger( type, data, this ); 
}); 
}, 
triggerHandler: function( type, data ) { 
if ( this[0] ) { 
return jQuery.event.trigger( type, data, this[0], true ); 
} 
},

可以看出,两者都调用jQuery.event.trigger。调用时一个没有传true,一个传了。传了true的triggerHander就表示仅执行事件handler。
此外还需注意一点区别:.trigger是对jQuery对象集合的操作,而.triggerHandler仅操作jQuery对象的第一个元素。如下
<p>p1</p> 
<p>p1</p> 
<p>p1</p> 
<script> 
$('p').click(function(){alert(1)}); 
$('p').trigger('click'); // 弹3次,即三个p的click都触发了 
$('p').triggerHandler('click'); // 仅弹1次,即只触发第一个p的click 
</script>

好了,是时候贴出jQuery.event.trigger的代码了
trigger: function( event, data, elem, onlyHandlers ) { 
// Event object or event type 
var type = event.type || event, 
namespaces = [], 
exclusive; 
...... 
}

这就是jQuery.event.trigger的定义,省略了大部分。下面一一列举
if ( type.indexOf("!") >= 0 ) { 
// Exclusive events trigger only for the exact event (no namespaces) 
type = type.slice(0, -1); 
exclusive = true; 
}

这一段是为了处理.trigger('click!')的情形,即触发非命名空间的事件。变量exclusive挂在事件对象上后在jQuery.event.handle内使用。举个例子
function fn1() { 
console.log(1) 
} 
function fn2() { 
console.log(2) 
} 
$(document).bind('click.a', fn1); 
$(document).bind('click', fn2); 
$(document).trigger('click!'); // 2

为document添加了两个点击事件,一个是具有命名空间的"click.a",一个则没有"click"。使用trigger时参数click后加个叹号"!"。从输出结果为2可以看出不触发命名空间的事件。总结一下:
.trigger('click') 触发所有的点击事件
.trigger('click.a') 仅触发“click.a” 的点击事件
.trigger('click!') 触发非命名空间的点击事件
接着看
if ( type.indexOf(".") >= 0 ) { 
// Namespaced trigger; create a regexp to match event type in handle() 
namespaces = type.split("."); 
type = namespaces.shift(); 
namespaces.sort(); 
}

这段就很好理解了,就是对.trigger('click.a')的处理,即对具有命名空间事件的处理。
接着看
if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { 
// No jQuery handlers for this event type, and it can't have inline handlers 
return; 
}

对于一些特殊事件如"getData"或对于已经触发过的事件直接返回。
往下
event = typeof event === "object" ? 
// jQuery.Event object 
event[ jQuery.expando ] ? event : 
// Object literal 
new jQuery.Event( type, event ) : 
// Just the event type (string) 
new jQuery.Event( type );

有三种情况
,event 本身就是jQuery.Event类的实例
,event是个普通js对象(非jQuery.Event类的实例)
,event是个字符串,如"click"

event.type = type;
event.exclusive = exclusive;
event.namespace = namespaces.join(".");
event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
需要注意exclusive/namespace/namespace_re挂到了event上了,在jQuery.event.handle中可以用到(事件命名空间)。
往下是
// triggerHandler() and global events don't bubble or run the default action 
if ( onlyHandlers || !elem ) { 
event.preventDefault(); 
event.stopPropagation(); 
}

onlyHandlers 只在 .triggerHandler用到了,即不触发元素的默认行为,且停止冒泡。
下面是
// Handle a global trigger 
if ( !elem ) { 
// TODO: Stop taunting the data cache; remove global events and always attach to document 
jQuery.each( jQuery.cache, function() { 
// internalKey variable is just used to make it easier to find 
// and potentially change this stuff later; currently it just 
// points to jQuery.expando 
var internalKey = jQuery.expando, 
internalCache = this[ internalKey ]; 
if ( internalCache && internalCache.events && internalCache.events[ type ] ) { 
jQuery.event.trigger( event, data, internalCache.handle.elem ); 
} 
}); 
return; 
}

这里是个递归调用。如果没有传elem元素,那么从jQuery.cache里取。
接着是
// Don't do events on text and comment nodes 
if ( elem.nodeType === 3 || elem.nodeType === 8 ) { 
return; 
}

属性,文本节点直接返回。
下面是
// Clone any incoming data and prepend the event, creating the handler arg list 
data = data != null ? jQuery.makeArray( data ) : []; 
data.unshift( event );

先将参数data放入数组,event对象放在数组的第一个位置。
接着是
// Fire event on the current element, then bubble up the DOM tree 
do { 
var handle = jQuery._data( cur, "handle" ); 
event.currentTarget = cur; 
if ( handle ) { 
handle.apply( cur, data ); 
} 
// Trigger an inline bound script 
if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { 
event.result = false; 
event.preventDefault(); 
} 
// Bubble up to document, then to window 
cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; 
} while ( cur && !event.isPropagationStopped() );

这段代码很重要,做了以下事情
,取handle
,执行
,执行通过onXXX方式添加的事件(如onclick="fun()")
,取父元素
while循环不断重复这四步以模拟事件冒泡。直到window对象。
接下是
// If nobody prevented the default action, do it now 
if ( !event.isDefaultPrevented() ) { 
var old, 
special = jQuery.event.special[ type ] || {}; 
if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && 
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { 
// Call a native DOM method on the target with the same name name as the event. 
// Can't use an .isFunction)() check here because IE6/7 fails that test. 
// IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. 
try { 
if ( ontype && elem[ type ] ) { 
// Don't re-trigger an onFOO event when we call its FOO() method 
old = elem[ ontype ]; 
if ( old ) { 
elem[ ontype ] = null; 
} 
jQuery.event.triggered = type; 
elem[ type ](); 
} 
} catch ( ieError ) {} 
if ( old ) { 
elem[ ontype ] = old; 
} 
jQuery.event.triggered = undefined; 
} 
}

这一段是对于浏览器默认行为的触发。如form.submit(),button.click()等。
注意,由于Firefox中链接的安全性限制,jQuery对链接的默认行为都统一为不能触发。即不能通过.trigger()使链接跳转。
Javascript 相关文章推荐
仿迅雷焦点广告效果(JQuery版)
Nov 19 Javascript
jQuery 前的按键判断代码
Mar 19 Javascript
JavaScript的Module模式编程深入分析
Aug 13 Javascript
JavaScript中instanceof与typeof运算符的用法及区别详细解析
Nov 19 Javascript
30个经典的jQuery代码开发技巧
Dec 15 Javascript
jquery实现多条件筛选特效代码分享
Aug 28 Javascript
Bootstrap滚动监听(Scrollspy)插件详解
Apr 26 Javascript
Bootstrap被封装的弹层
Jul 20 Javascript
easyui form validate总是返回false的原因及解决方法
Nov 07 Javascript
解析Vue 2.5的Diff算法
Nov 28 Javascript
三种Webpack打包方式(小结)
Sep 19 Javascript
javascript合并两个数组最简单的实现方法
Sep 14 Javascript
读jQuery之十三 添加事件和删除事件的核心方法
Aug 23 #Javascript
基于jquery实现的类似百度搜索的输入框自动完成功能
Aug 23 #Javascript
jquery 回车事件实现代码
Aug 23 #Javascript
基于jquery的大众点评,分类导航实现代码
Aug 23 #Javascript
20个非常棒的 jQuery 幻灯片插件和教程分享
Aug 23 #Javascript
基于jquery实现的鼠标拖拽元素复制并写入效果
Aug 23 #Javascript
一些有用的JavaScript和jQuery的片段分享
Aug 23 #Javascript
You might like
处理php自动反斜杠的函数代码
2010/01/05 PHP
php实现通用的从数据库表读取数据到数组的函数实例
2015/03/21 PHP
PHP实现的oracle分页函数实例
2016/01/25 PHP
利用PHP判断是否是连乘数字串的方法示例
2017/07/03 PHP
父窗口获取弹出子窗口文本框的值
2006/06/27 Javascript
js/jQuery对象互转(快速操作dom元素)
2013/02/04 Javascript
js禁止页面使用右键(简单示例代码)
2013/11/13 Javascript
javascript实现数字+字母验证码的简单实例
2014/02/10 Javascript
详细解密jsonp跨域请求
2015/04/15 Javascript
基于javascript简单实现对身份证校验
2021/01/25 Javascript
javascript的理解及经典案例分析
2016/05/20 Javascript
ashx文件获取$.ajax()方法发送的数据
2016/05/26 Javascript
js中的eval()函数把含有转义字符的字符串转换成Object对象的方法
2016/12/02 Javascript
canvas绘制环形进度条
2017/02/23 Javascript
jQuery插件Echarts实现的双轴图效果示例【附demo源码下载】
2017/03/04 Javascript
angular十大常见问题
2017/03/07 Javascript
JS实现页面打印功能
2017/03/16 Javascript
npm配置国内镜像资源+淘宝镜像的方法
2018/09/07 Javascript
JS多个表单数据提交下的serialize()应用实例分析
2019/08/27 Javascript
原生js实现随机点餐效果
2019/12/10 Javascript
[54:51]Ti4 冒泡赛第二轮LGD vs C9 3
2014/07/14 DOTA
linux系统使用python监测网络接口获取网络的输入输出
2014/01/15 Python
Python中为什么要用self探讨
2015/04/14 Python
Python面向对象class类属性及子类用法分析
2018/02/02 Python
Python3中的列表生成式、生成器与迭代器实例详解
2018/06/11 Python
Django 自定义分页器的实现代码
2019/11/24 Python
什么是python的函数体
2020/06/19 Python
django 将自带的数据库sqlite3改成mysql实例
2020/07/09 Python
HTML5-WebSocket实现聊天室示例
2016/12/15 HTML / CSS
Belvilla法国:休闲度假房屋出租
2020/10/03 全球购物
函授毕业自我鉴定
2013/12/19 职场文书
小学生推普周国旗下讲话稿
2014/09/21 职场文书
中职毕业生自我鉴定范文(3篇)
2014/09/28 职场文书
低端且暴利的线上线下创业项目分享
2019/09/03 职场文书
Python中生成随机数据安全性、多功能性、用途和速度方面进行比较
2022/04/14 Python
Nginx本地配置SSL访问的实例教程
2022/05/30 Servers