JavaScript event flow JavaScript event flow
JavaScript event flow
JS事件流最早要從IE和網景公司的瀏覽器大戰說起,IE提出的是冒泡流,而網景提出的是捕獲流,後來在W3C組織的統一之下,JS支援了冒泡流和捕獲流,但是目前低版本的IE瀏覽器還是隻能支援冒泡流(IE6,IE7,IE8均只支援冒泡流),所以為了能夠相容更多的瀏覽器,建議大家使用冒泡流。
JS事件流原理圖如下:
從圖中我們可以知道
1、一個完整的JS事件流是從window開始,最後回到window的一個過程
2、事件流被分為三個階段(1~5)捕獲過程、(5~6)目標過程、(6~10)冒泡過程
3、在冒泡過程中6比7早觸發,也就解釋了上面那題,為什麼btn1,會比content先觸發
然而在有些情況下JS的事件流不會根據上圖這個從捕獲過程到目標過程到冒泡過程這樣去推進的,
DOM Level | 捕獲事件 | 冒泡事件 |
---|---|---|
DOM Level 0 | 不支援 | 支援 |
DOM Level 2 | 支援 | 支援 |
DOM Level 3 | 支援 | 支援 |
從表中我們可以知道在DOM Level 0事件的時候是不支援捕獲事件的,那麼什麼是Level 0呢
四、what is DOM Level
這一章,我們就來探討什麼是DOM Level,先從DOM Level 0說起,以上面的例子為例,JS部分可以這樣改寫為:
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.onclick=function(){ alert("btn1_click"); } btn1.onclick=function(){ alert("btn1_click2"); } content.onclick=function(){ alert("content_click"); }
執行的結果是先彈出btn1_click2,然後彈出content_click,從這裡案例我們可以得出這幾點:
1、btn1_click這個事件沒有被觸發,所以在DOM Level 0中的只能一個元素只能繫結一個事件,並且繫結的是最後繫結的那個事件,這個有點像jquery中的html方法一樣,後面的會覆蓋掉前面的內容一樣
2、content_click的觸發也同時也證明了DOM Level 0不會阻止事件冒泡的發生
在DOM Level 0中要為事件解除繫結,我們可以這樣設定
btn1.onclick=null;
將click事件設定為null也就可以解除事件綁定了
接下來我們來說一說什麼是DOM Level 2,DOM Level 2支援事件的冒泡與捕獲,但是由於有些瀏覽器的不支援事件捕獲,所以為了更好的相容更多的瀏覽器,建議使用事件冒泡,除非是業務需要在某個事件觸發之前觸發某個事件,那麼就需要使用事件的捕獲。下面我們就來看看什麼是DOM Level 2,JS部分如下
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.addEventListener("click",function(){ alert("btn1"); },false); btn1.addEventListener("click",function(){ alert("btn2"); },false); content.addEventListener("click",function(){ alert("content"); },false);
執行的結果是:先彈出btn1,然後彈出btn2,最後彈出content ,執行的結果我們可以分析出來:
DOM Level 2可以在一個元素上面註冊一個事件的時候在註冊另外一個事件,也就是可以同時在一個元素上面存在多個事件
DOM Level 0與DOM Level 2的主要區別去了上面說的一點外還有以下的幾點:
1、DOM Level 2可以在一個元素上面註冊多個事件,但是DOM Level 0就不可以
2、DOM Level 0的相容性好,可以支援所有的瀏覽器,但是DOM Level 2中的addEventListener的這個方法在IE瀏覽器是不支援的,IE瀏覽器支援attachEvent事件,attachEvent事件支援兩個引數,第一個是事件型別,第二個是執行的函式,DOM Level 0不同於addEventListener,這個在使用的時候要加上on,例如:addEventListener的單擊事件是click,而attachEvent的單擊事件是onclick,由於IE只支援冒泡事件,所以沒有第三個引數
3、DOM Level 2在IE中的繫結事件是attachEvent,解除繫結是detachEvent,在標準的瀏覽器繫結事件是addEventListener,解除繫結是removeEventListener
所以為了相容IE瀏覽器和標準的瀏覽器,我們需要編寫通用的方法來處理,方法如下:
var EventUtil = { addHandler: function (element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function (element, type, handler) { if (element.removeEventListener()) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } };
方法來源於網上,如有錯誤請指出
這個通用的方法在使用的時候要注意的是handler不能在方法裡面直接寫,要在外部將方法封裝好,然後在通過呼叫外部這個封裝好的方法來實現,因為移除繫結事件的時候也要傳handler事件對應的方法,這個做的一個主要的原因是使用DOM Level 2可以註冊多個事件,但是移除的時候可以指定要移除元素上面的哪個事件,所以需要傳handler引數
講到這個我們就來拓展一下事件裡面的一些相關屬性
我們直接列印事件可以得到如下圖:
上圖中的重要屬性對應的介紹如下所示:
type
: 觸發事件的型別;bubbles
: 表名事件是否冒泡;cancelable
: 表名是否可以取消事件的預設行為;currentTarget
: 當前正在處理事件的元素;target
: 事件的目標元素;defaultPrevented
: 表名是否已經呼叫了preventDefault()方法;detail
: 與事件相關的細節資訊;eventPhase
: 表示事件處理的階段: 1,捕獲階段; 2,處於階段; 3,冒泡階段;trusted
: true表示該事件是瀏覽器生成的, false表示是開發人員通過JavaScript建立的;view
: 與事件關聯的抽象檢視, 相當於發生事件的window物件;preventDefault()
: 取消事件的預設行為;stopImmediatePropagation()
: 取消事件的進一步獲取或者冒泡, 同時阻止任何事件處理程式被呼叫;stopPropagation()
: 取消事件的進一步獲取或者冒泡;
這些屬性雖然在使用的時候比較少涉及,但是關鍵時候有時候還是需要用到的,這裡面有幾個屬性需要特別說明:
一、stopPropagation()與preventDefault()的區別
preventDefault()主要是用來阻止標籤的預設行為
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div style="width:200px;height:200px;background:lightblue" id="content"> <div onclick="alert(1)" style="width:100px;height:100px;background: lightyellow;" id="btn1"> </div> <a id="tag" target="_blank" href="http://www.baidu.com">qweqwe</a> </div> </body> <script type="text/javascript"> var tag=document.getElementById("tag"); tag.addEventListener("click",function(event){ event.preventDefault(); },false); </script> </html>
例如上面的那段程式碼使用preventDefault()這個方法就阻止了a標籤的開啟新視窗
stopPropagation()這個方法主要是用來阻止事件冒泡的,這個一般在一些特定業務中不需要冒泡的情況下可以使用,示例程式碼如下:
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.addEventListener("click",function(event){ alert("btn1"); event.stopPropagation(); },false); content.addEventListener("click",function(){ alert("content"); },false);
這一段程式碼就阻止了id="btn1"向上級id="content"上面冒泡,打印出來的結果是:彈窗彈出btn1
二、preventDeefault()和stopPropagation()在IE瀏覽器和標準瀏覽器上面使用的差異
在標準瀏覽器的使用方法如第一點所示,在IE瀏覽器上面是event事件是沒有preventDefault()這個屬性的,所以在IE上,我們需要設定的屬性是returnValue
window.event.returnValue=false
stopPropagation()在標準瀏覽器上面也是如第一點所示,在IE上面的用法如下:
event.cancelBubble=true
三、target與currentTarget
target這個屬性指向的是目標過程中的DOM物件,currentTarget這個指向的是當前的物件,具體內容跟this一樣,所以當this指向的是目標的時候,target與currentTarget相同,這個理解即可,在實際業務中這個知識點使用頻率較少
接下來我們就來說一說DOM Level 2,這一部分主要是一些複雜的互動事件,包括滑鼠響應事件,鍵盤響應事件,具體用法跟click事件類似,下面的這幾個屬性的對比是需要關注的。
clientX
,clientY
: 這兩個屬性表示滑鼠游標相對瀏覽器的水平和垂直座標;pageX
,pageY
: 這兩個屬性表示滑鼠游標相對文件的水平和垂直座標; IE8及更早版本不支援;screenX
,screenY
: 這兩個屬性表示滑鼠游標相對整個螢幕的水平和垂直座標;
五、HTML5新增的事件
在HTML5到來的時候新增加了一些事件,其中在這裡就一些關鍵的事件進行講解,洗完起到一個拋磚引玉的作用
1、 contextmenu事件
這個事件是當滑鼠右擊的時候觸發的,但是觸發這個屬性的時候預設的行為也會被觸發,所以需要通過preventDefault()方法來阻止
2、beforeunload事件
beforeunload在頁面解除安裝之前觸發, 該事件會彈出一個對話方塊, 詢問是否確定離開, 事件的returnValue屬性表示對話方塊顯示的文字內容;
3、hashchange事件
該事件當URL中的hash值改變時觸發, 通常用於Ajax應用中利用URL引數儲存導航資訊;這個在前端路由的製作中是非常有用得
除此之外還有很多的事件,但是可能在實際業務中使用得比較少,所以不在這裡說明
六、事件委託
不知道大家在平時的使用的時候有沒有遇到過這樣的一種情況,如果事件涉及到更新HTML節點或者新增HTML節點的時候,就會出現這樣的一種情況,新新增的節點無法繫結事件,更新的節點也是無法繫結事件,表現的行為是無法觸發事件
例如:
<ul id='myLink'> <li id='a'> apple </li> <li id='b'> banana </li> <li id='c'> orange </li> </ul>
如果在上面的ul中新增一個新的li標籤(<li id="d">four</li>),那麼新增加的就會無法觸發
var f = document.getElementById('myLink'); f.onclick = function(e) { console.log(e.target.innerHTML); };
轉自:wusifan的部落格
JS事件流最早要從IE和網景公司的瀏覽器大戰說起,IE提出的是冒泡流,而網景提出的是捕獲流,後來在W3C組織的統一之下,JS支援了冒泡流和捕獲流,但是目前低版本的IE瀏覽器還是隻能支援冒泡流(IE6,IE7,IE8均只支援冒泡流),所以為了能夠相容更多的瀏覽器,建議大家使用冒泡流。
JS事件流原理圖如下:
從圖中我們可以知道
1、一個完整的JS事件流是從window開始,最後回到window的一個過程
2、事件流被分為三個階段(1~5)捕獲過程、(5~6)目標過程、(6~10)冒泡過程
3、在冒泡過程中6比7早觸發,也就解釋了上面那題,為什麼btn1,會比content先觸發
然而在有些情況下JS的事件流不會根據上圖這個從捕獲過程到目標過程到冒泡過程這樣去推進的,
DOM Level | 捕獲事件 | 冒泡事件 |
---|---|---|
DOM Level 0 | 不支援 | 支援 |
DOM Level 2 | 支援 | 支援 |
DOM Level 3 | 支援 | 支援 |
從表中我們可以知道在DOM Level 0事件的時候是不支援捕獲事件的,那麼什麼是Level 0呢
四、what is DOM Level
這一章,我們就來探討什麼是DOM Level,先從DOM Level 0說起,以上面的例子為例,JS部分可以這樣改寫為:
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.onclick=function(){ alert("btn1_click"); } btn1.onclick=function(){ alert("btn1_click2"); } content.onclick=function(){ alert("content_click"); }
執行的結果是先彈出btn1_click2,然後彈出content_click,從這裡案例我們可以得出這幾點:
1、btn1_click這個事件沒有被觸發,所以在DOM Level 0中的只能一個元素只能繫結一個事件,並且繫結的是最後繫結的那個事件,這個有點像jquery中的html方法一樣,後面的會覆蓋掉前面的內容一樣
2、content_click的觸發也同時也證明了DOM Level 0不會阻止事件冒泡的發生
在DOM Level 0中要為事件解除繫結,我們可以這樣設定
btn1.onclick=null;
將click事件設定為null也就可以解除事件綁定了
接下來我們來說一說什麼是DOM Level 2,DOM Level 2支援事件的冒泡與捕獲,但是由於有些瀏覽器的不支援事件捕獲,所以為了更好的相容更多的瀏覽器,建議使用事件冒泡,除非是業務需要在某個事件觸發之前觸發某個事件,那麼就需要使用事件的捕獲。下面我們就來看看什麼是DOM Level 2,JS部分如下
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.addEventListener("click",function(){ alert("btn1"); },false); btn1.addEventListener("click",function(){ alert("btn2"); },false); content.addEventListener("click",function(){ alert("content"); },false);
執行的結果是:先彈出btn1,然後彈出btn2,最後彈出content ,執行的結果我們可以分析出來:
DOM Level 2可以在一個元素上面註冊一個事件的時候在註冊另外一個事件,也就是可以同時在一個元素上面存在多個事件
DOM Level 0與DOM Level 2的主要區別去了上面說的一點外還有以下的幾點:
1、DOM Level 2可以在一個元素上面註冊多個事件,但是DOM Level 0就不可以
2、DOM Level 0的相容性好,可以支援所有的瀏覽器,但是DOM Level 2中的addEventListener的這個方法在IE瀏覽器是不支援的,IE瀏覽器支援attachEvent事件,attachEvent事件支援兩個引數,第一個是事件型別,第二個是執行的函式,DOM Level 0不同於addEventListener,這個在使用的時候要加上on,例如:addEventListener的單擊事件是click,而attachEvent的單擊事件是onclick,由於IE只支援冒泡事件,所以沒有第三個引數
3、DOM Level 2在IE中的繫結事件是attachEvent,解除繫結是detachEvent,在標準的瀏覽器繫結事件是addEventListener,解除繫結是removeEventListener
所以為了相容IE瀏覽器和標準的瀏覽器,我們需要編寫通用的方法來處理,方法如下:
var EventUtil = { addHandler: function (element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function (element, type, handler) { if (element.removeEventListener()) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } };
方法來源於網上,如有錯誤請指出
這個通用的方法在使用的時候要注意的是handler不能在方法裡面直接寫,要在外部將方法封裝好,然後在通過呼叫外部這個封裝好的方法來實現,因為移除繫結事件的時候也要傳handler事件對應的方法,這個做的一個主要的原因是使用DOM Level 2可以註冊多個事件,但是移除的時候可以指定要移除元素上面的哪個事件,所以需要傳handler引數
講到這個我們就來拓展一下事件裡面的一些相關屬性
我們直接列印事件可以得到如下圖:
上圖中的重要屬性對應的介紹如下所示:
type
: 觸發事件的型別;bubbles
: 表名事件是否冒泡;cancelable
: 表名是否可以取消事件的預設行為;currentTarget
: 當前正在處理事件的元素;target
: 事件的目標元素;defaultPrevented
: 表名是否已經呼叫了preventDefault()方法;detail
: 與事件相關的細節資訊;eventPhase
: 表示事件處理的階段: 1,捕獲階段; 2,處於階段; 3,冒泡階段;trusted
: true表示該事件是瀏覽器生成的, false表示是開發人員通過JavaScript建立的;view
: 與事件關聯的抽象檢視, 相當於發生事件的window物件;preventDefault()
: 取消事件的預設行為;stopImmediatePropagation()
: 取消事件的進一步獲取或者冒泡, 同時阻止任何事件處理程式被呼叫;stopPropagation()
: 取消事件的進一步獲取或者冒泡;
這些屬性雖然在使用的時候比較少涉及,但是關鍵時候有時候還是需要用到的,這裡面有幾個屬性需要特別說明:
一、stopPropagation()與preventDefault()的區別
preventDefault()主要是用來阻止標籤的預設行為
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div style="width:200px;height:200px;background:lightblue" id="content"> <div onclick="alert(1)" style="width:100px;height:100px;background: lightyellow;" id="btn1"> </div> <a id="tag" target="_blank" href="http://www.baidu.com">qweqwe</a> </div> </body> <script type="text/javascript"> var tag=document.getElementById("tag"); tag.addEventListener("click",function(event){ event.preventDefault(); },false); </script> </html>
例如上面的那段程式碼使用preventDefault()這個方法就阻止了a標籤的開啟新視窗
stopPropagation()這個方法主要是用來阻止事件冒泡的,這個一般在一些特定業務中不需要冒泡的情況下可以使用,示例程式碼如下:
var btn1=document.getElementById("btn1"); var content=document.getElementById("content"); btn1.addEventListener("click",function(event){ alert("btn1"); event.stopPropagation(); },false); content.addEventListener("click",function(){ alert("content"); },false);
這一段程式碼就阻止了id="btn1"向上級id="content"上面冒泡,打印出來的結果是:彈窗彈出btn1
二、preventDeefault()和stopPropagation()在IE瀏覽器和標準瀏覽器上面使用的差異
在標準瀏覽器的使用方法如第一點所示,在IE瀏覽器上面是event事件是沒有preventDefault()這個屬性的,所以在IE上,我們需要設定的屬性是returnValue
window.event.returnValue=false
stopPropagation()在標準瀏覽器上面也是如第一點所示,在IE上面的用法如下:
event.cancelBubble=true
三、target與currentTarget
target這個屬性指向的是目標過程中的DOM物件,currentTarget這個指向的是當前的物件,具體內容跟this一樣,所以當this指向的是目標的時候,target與currentTarget相同,這個理解即可,在實際業務中這個知識點使用頻率較少
接下來我們就來說一說DOM Level 2,這一部分主要是一些複雜的互動事件,包括滑鼠響應事件,鍵盤響應事件,具體用法跟click事件類似,下面的這幾個屬性的對比是需要關注的。
clientX
,clientY
: 這兩個屬性表示滑鼠游標相對瀏覽器的水平和垂直座標;pageX
,pageY
: 這兩個屬性表示滑鼠游標相對文件的水平和垂直座標; IE8及更早版本不支援;screenX
,screenY
: 這兩個屬性表示滑鼠游標相對整個螢幕的水平和垂直座標;
五、HTML5新增的事件
在HTML5到來的時候新增加了一些事件,其中在這裡就一些關鍵的事件進行講解,洗完起到一個拋磚引玉的作用
1、 contextmenu事件
這個事件是當滑鼠右擊的時候觸發的,但是觸發這個屬性的時候預設的行為也會被觸發,所以需要通過preventDefault()方法來阻止
2、beforeunload事件
beforeunload在頁面解除安裝之前觸發, 該事件會彈出一個對話方塊, 詢問是否確定離開, 事件的returnValue屬性表示對話方塊顯示的文字內容;
3、hashchange事件
該事件當URL中的hash值改變時觸發, 通常用於Ajax應用中利用URL引數儲存導航資訊;這個在前端路由的製作中是非常有用得
除此之外還有很多的事件,但是可能在實際業務中使用得比較少,所以不在這裡說明
六、事件委託
不知道大家在平時的使用的時候有沒有遇到過這樣的一種情況,如果事件涉及到更新HTML節點或者新增HTML節點的時候,就會出現這樣的一種情況,新新增的節點無法繫結事件,更新的節點也是無法繫結事件,表現的行為是無法觸發事件
例如:
<ul id='myLink'> <li id='a'> apple </li> <li id='b'> banana </li> <li id='c'> orange </li> </ul>
如果在上面的ul中新增一個新的li標籤(<li id="d">four</li>),那麼新增加的就會無法觸發
var f = document.getElementById('myLink'); f.onclick = function(e) { console.log(e.target.innerHTML); };
轉自:wusifan的部落格