JS學習筆記——事件
1.事件流
當我們點選網頁上的某一個元素時,觸發的動作不僅僅跟這個元素有關,還跟包含這個元素的所有上層容器有關。觸發事件流的流動有一個先後順序,目前有兩種:1.IE所提出的事件冒泡;2.Netscape所提出的事件捕獲。
DOM事件流則是綜合了前面兩種事件流,先捕獲再冒泡。現代瀏覽器大多支援DOM事件流。
2.事件處理程式
為元素繫結相應的事件處理程式有3種方法:HTML、DOM0級、DOM2級。事件處理程式以”on”開頭,如”click”事件的處理程式是”onclick()”。
2.1 HTML事件處理
我們把事件處理程式當做html的元素屬性加入html標籤中。這種方法的最大缺點就是html和js緊耦合。
<input type="button" value="button" onclick="alert(`Click Me`)" />
<input type="button" value="button" onclick="test()" />
<script>
function test() {
//...
}
</script>
2.2 DOM0級事件處理
通過DOM操作獲取元素,元素都帶有事件處理(比如onclik()
)的屬性,我們把函式賦值給該屬性即可。在事件處理程式中,this指向給呼叫該事件處理程式的元素(在事件委託中,this就指向那個儘量高層次的元素)。若想刪除事件處理程式,將該屬性賦值null即可。DOM0級繫結的事件處理程式會發生在冒泡階段。
var p = document.getElementById('p');
p.onclick = function() {
alert(this.id);//p
}
p.onclick = null;//刪除繫結的事件處理程式
2.3 DOM2級事件處理
DOM2級事件處理程式的功能比DOM0級更加豐富。DOM2級可以一個事件新增多個處理程式,還可以指定程式在冒泡階段呼叫還是捕獲階段呼叫。DOM2級要刪除處理程式,需要傳入新增函式時的那個函式引數,這就說明,新增的匿名函式無法刪除。
var p = document.getElementById('p');
p.addEventListener("click" , function(){
alert(this.id);//p
}, false);//第三個引數 true:捕獲階段呼叫;false:冒泡階段呼叫;
//該移除函式沒有起作用
p.removeEventListener("click", function(){
alert(this.id);
}, false);
2.4 IE事件處理
雖然IE9也支援DOM2級事件處理程式(DOM事件流),但在IE9以前,是隻支援IE事件處理(IE事件流)
的。IE事件流是事件冒泡。IE事件處理程式與DOM2級事件處理程式非常相似。IE事件處理只支援兩個引數,第三個引數無法選擇捕獲還是冒泡。在IE8以及更早版本中,只支援冒泡。與DOM2級不同的是,這裡的this指向全域性window。
這裡我們注意,並不是所有IE瀏覽器都是IE事件處理(IE事件流)
,像IE9就支援DOM事件流
,支援DOM事件流,也支援DOM2級事件處理程式。
var p = document.getElementById('p');
p.attachEvent("onclick", function(){//注意這裡是"onclick",而不是DOM2級的"click"
alert(this === window);//true
});
//與DOM2級一樣,匿名函式無法被刪除
p.detachEvent("onclick", function(){
alert(this === window);//true
});
3.DOM事件物件
事件物件分為DOM事件物件和IE事件物件,IE就是個奇葩的存在,除了IE之外,其他瀏覽器都是DOM事件物件。
事件處理程式會接收一個事件物件,該事件物件中有許多跟事件有關的屬性,比如type、churentTarget、target等。
下面我們區分一下三個容易混淆的屬性this
、target
、currentTarget
。this始終等於currentTarget,這兩個又等於呼叫處理程式的元素。target等於動作真實發生的物件。
<body>
<div id='div'>
<p id='p'>hello!</p>
</div>
</body>
//div是p的父元素,如果點選p,event.target就是p
//如果點選div其他地方(不是p),event.target就是div
div.detachEvent("onclick", function(event){
//event.type == "click"
alert(this === div);//true
alert(event.currentTarget === div);//true
alert(event.target === p);//true
});
4.事件代理(事件委託)
如果我們有許多的元素需要繫結事件處理程式,按照上面的方法:先使用DOM獲取元素,再給元素繫結函式。這種方法會使得web效能下降:DOM操作耗時;函式的本質是物件,耗記憶體。
這種場景的解決方法就是使用事件代理,即利用事件冒泡,給儘量高層次的元素繫結一個事件,在這個事件中,根據事件物件的屬性來做相應的處理。
div.onclick = function(event) {
switch(event.target.id) {
case 'p':
alert('p');
break;
case 'div':
alert('div');
break;
}
}