給 DOM 元素綁定事件分爲兩大類:在 html 中直接綁定 和 在 JavaScript 中綁定。javascript
在 HTML 中綁定事件叫作內聯綁定事件,HTML 的元素中有如 onclick
這樣的 on***
屬性,它能夠給這個 DOM 元素綁定一個類型的事件,主要是這樣的:html
<div onclick="***">CLICK ME</div> |
這裏的 ***
有兩種形式:java
<div onclick="var a = 1; console.log(a); console.log(this);">CLICK ME</div> |
點擊能夠發現,console:瀏覽器
1 |
var a = 1; console.log(a); console.log(this);
這一段字符串被看成 js 執行了,同時 this
指向當前這個點擊的元素。dom
<div onclick="foo(this)">CLICK ME</div> |
這裏須要添加 script
去定義函數:函數
<script> |
能夠觀察到,console:post
<div onclick="var a = 1; console.log(a); console.log(this);">CLICK ME</div> |
這裏的 this
指向了全局,傳入的參數是點擊的 DOM 元素,其實和第一種方式是同樣的,都是在 onclick=
後面指向了一段js的字符串,不一樣的是在這個字符串中是執行了一個函數名,而這個函數咱們在全局中定義了,因此點擊的時候能夠執行,而後傳入的參數 this
也就是同樣的道理了。ui
或者多說一句,這裏的字符串纔是真正賦值給 onclick
的函數,這裏咱們是在函數裏面再執行了 foo
函數。this
然而這內聯的方式綁定時間不利於分離,因此通常咱們不推薦這種作法,因此也就再也不多闡述了spa
先上栗子:
<div id="clickme">CLICK ME</div> |
document.getElementById('clickme').onclick = function (e) { |
觀察 console:
clickme |
首先,咱們獲取到了 dom
元素,而後給它的 onclick
屬性賦值了一個函數;
點擊 dom
咱們發現那個函數執行了,同時發現函數中的 this
是指向當前的這個 dom
元素;
細細一想,其實這和前面用的在 html
中 內聯 綁定函數是同樣的,咱們一樣是給 dom
的 onclick
屬性賦值一個函數,而後函數中的 this
指向當前元素,只是這個過程這裏咱們是在 js 中作的;
而還有一點區別就是前面的是賦值一段 js 字符串,這裏是賦值一個函數,因此能夠接受一個參數:event
,這個參數是點擊的事件對象。
用賦值綁定函數的一個缺點就是它只能綁定一次:
document.getElementById('clickme').onclick = function (e) { |
能夠看到這裏只打印了一次 clickme
。
這個纔是咱們須要重點介紹的一個函數
target.addEventListener(type, listener[, useCapture]); |
target
: 表示要監聽事件的目標對象,能夠是一個文檔上的元素 Document
自己,Window
或者 XMLHttpRequest
;type
: 表示事件類型的字符串,好比: click
、change
、touchstart
…;listener
: 當指定的事件類型發生時被通知到的一個對象。該參數必是實現 EventListener 接口的一個對象或函數。useCapture
: 設置事件的 捕獲或者冒泡 (後文闡述) ,true
: 捕獲,false
: 冒泡*,默認爲 false
。 <div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
觀察 console:
clickme |
監聽函數中的 this
指向當前的 dom
元素,函數接受一個 event
參數。
addEventListener
能夠給同一個 dom
元素綁定多個函數:
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
能夠看到 console:
111 |
咱們能夠看到兩個函數都執行了,而且執行順序按照綁定的順序執行。
改變一下,若是咱們的 useCapture
參數不一樣呢?
看下面 3 組對比:
var clickme = document.getElementById('clickme'); |
var clickme = document.getElementById('clickme'); |
var clickme = document.getElementById('clickme'); |
能夠看到,console 並無改變,因此執行順序只和綁定順序有關,和 useCapture
無關。
結論:
咱們能夠給一個 dom
元素綁定多個函數,而且它的執行順序按照綁定的順序執行。
咱們給一個 dom
元素綁定同一個函數兩次試試:
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
觀察 console:
111 |
能夠看到函數只執行了一次;
換一種方式:
var clickme = document.getElementById('clickme'); |
觀察 console:
111 |
能夠看到函數執行了兩次。
結論:
咱們能夠給一個 dom
元素綁定同一個函數,最多隻能綁定 useCapture
類型不一樣的兩次。
addEventListener
只支持到 IE 9,因此爲了兼容性考慮,在兼容 IE 8 以及如下瀏覽器能夠用 attachEvent
函數,和 addEventListener
函數表現同樣,除了它綁定函數的 this
會指向全局這個缺點之外
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
觀察 console:
111 |
可見執行順序只和綁定順序有關
與事件綁定相對應的就是事件解綁了。
對於 Bind in HTML 和 dom.onclick
綁定的事件均可以用 dom.onclick = null
來解綁事件。
經過 addEventListener
綁定的事件能夠使用 removeEventListener
來解綁, removeEventListener
接受的參數和 addEventListener
是同樣的
<div id="clickme">CLICK ME</div> |
var clickme = document.getElementById('clickme'); |
這裏發現事件並無取消綁定,發現 removeEventListener
的 useCapture
的參數原來和綁定時候傳入的不一致,咱們改爲 false
以後發現事件取消了。
結論:
對於使用 removeEventListener
函數解綁事件,須要傳入的 listener
useCapture
應和 addEventListener
一致才能夠解綁事件。
與 attachEvent
對應
<!DOCTYPE html> |
事件開始時由最具體的元素接受,而後逐級向上傳播到較爲不具體的節點。
好比上面的 HTML ,冒泡的順序: div3 -> div3 -> div1 -> body -> html -> document (-> window)
事件捕獲的思想是不太具體的DOM節點應該更早接收到事件,而最具體的節點應該最後接收到事件。與事件冒泡的順序相反。
好比上面的 HTML ,捕獲的順序: document -> html -> body -> div1 -> div2 -> div3
DOM事件流包括三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。首先發生的事件捕獲,爲截獲事件提供機會。而後是實際的目標接受事件。最後一個階段是時間冒泡階段,能夠在這個階段對事件作出響應。
咱們來作幾個小對比:
<div id="wrap"> |
|
console:
222 |
這裏兩個事件都是冒泡類型,因此是從內到外;
|
console:
111 |
這裏兩個事件都是捕獲類型,因此是從外到內;
|
console:
222 |
wrap 事件是冒泡,clickme 事件是捕獲,根據 dom 的事件流,先執行了捕獲階段(這裏是目標階段)再到冒泡階段。
|
console:
111 |
clickme 事件是冒泡,wrap 事件是捕獲,根據 dom 的事件流,先執行了捕獲階段(這裏是目標階段)再到冒泡階段。
<div id="wrap"> |
|