js事件監聽機制(事件捕獲)

在前端開發過程當中咱們常常會遇到給頁面元素添加事件的問題,添加事件的js方法也不少,有直接加到頁面結構上的,有使用一些js事件監聽的方法,因爲各個瀏覽器對事件冒泡事件監聽的機制不一樣,le瀏覽器只有事件冒泡,沒有事件監聽的機制,對於事件監聽的兼容性問題是最大的難題:javascript

1.直接把事件的方法寫在頁面結構上html

?
1
2
3
4
function eventfun(){
//console.log(this);
}
<input type= "button" onclick= "eventfun()" value= "button" /> //這裏涉及到一個this做用域的問題,eventfun再這裏是一個全局函數, 對象是[object Window],this指向的是window.

要解決this做用域的問題,可使用爲全局函數添加event變量的方法,在頁面結構上將this對象做爲參數傳遞到函數內部使用前端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<input type= "button" onclick= "eventfun2(this)" value= "button2" />
function eventfun2(eve){ //在這裏把事件對象做爲參數傳遞到全局方法裏
eve.name= "alex" ;
window.name= "robin" ;
console.log( this ); //[object Window]
console.log(eve); // [object HTMLInputElement]
console.log( this .name); // robin
console.log(eve.name); // alexvar
self=eve;
console.log( this .name); //robin
console.log(self.name); //alex
alert(window.name);
alert(self.name);
}

2. 使用給事件屬性賦值的方法,是一種爲事件綁定的方法,可是這種方法的侷限性就是隻能爲事件綁定一個方法,若是綁定多個就會之後一個方法爲準java

HTMLElementobject.onclick = fucntion(){//使用這種爲事件屬性賦值的方法,this的指針會指向window對象,而不是被事件對象,因此這種方法是引用 web

?
1
2
3
4
5
6
7
8
9
10
11
//js code
fun1();
fun2();
fun3();
console.log( this ); //window.object
}
function dosomething(){
//js code
}
HTMLElementobject.onclick = dosomething; //使用這種爲事件對象屬性賦值的形式,this指針指向事件執行對象
console.log( this ); //htmlElementObject

3.事件傳播——冒泡與捕獲
DOM事件標準定義了兩種事件流,這兩種事件流有着顯著的不一樣而且可能對你的應用有着至關大的影響。這兩種事件流分別是捕獲和冒泡。和許多Web技 術同樣,在它們成爲標準以前,Netscape和微軟各自不一樣地實現了它們。Netscape選擇實現了捕獲事件流,微軟則實現了冒泡事件流。幸運的 是,W3C決定組合使用這兩種方法,而且大多數新瀏覽器都遵循這兩種事件流方式。
默認狀況下,事件使用冒泡事件流,不使用捕獲事件流。然而,在Firefox和Safari裏,你能夠顯式的指定使用捕獲事件流,方法是在註冊事件時傳入useCapture參數,將這個參數設爲true。
冒泡事件流
當事件在某一DOM元素被觸發時,例如用戶在客戶名字節點上點擊鼠標,事件將跟隨着該節點繼承自的各個父節點冒泡穿過整個的DOM節點層次,直到它 遇到依附有該事件類型處理器的節點,此時,該事件是onclick事件。在冒泡過程當中的任什麼時候候均可以終止事件的冒泡,在聽從W3C標準的瀏覽器裏能夠通 過調用事件對象上的stopPropagation()方法,在Internet Explorer裏能夠經過設置事件對象的cancelBubble屬性爲true。若是不中止事件的傳播,事件將一直經過DOM冒泡直至到達文檔根。
捕獲事件流
事件的處理將從DOM層次的根開始,而不是從觸發事件的目標元素開始,事件被從目標元素的全部祖先元素依次往下傳遞。在這個過程當中,事件會被從文檔 根到事件目標元素之間各個繼承派生的元素所捕獲,若是事件監聽器在被註冊時設置了useCapture屬性爲true,那麼它們能夠被分派給這期間的任何 元素以對事件作出處理;不然,事件會被接着傳遞給派生元素路徑上的下一元素,直至目標元素。事件到達目標元素後,它會接着經過DOM節點再進行冒泡。
現代事件綁定方法
針對如上節課所討論的,使用傳統事件綁定有許多缺陷,好比不能在一個對象的相同事件上註冊多個事件處理函數。而瀏覽器和W3C也並不是沒有考慮到這一點,所以在現代瀏覽器中,它們有本身的方法綁定事件。
W3C DOM
obj.addEventListener(evtype,fn,useCapture)——W3C提供的添加事件處理函數的方法。obj是要添 加事件的對象,evtype是事件類型,不帶on前綴,fn是事件處理函數,若是useCapture是true,則事件處理函數在捕獲階段被執行,不然 在冒泡階段執行
obj.removeEventListener(evtype,fn,useCapture)——W3C提供的刪除事件處理函數的方法
微軟IE方法
obj.attachEvent(evtype,fn)——IE提供的添加事件處理函數的方法。obj是要添加事件的對象,evtype是事件類型,帶on前綴,fn是事件處理函數,IE不支持事件捕獲
obj.detachEvent(evtype,fn,)——IE提供的刪除事件處理函數的方法,evtype包含on前綴瀏覽器

