读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 相关文章推荐
js href的用法
May 13 Javascript
Extjs Gird 支持中文拼音排序实现代码
Apr 15 Javascript
js制作简易年历完整实例
Jan 28 Javascript
JSON格式的键盘编码对照表
Jan 29 Javascript
JS实现网页背景颜色与select框中颜色同时变化的方法
Feb 27 Javascript
jQuery+ajax实现无刷新级联菜单示例
May 21 Javascript
bootstrap提示标签、提示框实现代码
Dec 28 Javascript
使用Node.js实现RESTful API的示例
Aug 01 Javascript
vue2中使用sass并配置全局的sass样式变量的方法
Sep 04 Javascript
Vue 动态组件与 v-once 指令的实现
Feb 12 Javascript
layer关闭当前窗口页面以及确认取消按钮的方法
Sep 09 Javascript
微信小程序连接服务器展示MQTT数据信息的实现
Jul 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 session安全问题分析
2011/06/24 PHP
php增删改查示例自己写的demo
2013/09/04 PHP
thinkphp的CURD和查询方式介绍
2013/12/19 PHP
PHP检测移动设备类mobile detection使用实例
2014/04/14 PHP
PHP实现通过中文字符比率来判断垃圾评论的方法
2014/10/20 PHP
php中使用session_set_save_handler()函数把session保存到MySQL数据库实例
2014/11/06 PHP
PHP中set_include_path()函数相关用法分析
2016/07/18 PHP
javascript客户端遍历控件与获取父容器对象示例代码
2014/01/06 Javascript
JavaScript中的console.time()函数详细介绍
2014/12/29 Javascript
jQuery添加options点击事件并传值实例代码
2016/05/18 Javascript
jQuery基础_入门必看知识点
2016/07/04 Javascript
微信小程序 loading 详解及实例代码
2016/11/09 Javascript
Node.js dgram模块实现UDP通信示例代码
2017/09/26 Javascript
vue 2.0 购物车小球抛物线的示例代码
2018/02/01 Javascript
node跨域转发 express+http-proxy-middleware的使用
2018/05/31 Javascript
详解如何在你的Vue项目配置vux
2018/06/04 Javascript
详解Angularjs 自定义指令中的数据绑定
2018/07/19 Javascript
JS实现图片轮播效果实例详解【可自动和手动】
2019/04/04 Javascript
[02:22]2018DOTA2亚洲邀请赛VG赛前采访
2018/04/03 DOTA
[01:05:36]VP vs TNC Supermajor小组赛B组 BO3 第二场 6.2
2018/06/03 DOTA
python实现根据窗口标题调用窗口的方法
2015/03/13 Python
Python实现的数据结构与算法之链表详解
2015/04/22 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
2019/03/14 Python
Python函数装饰器原理与用法详解
2019/08/16 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
2020/02/07 Python
python字符串替换re.sub()实例解析
2020/02/09 Python
jupyter notebook插入本地图片的实现
2020/04/13 Python
python线程里哪种模块比较适合
2020/08/02 Python
python 调用Google翻译接口的方法
2020/12/09 Python
塔吉特百货公司官网:Target
2017/04/27 全球购物
LN-CC美国:伦敦时尚生活的缩影
2019/02/19 全球购物
Yankee Candle官网:美国最畅销蜡烛品牌之一
2020/01/05 全球购物
社会体育专业大学生职业生涯规划书
2014/09/17 职场文书
2015年电教工作总结
2015/05/26 职场文书
受欢迎的自荐信,就这么写!
2019/04/19 职场文书
Go语言基础知识点介绍
2021/07/04 Golang