jQuery源码阅读13-3:事件trigger流程

模拟触发的调用流程和之前基本一致: fn.trigger ==> jQuery.event.trigger ==> 缓存的jQ.Event队列

最近工作比较烦, 碰上这么个自私的领导算是我三生有幸 对我来说绝对算得上是修炼. 都说机会是给有准备的人 我看还得加个前提: 要有合适的舞台.
我认栽, 但并不代表我会接受你们只会糊弄的习惯. 我的代码是给我自己写的 我不糊弄别人 更不糊弄自己. 我都25了 我可不想一辈子都只写这种垃圾代码.
想到<中国合伙人>里的一句台词(电影虽烂 但是这句台词我印象挺深)大概意思是: 没必要和有些人生气 她这样对你是因为她会永远留在这刷盘子 而你不一样, 你会飞的很远.

一: 触发的几种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//1.绑定俩事件
function handler1(event,arg1,arg2){
alert("h1==>"+$(this).text()+"args==>"+arg1+"__"+arg2 );
}
function handler2(event,arg1,arg2){
alert("h2==>"+$(this).text()+"args==>"+arg1+"__"+arg2 );
}
$("p").on("click.rt", handler1);
$("p").on("click", handler2);
//2.触发
//全部click
function trigger_all(){
$("p").trigger("click");
}
//指定ns的事件
function trigger_ns(){
$("p").trigger("click.rt");
}
//指定事件, 且不带ns的
function trigger_NO_ns(){
$("p").trigger("click!");
}
//指定事件, 带传参
function trigger_args(){
$("p").trigger("click",["ruantao","duapp.com"],["ruantao111","duapp.com"]);
}

二:fn上的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 博文作者:ruantao1989{@}gmail.com
* 引自博客:ruantao.duapp.com/blog
*/
jQuery.fn.extend({
//第一步
//==>源码3597行
trigger: function( type, data ) {
return this.each(function() {
//对选择器选出来的东西逐个触发
jQuery.event.trigger( type, data, this );
});
},
//仅触发this[0]上的事件, 且浏览器默认动作将不会被触发
triggerHandler: function( type, data ) {
if ( this[0] ) {
//仅触发this[0]
return jQuery.event.trigger( type, data, this[0], true );
}
},

三: jQuery.event实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
jQuery.event = {
//第二步
//==>源码2836行
trigger: function( event, data, elem, onlyHandlers ) {
//没元素,或元素的nodeType不对
if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
return;
}
var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
//jQ.Event实例有event.type, 否则就用普通event事件名
type = event.type || event,
namespaces = [];
//rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, 针对focusin/focusout事件 + focus/blur
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
}
//传入的事件名带叹号,仅处理不在任何命名空间中的事件
if ( type.indexOf( "!" ) >= 0 ) {
type = type.slice(0, -1);
//不执行命名空间
exclusive = true;
}
//拆出命名空间
if ( type.indexOf( "." ) >= 0 ) {
//拆分命名空间和事件类型["click", "rt"]
namespaces = type.split(".");
//弹出"click" 还剩"rt"
type = namespaces.shift();
//按字母排序
namespaces.sort();
}
//没有dom元素, 或"getData","setData","changeData"这三个事件, 且jQuery.event.global标记没注册过的 不作处理
//jQuery.event.global是add时候添加的标志位, 标记某种事件是否有处理队列
if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
return;
}
//event转成jQ.Event对象
event = typeof event === "object" ?
//Event实例在构造时会this[ jQuery.expando ] = true,
event[ jQuery.expando ] ? event :
//不知道什么情况会没有这个值, 多传一个参数会在内部extend合并
new jQuery.Event( type, event ) :
//传来字串,构造一个jQ.Enevt实例
new jQuery.Event( type );
//原生事件类型
event.type = type;
//写一个标志isTrigger = true
event.isTrigger = true;
//是否不执行ns
event.exclusive = exclusive;
//支持多级ns
event.namespace = namespaces.join( "." );
//组装一个ns的正则, 没ns就用null
event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
//事件类型不带冒号,就组装成onXXX, 带冒号就是""
ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
//没指定具体元素的, 就遍历缓存上所有的此类型事件 逐个触发
if ( !elem ) {
cache = jQuery.cache;
for ( i in cache ) {
if ( cache[ i ].events && cache[ i ].events[ type ] ) {
//取得有events队列, 且事件类型一致的, 触发
jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
}
}
return;
}
//event.result是dispatch()里用来记录执行结果的, 这里清掉
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}
//创建data数组, 把enevt则插到数组头
data = data != null ? jQuery.makeArray( data ) : [];
data.unshift( event );
//是否需要event.special特殊处理
special = jQuery.event.special[ type ] || {};
//特殊处理中有trigger则调用
if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
//[[元素, 优先用special.bindType而后type]]
eventPath = [[ elem, special.bindType || type ]];
//非triggerHandler(), 且special没有noBubble, 且elem不是window
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
//冒泡类型, 优先用special.delegateType, 而后type
bubbleType = special.delegateType || type;
//若是focusin/out, cur就是当前elem, 否则是父元素
cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
//这个for比较有意思:
//当前元素是old, cur迭代继续找父级, old再改成cur
//for的判断结束是cur.parentNode, 就是一级一级往上找, 直到找到document, 再往上就没了.
for ( old = elem; cur; cur = cur.parentNode ) {
eventPath.push([ cur, bubbleType ]);
old = cur;
}
//最后追溯到#document时, 还需要在最后再补一个window
//这样整个eventPath数组存储了整个事件冒泡的过程
if ( old === (elem.ownerDocument || document) ) {
eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
}
}
//触发eventPath中的事件,模拟冒泡触发的过程
for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
//事件所在的dom对象
cur = eventPath[i][0];
//事件类型
event.type = eventPath[i][1];
//cache里有事件处理队列, 且绑定过jQuery.event.dispatch.apply
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
//data里存的是event实例的数组, dispatch里触发
handle.apply( cur, data );
}
//不清楚为什么这又触发一次 onXXX, 如果返回false则组织默认行为
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
}
event.type = type;
//非triggerHandler()且不阻止默认
//event.isDefaultPrevented()必须手动指定,event实例.stopPropagation, 否则都是fasle
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
//一大堆兼容性判断
if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
//查了下, <a>的click因为安全性问题不让跳转
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
//元素上的onXXX事件
old = elem[ ontype ];
if ( old ) {
//如果有先置空
elem[ ontype ] = null;
}
//把triggered记录去掉
jQuery.event.triggered = type;
elem[ type ]();
jQuery.event.triggered = undefined;
//还原elem[ ontype ]
if ( old ) {
elem[ ontype ] = old;
}
}
}
}
return event.result;
},
customEvent: {
"getData": true,
"setData": true,
"changeData": true
},