整合二者的方法app

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function addEvent(obj,evtype,fn,useCapture) {
if (obj.addEventListener) {
obj.addEventListener(evtype,fn,useCapture);
} else {
obj.attachEvent( "on" +evtype,fn); //IE不支持事件捕獲
} else {
obj[ "on" +evtype]=fn; //事實上這種狀況不會存在
}
}
function delEvent(obj,evtype,fn,useCapture) {
if (obj.removeEventListener) {
obj.removeEventListener(evtype,fn,useCapture);
} else {
obj.detachEvent( "on" +evtype,fn);
} else {
obj[ "on" +evtype]= null ;
}
}

IE的attach方法有個問題,就是使用attachEvent時在事件處理函數內部,this指向了window,而不是obj!固然,這個是有解決方案的!dom

但IE的attachEvent方法有另一個問題,同一個函數能夠被註冊到同一個對象同一個事件上屢次,解決方法:拋棄IE的 attachEvent方法吧!IE下的attachEvent方法不支持捕獲,和傳統事件註冊沒多大區別(除了能綁定多個事件處理函數),而且IE的 attachEvent方法存在內存泄漏問題!
addEvent,delEvent現代版函數

?
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
<html xmlns= "http://www.w3.org/1999/xhtml" >
<head>
<meta http-equiv= "Content-Type" content= "text/html; charset=utf-8" />
<title>js事件監聽</title>
<style>
table td{font:12px; border-bottom:1px solid #efefef;}
</style>
</head>
 
<body>
<div id= "outEle" style= "padding:10px; border:1px solid #b2b2b2; background:#efefef;" >
<input type= "button" onclick= "eventfun()" id= "button" value= "button" /><br />
<input type= "button" onclick= "eventfun2(this);" id= "button2" value= "button2" /><br />
<input type= "button" id= "button3" value= "button3" /><br />
<input type= "button" id= "button4" value= "button4" /><br />
<table id= "htmlEleTable" width= "100%" border= "0" style= "border:1px solid #b2b2b2; background:#fff;" >
<tr id= "1111" ><td>111111111111111111111111111111</td></tr>
<tr id= "22222" ><td>222222222222222222222222222222</td></tr>
<tr id= "33333" ><td>333333333333333333333333333333</td></tr>
<tr id= "4444" ><td>444444444444444444444444444444</td></tr>
<tr id= "55555" ><td>555555555555555555555555555555</td></tr>
</table>
</div>
<script language= "javascript" type= "text/javascript" >
function eventfun(){ //1.直接把js方法寫在頁面結構上
console.log( this ); //這裏涉及到一個this做用域的問題,eventfun再這裏是一個全局函數, 對象是window,this指向的是window
alert( this );
}
function eventfun2(eve){ //在這裏把事件對象做爲參數傳遞到全局方法裏
eve.name= "alex" ; //
window.name= "robin" ;
console.log( this ); //[object Window]
console.log(eve); // [object HTMLInputElement]
console.log( this .name); // robin
console.log(eve.name); // alex
var self=eve;
console.log( this .name); //robin
console.log(self.name); //alex
alert(window.name);
alert(self.name);
}
function eventfun3(){ //1.直接把js方法寫在頁面結構上
console.log( this ); //這裏涉及到一個this做用域的問題,eventfun再這裏是一個全局函數, 對象是window,this指向的是window
console.log( this .id);
alert( this );
alert( this .id);
//var outEleObj = EventUtil.$("outEle");
//removeEvent(outEleObj,"click",eventfun3);
}
/*
var EventUtil = {};
EventUtil.$ = function(id){
return document.getElementById(id);
}
EventUtil.openmes = eventfun3;
EventUtil.addEventHandle = function(eventTarget,eventtype,eventHandle){//定義事件監聽的對象元素,事件類型,事件函數
if(eventTarget.attachEvent){
eventTarget.attachEvent("on"+eventtype,eventHandle);
}else if(eventTarget.addEventListener){
eventTarget.addEventListener(eventtype,eventHandle,false)
}else{
eventTarget["on" + eventtype] = null;
}
 
};
EventUtil.deleEventHandle = function(eventTarget,eventtype,eventHandle){//定義事件監聽的對象元素,事件類型,事件函數
if(eventTarget.detachEvent){
alert("on"+eventtype);
alert("on"+eventHandle);
eventTarget.detachEvent("on"+eventtype,eventHandle);
}else if(eventTarget.removeEventListener){
eventTarget.removeEventListener(eventtype,eventHandle,false)
}else{
eventTarget["on" + eventtype] = null;
}
 
};*/
var EventUtil={
$: function (id){
return document.getElementById(id);
},
but4fun: function (){
console.log( this );
this .addEventHandle();
},
eventfun3: function (){
console.log( this );
alert( this );
delEvent(obj,evtype,fn,useCapture);
}
}
/***使用addEventListener,attachEvent進行dom事件的監聽
function addEvent(obj,evtype,fn,useCapture){
if (obj.addEventListener) {
obj.addEventListener(evtype,fn,useCapture);
}else if(obj.attachEvent){
obj.attachEvent("on"+evtype,function () {
fn.call(obj);
});
}else {
obj["on"+evtype]=fn;//事實上這種狀況不會存在
}
}
function delEvent(obj,evtype,fn,useCapture) {
if (obj.removeEventListener) {
obj.removeEventListener(evtype,fn,useCapture);
} else if(obj.detachEvent){
obj.detachEvent("on"+evtype,fn);
} else {
obj["on"+evtype]=null;
}
}
 
 
 
function addEvent(obj,evtype,fn,useCapture) {
if (obj.addEventListener) {//優先考慮W3C事件註冊方案
obj.addEventListener(evtype,fn,!!useCapture);
} else {//當不支持addEventListener時(IE),因爲IE同時也不支持捕獲,因此不如使用傳統事件綁定
if (!fn.__EventID) {fn.__EventID = addEvent.__EventHandlesCounter++;}
//爲每一個事件處理函數分配一個惟一的ID
 
if (!obj.__EventHandles) {obj.__EventHandles={};}
//__EventHandles屬性用來保存全部事件處理函數的引用
 
//按事件類型分類
if (!obj.__EventHandles[evtype]) {//第一次註冊某事件時
obj.__EventHandles[evtype]={};
if (obj["on"+evtype]) {//之前曾用傳統方式註冊過事件處理函數
(obj.__EventHandles[evtype][0]=obj["on"+evtype]).__EventID=0;//添加到預留的0位
//而且給原來的事件處理函數增長一個ID
}
obj["on"+evtype]=addEvent.execEventHandles;
//當事件發生時,execEventHandles遍歷表obj.__EventHandles[evtype]並執行其中的函數
}
}
}
 
addEvent.__EventHandlesCounter=1;//計數器,0位預留它用
addEvent.execEventHandles = function (evt) {//遍歷全部的事件處理函數並執行
if (!this.__EventHandles) {return true;}
evt = evt || window.event;
var fns = this.__EventHandles[evt.type];
for (var i in fns) {
fns[i].call(this);
}
};
/*
function delEvent(obj,evtype,fn,useCapture) {
if (obj.removeEventListener) {//先使用W3C的方法移除事件處理函數
obj.removeEventListener(evtype,fn,!!useCapture);
} else {
if (obj.__EventHandles) {
var fns = obj.__EventHandles[evtype];
if (fns) {delete fns[fn.__EventID];}
}
}
}
 
function fixEvent(evt) {//fixEvent函數不是單獨執行的,它必須有一個事件對象參數,並且只有事件發生時它才被執行!最好的方法是把它整合到addEvent函數的execEventHandles裏面
if (!evt.target) {
evt.target = evt.srcElement;
evt.preventDefault = fixEvent.preventDefault;
evt.stopPropagation = fixEvent.stopPropagation;
if (evt.type == "mouseover") {
evt.relatedTarget = evt.fromElement;
} else if (evt.type =="mouseout") {
evt.relatedTarget = evt.toElement;
}
evt.charCode = (evt.type=="keypress")?evt.keyCode:0;
evt.eventPhase = 2;//IE僅工做在冒泡階段
evt.timeStamp = (new Date()).getTime();//僅將其設爲當前時間
}
return evt;
}
fixEvent.preventDefault =function () {
this.returnValue = false;//這裏的this指向了某個事件對象,而不是fixEvent
};
fixEvent.stopPropagation =function () {
this.cancelBubble = true;
};*/
//console.log(EventUtil.$("button3"));//返回EventUtil函數的對象屬性
//EventUtil.$("button3").onclick= eventfun;//2.使用爲對象事件屬性賦值的方法來實現事件的監聽
//EventUtil.$("button3").onclick= eventfun2;//爲事件屬性添加多個方法時,爲後者
//EventUtil.$("button3").onclick= eventfun;//事件捕獲是從事件對象逐層外父級檢察一直到window對象
var EventUtil = function (){
function getByid(id){
return document.getElementById(id);
};
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini
 
 
function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false );
} else {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++;
// create a hash table of event types for the element
if (!element.events) element.events = {};
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element[ "on" + type]) {
handlers[0] = element[ "on" + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element[ "on" + type] = handleEvent;
}
};
// a counter used to create unique IDs
addEvent.guid = 1;
 
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false );
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
};
 
