DOM是「Document Object Model」的縮寫,中文譯爲「文檔對象模型」。它是一種跨平臺、跨語言的編程接口,將HTML,XHTML,XML文檔映射成樹形結構,樹的每個節點都是一個對象。正因如此,面向對象的編程語言(如javascript)能夠經過DOM對HTML,XHTML,XML文檔進行操做。對於HTML文檔來講,它的根結點爲document對象,HTML元素爲element對象,HTML元素的屬性爲attr對象。javascript
在瀏覽網頁時,咱們經常須要頁面對用戶的操做做出響應,好比點擊「閱讀全文」後咱們指望頁面展現被摺疊的文本,按下回車鍵後瀏覽器提交已填好的表單。用戶的各類操做都是「事件」。事件都是在對象上發生的,多是DOM對象、BOM對象,等等。事件發生後,對象可能會做出響應,也有可能「無動於衷」。咱們但願DOM元素對事件做出響應,通常而言有兩種方法:
i.事件屬性
事件屬性是一種特殊的屬性,它的值規定了對應事件發生時須要執行的javascript腳本。例:html
<button onclick="console.log('button clicked!')"></button>
java
上面爲button標籤添加了事件屬性onclick,其值爲"console.log('button clicked!')",它規定了當元素被鼠標點擊時,控制檯輸出'button clicked'。
ii.addEventListener()方法
EventTarget.addEventListener()方法將指定的監聽器註冊到EventTarget上,當該對象觸發指定的事件時,指定的回調函數就會被執行。EventTarget能夠是element對象,document對象或者任何其餘支持事件的對象。例:編程
<button id='mybutton'></button>
//腳本中 var mybutton=document.getElementById('mybutton'); mybutton.addEventListener('click',function(e){console.log('button clicked!');});
上例爲button元素註冊了click事件的監聽器,並規定事件時觸發控制檯輸出'button clicked'。segmentfault
在講解DOM事件模型前,再用一個例子做爲引入。請看下面的html文件:瀏覽器
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>DOM Event Model</title> <style> div{position: absolute;} #outer{ top: 100px; left: 100px; width: 600px; height: 400px; background-color: #aff; } #inner1,#inner2{ top: 50px; width: 200px; height: 300px; background-color: #f9a; } #inner1{left: 50px;} #inner2{left: 350px;} #core{ left: 50px; top: 50px; width: 100px; height: 150px; background-color: #f50; } </style> </head> <body> <div id='outer' onclick="console.log(this.id)"> <div id='inner1' onclick="console.log(this.id)"></div> <div id='inner2' onclick="console.log(this.id)"> <div id='core' onclick="console.log(this.id)"></div> </div> </div> </body> </html>
編程語言
這裏爲id分別爲outer,inner1,inner2,core的4個元素定義了事件屬性,元素被點擊後將在控制檯輸出它的id。如今問題來了:
若是我點擊core元素,控制檯將會輸出什麼?
點擊core元素時,因爲core元素包含在inner2元素裏,inner2元素一樣被點擊了;同理,inner2元素包含在outer元素裏,那麼outer元素也被點擊了。這種狀況下哪個元素的click事件將會被觸發,或者說三者都被觸發?若是說三者都被觸發,那麼又是以怎樣的順序被觸發?
我在火狐瀏覽器作了一次實驗,控制檯輸出結果以下:函數
inner2 outer
也就是說,三者的事件都被觸發了,且是「由內向外」觸發的。
下面咱們再作一個有趣的實驗:咱們將上面的html文件再作一個小小的改動,將core元素的樣式this
left: 50px;
改成code
left: -250px;
此時觀察頁面咱們會發現,儘可能core是inner2的子節點,但因爲咱們定義了「怪異」的樣式,它跑到了inner1裏面。如今咱們再次用鼠標點擊core,觀察控制檯的輸出:
inner2 outer
和剛纔的結果如出一轍!儘管表面上inner1彷佛被點擊了,但它的click事件並無觸發;反而是看似未被點擊的inner2元素的click事件被觸發了。彷彿core元素的click事件被觸發後,click事件一層一層向上「傳播」給了父節點。
爲了解釋剛纔的實驗結果,是時候開始講解DOM事件模型了。
當一個事件發生時,事件會在DOM樹中進行傳播。傳播分爲兩個階段:
i.捕獲階段
在此階段,事件從根結點(即document結點)開始向下傳播,直到事件源所在元素。
ii.冒泡階段
在此階段,事件從事件源開始向上傳播,直到根結點。
拿剛纔的例子來講,事件傳播的順序爲:
document捕獲->html捕獲->body捕獲->outer捕獲->inner2捕獲->core捕獲->core冒泡->inner2冒泡->outer冒泡->html冒泡->document冒泡
對於事件屬性,默認在冒泡階段觸發事件。若是用addEventListener()方法註冊監聽器,則能夠指定在捕獲階段仍是冒泡階段觸發事件:若是最後一個參數爲false(默認值),則在冒泡階段觸發事件;若是爲true,則在捕獲階段觸發事件。
通常來講,咱們推薦採用addEventListener()方法來註冊監聽器,而儘可能不用事件屬性。由於事件屬性不利於行爲與結構的分離,使代碼難以維護。