事件是我認爲前端最特別的地方,這是惟一其餘語言不同的地方,咱們經過它與頁面進行交互。html
事件流描述的是從頁面中接收事件的順序。IE和網景團隊提出流相反的事件流概念。IE事件流是事件冒泡流,而網景是事件捕獲流。前端
IE的事件流叫作事件冒泡,即事件開始由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點。web
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div> 點我點我 </div> </body> </html>
若是你單擊了div元素,那麼click事件會按照以下事件傳播瀏覽器
<div>
<body>
<html>
document
點擊元素會從最具體的元素向上傳播。全部現代瀏覽器都支持事件冒泡,可是具體實現仍是有些差異,IE5.5及更早版本的事件會跳過<html>元素。函數
網景公司提出的另外一個事件流叫作事件捕獲,事件捕獲的思想上不太具體的節點應該更早接收到事件,而最具體的元素應該最後接收到事件,事件捕獲的目的的事件到達預期目標以前捕獲它。以前的例子來講就是this
document
html
body
divspa
因爲老版的瀏覽器不支持事件捕獲,因此咱們通常使用的都是事件冒泡。code
DOM2級規定的事件流包含三個階段,事件捕獲階段、處於目標階段和事件冒泡階段。htm
事件就是用戶或瀏覽器自身執行的某種動做,click、load、mouseover,都是事件的名字。而響應某個事件的函數就叫作事件處理程序,click的事件處理程序就是onclick,load事件處理程序就是onload。對象
<input type="button" value="Click me" onclick="alert(clicked)" />
在HTML中指定事件處理程序有兩個缺點,一是存在時間差,若是頁面已經出現了HTML元素,可是js還沒加載完,這時候就會報錯。還有就是會形成js代碼與HTML代碼耦合。
經過js指定事件處理程序的傳統方式,就是將一個函數賦值給一個事件處理程序屬性。這種爲事件處理程序賦值的方法是在第四代web瀏覽器中出現的,如今瀏覽器還支持這種操做。
使用DOM0級方法指定的事件處理程序被認爲是元素的方法。所以,這時候的事件處理程序是在元素的做用域中運行;程序中的this指向當前元素。
var btn = document.getElementById('mybtn'); btn.onclick = function () { alert(this.id); //mybtn }
DOM0級添加的事件處理程序會在事件流的冒泡階段被處理。
也能夠經過刪除DOM0級方法指定的事件處理程序,只要像下面這樣將事件事件處理程序屬性設置爲null就能夠了。
將事件處理程序設置爲null以後,再單擊按鈕將不會有任何動做發生。
「DOM2級事件」定義了兩個方法,用於處理指定和刪除事件處理程序的操做:addEventListener()和 removeEventListener()。全部DOM節點都包含這兩個方法,而且它們都接收三個參數,要處理的事件名、事件處理函數、一個布爾值。最後一個參數若是是true,表示在捕獲階段處理程序,若是是false,表示在捕獲階段處理程序。
與DOM0級方法同樣,這裏的this指向是添加處理事件的元素中執行,使用DOM2級能夠爲元素添加多個事件處理程序,順序按添加事件的順序執行。經過addEventListener添加的事件處理程序只能經過removeEventListener移除,移除傳入的參數必須與添加時的參數相同,這就覺得添加的匿名函數沒法移除。
IE實現了兩個方法:attachEvent和detachEvent。這兩個方法,經過attachEvent添加的事件處理程序都會被添加到冒泡階段,注意attachEvent第一個參數是onclick,而非addEventListener方法中的click。在IE中使用attachEvent的this指向的是全局做用域。
var btn = document.getElementById('myBtn'); btn.attachEvent('onclick', function () { console.log(this === window); // true })
一樣的能夠添加多個事件,不過事件的執行順序是相反的。
咱們能夠封裝跨瀏覽器處理程序。
var EventUtil = { addHandler: function (ele, type, handler) { if (ele.addEventListener) { ele.addEventListener(type, handler, false) } else if (ele.attachEvent) { ele.attachEvent(`on${type}`, handler) } else { ele[`on${type}`] = handler } }, removeHandler: function (ele, type, handler) { if (ele.removeEventListener) { ele.removeEventListener(type, handler, false) } else if (ele.detachEvent) { ele.detachEvent(`on${type}`, handler) } else { ele[`on${type}`] = null } }, }
不管指定事件處理程序時用的什麼方法,都會傳入一個event對象。事件處理程序內部,對象this的值等於currentTarget的值,而target只包含事件的實際目標,若是直接將事件處理程序指定給了目標元素,this、currentTarget和target包含相同的值。
var bth = document.getElementById('myBth'); bth.onclick = function (e) { console.log(e.currentTarget === this); // true console.log(e.target === this); // true }
能夠經過e.preventDefault()阻止事件的默認行爲,cancelable爲true的事件才能夠阻止。
stopPropagation()用於當即中止事件在DOM中的傳播,取消進一步的事件捕獲或冒泡。
var btn = document.getElementById('myBtn'); btn.onclick = function (e) { e.stopPropagation(); } document.body.onclick = function (e) { //不執行 }
能夠用eventPhase來肯定事件處於哪一個事件流階段。
1:捕獲
2:事件目標對象
3:冒泡
只有在事件處理程序執行期間,event對象纔會存在,執行完成後,對象就會被銷燬。
在使用DOM0級方法添加事件處理程序時,event 對象作爲window對象的一個屬性存在。
var btn = document.getElementById('myBtn'); btn.onclick = function () { var event = window.event; console.log(event.type) } var btn = document.getElementById('myBtn'); btn.onclick = function () { var event = window.event; console.log(event.srcElement === this) //true //event.srcElement 至關於DOM中的target } var btn = document.getElementById('myBtn'); btn.attachEvent = function (event) { console.log(event.srcElement === this) //false //this是window }
returnValue屬性至關於preventDefault,cancelBubble屬性stopPropagation()方法類型,不過其只能阻止冒泡,由於低版本IE只支持冒泡。
var EventUtil = { addHandler: function (ele, type, handler) { }, getEvent: function (event) { return event ? event : window.event }, getTarget: function (event) { return event || event.srcElement }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault() } else { event.returnValue = false } }, stopPropagation: function (event) { if (event.stopPropagation) { event.stopPropagation() } else { event.cancelBubble = true } } }