function handleEvent(event) {
var returnValue = true ;
// grab the event object (IE uses a global event object)
event = event || fixEvent((( this .ownerDocument || this .document || this ).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this .events[event.type];
// execute each event handler
for ( var i in handlers) {
this .$$handleEvent = handlers[i];
if ( this .$$handleEvent(event) === false ) {
returnValue = false ;
}
}
return returnValue;
};
 
function fixEvent(event) {
// add W3C standard event methods
event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function () {
this .returnValue = false ;
};
fixEvent.stopPropagation = function () {
this .cancelBubble = true ;
};
function tableAddEvent(){
 
};
 
return {
add:addEvent,
remove:removeEvent,
$:getByid
}
}();
 
var outEleObj = EventUtil.$( "outEle" );
//addEvent.apply(EventUtil,[outEleObj,"click",eventfun3]);
//EventUtil.add(outEleObj,"click",eventfun3);
var inputObj = EventUtil.$( "button4" );
var tableEle = EventUtil.$( "htmlEleTable" );
var tabTrEle = tableEle.getElementsByTagName( "tr" );
EventUtil.add(tableEle, "click" ,eventfun3);
for (i=0; i<tabTrEle.length; i++){
EventUtil.add(tabTrEle[i], "click" ,eventfun3);
}
EventUtil.remove(tableEle, "click" ,eventfun3); //事件冒刪除方法
EventUtil.add(tableEle, "click" ,eventfun3); //事件冒泡添加方法
//EventUtil.add(inputObj,"click",eventfun3);
//EventUtil.remove(outEleObj,"click",eventfun3);
//console.log(addEvent);
//addEvent(inputObj,"click",eventfun3,true);
//delEvent(outEleObj,"click",eventfun3,false);
</script>
</body>
</html>
相關文章
相關標籤/搜索