這是我參與8月更文挑戰的第4天,活動詳情查看:8月更文挑戰html
EventTarget
接口概述DOM 的事件操做(監聽和觸發),都定義在EventTarget
接口。全部節點對象都部署了這個接口,其餘一些須要事件通訊的瀏覽器內置對象(好比,XMLHttpRequest
、AudioNode
、AudioContext
)也部署了這個接口。node
該接口主要提供三個實例方法。web
addEventListener
:綁定事件的監聽函數removeEventListener
:移除事件的監聽函數dispatchEvent
:觸發事件EventTarget.addEventListener()
用於在當前節點或對象上,定義一個特定事件的監聽函數。一旦這個事件發生,就會執行監聽函數。該方法沒有返回值。編程
target.addEventListener(type, listener[, useCapture]);
複製代碼
該方法接受三個參數。瀏覽器
type
:事件名稱,大小寫敏感。listener
:監聽函數。事件發生時,會調用該監聽函數。useCapture
:布爾值,表示監聽函數是否在捕獲階段(capture)觸發,默認爲false
(監聽函數只在冒泡階段被觸發)。該參數可選。下面是一個例子。markdown
function hello() {
console.log('Hello world');
}
var button = document.getElementById('btn');
button.addEventListener('click', hello, false);
複製代碼
上面代碼中,button
節點的addEventListener
方法綁定click
事件的監聽函數hello
,該函數只在冒泡階段觸發。函數
上面代碼中,addEventListener
方法的第二個參數,就是一個具備handleEvent
方法的對象。post
其次,第三個參數除了布爾值useCapture
,還能夠是一個屬性配置對象。該對象有如下屬性。ui
capture
:布爾值,表示該事件是否在捕獲階段
觸發監聽函數。once
:布爾值,表示監聽函數是否只觸發一次,而後就自動移除。passive
:布爾值,表示監聽函數不會調用事件的preventDefault
方法。若是監聽函數調用了,瀏覽器將忽略這個要求,並在監控臺輸出一行警告。
若是但願事件監聽函數只執行一次,能夠打開屬性配置對象的once
屬性。this
element.addEventListener('click', function (event) {
// 只執行一次的代碼 且 this 指向 element
console.log(this.nodeName); // "element"
}, {once: true});
複製代碼
addEventListener
方法能夠爲針對當前對象的同一個事件,添加多個不一樣的監聽函數。這些函數按照添加順序觸發,即先添加先觸發。若是爲同一個事件屢次添加同一個監聽函數,該函數只會執行一次,多餘的添加將自動被去除(沒必要使用removeEventListener
方法手動去除)。
上面代碼中,監聽函數內部的this
指向事件所在的對象element
。
EventTarget.removeEventListener
方法用來移除addEventListener
方法添加的事件監聽函數。該方法沒有返回值。
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
複製代碼
removeEventListener
方法的參數,與addEventListener
方法徹底一致。它的第一個參數「事件類型」,大小寫敏感。
注意,removeEventListener
方法移除的監聽函數,必須是addEventListener
方法添加的那個監聽函數,並且必須在同一個元素節點,不然無效。
EventTarget.dispatchEvent
方法在當前節點上觸發指定事件,從而觸發監聽函數的執行。該方法返回一個布爾值,只要有一個監聽函數調用了Event.preventDefault()
,則返回值爲false
,不然爲true
。
target.dispatchEvent(event)
複製代碼
dispatchEvent
方法的參數是一個Event
對象的實例。
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
複製代碼
上面代碼在當前節點觸發了click
事件。
若是dispatchEvent
方法的參數爲空,或者不是一個有效的事件對象,將報錯。
瀏覽器的事件模型,就是經過監聽函數(listener)對事件作出反應。事件發生後,瀏覽器監聽到了這個事件,就會執行對應的監聽函數。這是事件驅動編程模式(event-driven)的主要編程方式。
JavaScript 有三種方法,能夠爲事件綁定監聽函數。
HTML 語言容許在元素的屬性中,直接定義某些事件的監聽代碼。
<body onload="doSomething()">
<div onclick="console.log('觸發事件')">
複製代碼
上面代碼爲body
節點的load
事件、div
節點的click
事件,指定了監聽代碼。一旦事件發生,就會執行這段代碼。
元素的事件監聽屬性,都是on
加上事件名,好比onload
就是on + load
,表示load
事件的監聽代碼。
注意,這些屬性的值是將會執行的代碼,而不是一個函數。
<!-- 正確 -->
<body onload="doSomething()">
複製代碼
一旦指定的事件發生,on-
屬性的值是原樣傳入 JavaScript 引擎執行。所以若是要執行函數,不要忘記加上一對圓括號
。使用這個方法只會在冒泡階段觸發
。
直接設置on-
屬性,與經過元素節點的setAttribute
方法設置on-
屬性,效果是同樣的。
el.setAttribute('onclick', 'doSomething()');
// 等同於
// <Element onclick="doSomething()">
複製代碼
元素節點對象的事件屬性,一樣能夠指定監聽函數。
window.onload = doSomething;
div.onclick = function (event) {
console.log('觸發事件');
};
複製代碼
使用這個方法指定的監聽函數,也是隻會在冒泡階段觸發。
注意,這種方法與 HTML 的on-
屬性的差別是,它的值是函數名(doSomething
),而不像後者,必須給出完整的監聽代碼(doSomething()
)。
全部 DOM 節點實例都有addEventListener
方法,用來爲該節點定義事件的監聽函數。
window.addEventListener('load', doSomething, false);
複製代碼
addEventListener
方法的詳細介紹,參見上面EventTarget
接口。
上面三種方法,第一種「HTML 的 on- 屬性」,違反了 HTML 與 JavaScript 代碼相分離的原則,將二者寫在一塊兒,不利於代碼分工,所以不推薦使用。
第二種「元素節點的事件屬性」的缺點在於,同一個事件只能定義一個監聽函數,也就是說,若是定義兩次onclick
屬性,後一次定義會覆蓋前一次。所以,也不推薦使用。
第三種EventTarget.addEventListener
是推薦的指定監聽函數的方法。它有以下優勢:
window
、XMLHttpRequest
等)也有這個接口,它等因而整個 JavaScript 統一的監聽函數接口。監聽函數內部的this
指向觸發事件的那個元素節點。三種監聽函數的寫法,this
的指向均是如此。
// HTML 代碼以下 寫法一
// <button id="btn" onclick="console.log(this.id)">點擊</button> // btn
var btn = document.getElementById('btn');
// 寫法二
btn.onclick = function () {
console.log(this.id); // btn
};
// 寫法三
btn.addEventListener(
'click',
function (e) {
console.log(this.id); // btn
},
false
);
複製代碼
上面兩種寫法,點擊按鈕之後也是輸出btn
。
一個事件發生後,會在子元素和父元素之間傳播(propagation)。這種傳播分紅三個階段。
window
對象傳導到目標節點(上層傳到底層),稱爲「捕獲階段」(capture phase)。window
對象(從底層傳回上層),稱爲「冒泡階段」(bubbling phase)。這種三階段的傳播模型,使得同一個事件會在多個節點上觸發。
<div>
<p>點擊</p>
</div>
複製代碼
上面代碼中,<div>
節點之中有一個<p>
節點。
若是對這兩個節點,都設置click
事件的監聽函數(每一個節點的捕獲階段和冒泡階段,各設置一個監聽函數),共計設置四個監聽函數。而後,對<p>
點擊,click
事件會觸發四次。
var phases = {
1: 'capture',
2: 'target',
3: 'bubble'
};
var div = document.querySelector('div');
var p = document.querySelector('p');
div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);
function callback(event) {
var tag = event.currentTarget.tagName;
var phase = phases[event.eventPhase];
console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
}
// 點擊之後的結果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'
複製代碼
上面代碼表示,click
事件被觸發了四次:<div>
節點的捕獲階段和冒泡階段各1次,<p>
節點的目標階段觸發了2次。
<div>
向<p>
傳播時,觸發<div>
的click
事件;<div>
到達<p>
時,觸發<p>
的click
事件;<p>
傳回<div>
時,再次觸發<div>
的click
事件。其中,<p>
節點有兩個監聽函數(addEventListener
方法第三個參數的不一樣,會致使綁定兩個監聽函數),所以它們都會由於click
事件觸發一次。因此,<p>
會在target
階段有兩次輸出。
事件傳播的最上層對象是window
,接着依次是document
,html
(document.documentElement
)和body
(document.body
)。也就是說,上例的事件傳播順序,在捕獲階段依次爲window
、document
、html
、body
、div
、p
,在冒泡階段依次爲p
、div
、body
、html
、document
、window
。
因爲事件會在冒泡階段向上傳播到父節點,所以能夠把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。這種方法叫作事件的代理(delegation)。
var ul = document.querySelector('ul');
ul.addEventListener('click', function (event) {
if (event.target.tagName.toLowerCase() === 'li') {
// some code
}
});
複製代碼
上面代碼中,click
事件的監聽函數定義在<ul>
節點,可是實際上,它處理的是子節點<li>
的click
事件。這樣作的好處是,只要定義一個監聽函數,就能處理多個子節點的事件,而不用在每一個<li>
節點上定義監聽函數。並且之後再添加子節點,監聽函數依然有效。
若是但願事件到某個節點爲止,再也不傳播,可使用事件對象的stopPropagation
方法。
// 事件傳播到 p 元素後,就再也不向下傳播了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, true);
// 事件冒泡到 p 元素後,就再也不向上冒泡了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, false);
複製代碼
上面代碼中,stopPropagation
方法分別在捕獲階段和冒泡階段,阻止了事件的傳播。
可是,stopPropagation
方法只會阻止事件的傳播,不會阻止該事件觸發<p>
節點的其餘click
事件的監聽函數。也就是說,不是完全取消click
事件。
若是想要完全取消該事件,再也不觸發後面全部click
的監聽函數,可使用stopImmediatePropagation
方法。
p.addEventListener('click', function (event) {
event.stopImmediatePropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 不會被觸發
console.log(2);
});
複製代碼
上面代碼中,stopImmediatePropagation
方法能夠完全取消這個事件,使得後面綁定的全部click
監聽函數都再也不觸發。因此,只會輸出1,不會輸出2。