读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 相关文章推荐
javascript 写类方式之二
Jul 05 Javascript
jQuery 获取对象 基本选择与层级
May 31 Javascript
jquery之empty()与remove()区别说明
Sep 10 Javascript
idTabs基于JQuery的根据URL参数选择Tab插件
Apr 11 Javascript
JQ实现新浪游戏首页幻灯片
Jul 29 Javascript
浅谈javascript中onbeforeunload与onunload事件
Dec 10 Javascript
理解javascript中Map代替循环
Feb 26 Javascript
bootstrap输入框组代码分享
Jun 07 Javascript
javascript深拷贝和浅拷贝详解
Feb 14 Javascript
原生js实现省市区三级联动代码分享
Feb 12 Javascript
实例详解vue.js浅度监听和深度监听及watch用法
Aug 16 Javascript
Vue中ref和$refs的介绍以及使用方法示例
Jan 11 Vue.js
读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
DC最新动画电影:《战争之子》为何内容偏激,毁了一个不错的漫画
2020/04/09 欧美动漫
第十二节--类的自动加载
2006/11/16 PHP
PHP初学者常见问题集合 修正版(21问答)
2010/03/23 PHP
php入门学习知识点一 PHP与MYSql连接与查询
2011/07/14 PHP
微信公众号开发之通过接口删除菜单
2017/02/20 PHP
ajax调用返回php接口返回json数据的方法(必看篇)
2017/05/05 PHP
kindeditor 加入七牛云上传的实例讲解
2017/11/12 PHP
通过DOM脚本去设置样式信息
2010/09/19 Javascript
jQuery中[attribute=value]选择器用法实例
2014/12/31 Javascript
分享javascript实现的冒泡排序代码并优化
2016/06/05 Javascript
jquery实现垂直和水平菜单导航栏
2020/08/27 Javascript
Node.js中路径处理模块path详解
2016/11/14 Javascript
JS调用打印机功能简单示例
2016/11/28 Javascript
jquery Form轻松实现文件上传
2017/05/24 jQuery
详解如何使用node.js的开发框架express创建一个web应用
2018/12/20 Javascript
JS判断数组里是否有重复元素的方法小结
2019/05/21 Javascript
详解vue 2.6 中 slot 的新用法
2019/07/09 Javascript
jQuery实现获取多选框的值示例
2020/02/07 jQuery
解决vue-photo-preview 异步图片放大失效的问题
2020/07/29 Javascript
python getopt详解及简单实例
2016/12/30 Python
Python实现动态加载模块、类、函数的方法分析
2017/07/18 Python
利用python解决mysql视图导入导出依赖的问题
2017/12/17 Python
Python实现的双色球生成功能示例
2017/12/18 Python
python matplotlib画图实例代码分享
2017/12/27 Python
Python字符串的全排列算法实例详解
2019/01/07 Python
顶级宝石首饰网络零售商:Angara
2016/10/25 全球购物
日本7net购物网:书籍、漫画、杂志、DVD、游戏邮购
2017/02/17 全球购物
美国最灵活的移动提供商:Tello
2017/07/18 全球购物
英国手工布艺沙发在线购买:Sofas & Stuff
2018/03/02 全球购物
人力资源经理的岗位职责范本
2014/02/28 职场文书
关于环保的演讲稿
2014/05/10 职场文书
公司周年庆典策划方案
2014/05/17 职场文书
2015初一年级组工作总结
2015/07/24 职场文书
互联网的下一个风口:新的独角兽将诞生
2019/08/02 职场文书
使用RedisTemplat实现简单的分布式锁
2021/11/20 Redis
解决Vmware虚拟机安装centos8报错“Section %Packages Does Not End With %End. Pane Is Dead”
2022/06/01 Servers