1. 事件 在瀏覽器用戶端套用平台,一般專業皆以事件驅動的,即某個事件發生,然後做出對應的動作。 瀏覽器的事件所表示的是某些事情所發生的訊號。事件的闡述不是本文的重點,尚未了解的朋友,可以訪問W3school教程 進行了解,這將有助於更好地理解以下的內容 。 2.冒泡機制 為冒泡呢? 下面這個圖片大家應該心領神會吧,氣泡從水底開始往上升,由深到淺,升到最上面。在上升的過程中,氣泡會經過不同深度的水。 #相對應地:這個氣泡相當於我們這裡的事件,而水則相當於我們整個的dom樹;事件從dom 樹的底層層層往上傳遞,直至傳遞到dom的根節點。 簡單案例分析 下面透過一個簡單的範例案例來闡述冒泡原則: 定義一個html, 裡面有三個簡單的dom元素:div1,div2, span,div2包含span,div1 包含div2;而它們都在body 下: #? ##? ##? ##? ##?##1 2 34567 "body"> ### ######### ################## ######This is a span.< ;/span>############ ################## ###### ################################## 介面原型如下: a. body加入click 事件監聽,當body擷取到event事件時,列印出事件發生的時間和觸發事件的節點資訊: ##? ##123#45##6 #7 8 <div class="line number8 index7 alt1">"text/javascript"</div></td>><td class="code"> <div class="container"> <div class="line number1 index0 alt2"> <code class="js plain"> </code><code class="js string">#window.onload = </code><code class="js plain">function</code>##() {</div> <div class="line number2 index1 alt1"> <code class="js spaces"> </code> <code class="js plain"></code>document.getElementById(<code class="js keyword"></code>"body"<code class="js plain"></code>).addEventListener(</div> <div class="line number3 index2 alt2">"click"<code class="js spaces"></code>#,eventHandler) ;<code class="js plain"></code><code class="js string"></code> <code class="js plain"></code>}<code class="js string"></code><code class="js plain"></code># </div> <div class="line number4 index3 alt1">function<code class="js spaces"> </code>eventHandler(event) { <code class="js plain"></code> </div> <div class="line number5 index4 alt2"> <code class="js spaces"></code>console.log(<code class="js keyword"></code>#"時間:"<code class="js plain"></code>+</div> <div class="line number6 index5 alt1">new<code class="js spaces"> </code>Date(event.timeStamp)+<code class="js plain"></code>" 產生事件的節點:"<code class="js string"> </code>+ event.target.id +<code class="js plain"></code>" 目前節點:"<code class="js keyword"></code>+event.currentTarget.id);<code class="js plain"></code><code class="js string"></code> <code class="js plain"></code>#}<code class="js string"></code><code class="js plain"></code> 當我們依序點選"This is span",div2,div1,body後,輸出下列資訊: #分析以上的結果: 無論是body,body 或 body的子元素div1,或是div2,以及當這些元素被點擊click時,都會產生click事件,並且body都會捕獲到,然後呼叫對應的事件處理函數。就像水中的氣泡從底往上冒一樣,事件也會往上傳遞。 事件傳遞的示意圖如下: ## 發生的時間+事件發生的地點+ 事件的類型+事件的當前處理者+其他信息, 完整的html代碼如下: #? 1 2##3 #45678910# 11121314#15#16#17##18 192021222324252627282930313233343536373839 40414243444546#47 # "UTF-8"> #<div class="line number5 index4 alt2">"text/javascript"<code class="js plain"> </code>src=<code class="js string"></code>"js/jquery-1.11.0.js?1.1. 11"<code class="js plain"></code>>#Insert title here "text/css"> .box1 { 邊框:綠色40 像素實線; # 寬度:300 像素; # 高度:300px; 邊距:自動; #} ## ##.box2 {# 邊框:黃色40 像素實線; # 寬度:220像素; # 高:220像素; ## 邊距:自動;} # #span { 位置:相對; # 左:50px;# 上:50px; # ##} # # ##<腳本類型="text/javascript"> window.onload = function () {# document.getElementById( "body").addEventListener("click" ,eventHandler); } function #eventHandler (event) { ## console.log( "時間:"##+ new Date(事件.timeStamp)+ " 產生事件的節點:" + event.target.id + " 目前節點:"+event. currentTarget.id); } ######### ########## ############ "box2"#class="box2"##> ; "span">這是一個跨度。 ## # ## b.終止事件的冒泡 我們現在想實現這樣的功能,在div1 點擊的時候,彈出」你好,我是最外層div。 由此我們會有下面的javascript片段: ? 1 2 3 4 5##67#8 910111213 ##<code class="js plain">"text/javascript"</code><code class="js string">><code class="js plain"></code> </div> <div class="line number2 index1 alt1"> <code class="js spaces">window.onload = </code><code class="js plain">function</code><code class="js keyword">() {</code><code class="js plain"></code> </div> <div class="line number3 index2 alt2"> <code class="js spaces">document.getElementById(</code><code class="js plain">"box1"</code><code class="js string">).addEventListener(</code><code class="js plain">"click"</code><code class="js string">,</code><code class="js plain">#function</code><code class="js keyword">#(event){</code><code class="js plain"></code> </div> <div class="line number4 index3 alt1"> <code class="js spaces">alert(</code><code class="js plain">"您好,我是最外層div。"</code><code class="js string">);</code><code class="js plain"></code> </div> <div class="line number5 index4 alt2"> <code class="js spaces"></code><code class="js plain"></code> </div>});<div class="line number6 index5 alt1"> <code class="js spaces"></code><code class="js plain"> </code><code class="js string">document.getElementById(</code><code class="js plain">"box2"</code><code class="js string">).addEventListener(</code> <code class="js plain">"click"</code><code class="js keyword">,</code><code class="js plain">function</code> </div>(event){<div class="line number7 index6 alt2"> <code class="js spaces"></code><code class="js plain"> </code><code class="js string">alert( </code><code class="js plain">"您好,我是第二層div。"</code> </div>);<div class="line number8 index7 alt1"> <code class="js spaces"></code><code class="js plain"># </code>##});</div> <div class="line number9 index8 alt2"> <code class="js spaces"></code> <code class="js plain"></code>document.getElementById(<code class="js string"></code>"span"<code class="js plain"></code>).addEventListener(<code class="js string"></code>"click"<code class="js plain"> </code>,<code class="js keyword"></code>function<code class="js plain"></code>(event){</div> <div class="line number10 index9 alt1"> <code class="js spaces"></code># <code class="js plain"></code>alert(<code class="js string"></code>"你好,我是span。 <code class="js plain">}</code> </div> <div class="line number11 index10 alt2"> <code class="js spaces"> 預期上述程式碼會點擊span 的時候,會出來一個彈出框"您好,我是span。" 是的,確實彈出了這樣的對話框: 然而,以該對話框,點選確定後,依序彈出下列對話方塊: ##這顯然不是我們想要的! 我們希望的是點誰顯示誰的資訊而已。為什麼會出現上述的情況呢? 原因就在於事件的冒泡,點擊span的時候,span 會把產生的事件往上冒泡,作為父節點的div2 和祖父節點的div1也會收到此事件,於是會做出事件回應,執行回應函數。現在問題是發現了,但是要怎麼解決呢? 方法一:我們來考慮一個形像一點的情況:水中的一個氣泡正在從底部往上冒,而你現在在水中,不想讓這個氣泡往上冒,怎麼辦呢? ——把它刺破!沒了氣泡,自然不會往上冒了。類似地,對某節點而言,如果不想它現在處理的事件繼續往上冒泡的話,我們可以終止冒泡: 在對應的處理函數內,加入 event.stopPropagation() ,終止事件的廣播分發,這樣事件停留在本節點,不會再往外傳播了。修改上述的script片段: ? #123456#78##9 #10 11 12 13 14 15 16 #"text/javascript"> window.onload = function () { document.getElementById( "box1" ).addEventListener("click",function(event){ alert( #"您好,我是最外層div。" #); event.stopPropagation(); #}); document.getElementById("box2").addEventListener( "click"#,function(event){ alert("你好,我是第二層div。 }); document.getElementById("span").addEventListener("click",#function#(event){ alert("你好,我是span。"); event.stopPropagation();############ #######});############ ######}#### ############################## 經過這樣一段程式碼,點選不同元素會有不同的提示,不會出現彈出多個方塊的狀況了。 方法二:事件包含最初觸發事件的節點引用 和 目前處理事件節點的引用,那麼如果節點只處理自己觸發的事件即可,不是自己產生的事件不處理。 event.target 引用了產生此event物件的dom 節點,而event.currrentTarget 則引用了目前處理節點,我們可以透過這 兩個target 是否相等。 例如span 點選事件,產生一個event 事件對象,event.target 指向了span元素,span處理此事件時,event.currentTarget 所指向的也是span元素,這時判斷兩者相等,則執行對應的處理函數。而事件傳遞給 div2 的時候,event.currentTarget變成 div2,這時候判斷二者不相等,也就是事件不是div2 本身產生的,就不作回應處理邏輯。 ? #1 2 #3 4 5 6 7 #8 9 10 11 12 13 14 15 16 17 18 19 20 21 #22 23 <code class="js string">"text/javascript"</code><code class="js plain">#></code></div> <div class="line number2 index1 alt1"> <code class="js spaces"> </code><code class="js plain">window .onload = </code><code class="js keyword">function</code>##() {<code class="js plain"></code> </div> <div class="line number3 index2 alt2"># <code class="js spaces"></code>document.getElementById(<code class="js plain"></code>#"box1" <code class="js string"></code>).addEventListener(<code class="js plain"></code>"click"<code class="js string"></code>,<code class="js plain"></code>function<code class="js keyword"></code>(event){<code class="js plain"></code> </div> <div class="line number4 index3 alt1"> <code class="js spaces"></code>if<code class="js keyword"></code>(event.target == event.currentTarget)<code class="js plain"></code> </div> <div class="line number5 index4 alt2"> <code class="js spaces"></code>#{<code class="js plain"> </code> </div> <div class="line number6 index5 alt1"> <code class="js spaces"></code>alert(<code class="js plain"></code>#"您好,我是最外層div."<code class="js string"></code>);<code class="js plain"></code> </div> <div class="line number7 index6 alt2"> <code class="js spaces"></code><code class="js plain"></code> </div> <div class="line number8 index7 alt1"> <code class="js spaces"></code><code class="js plain"></code> </div> <div class="line number9 index8 alt2"> <code class="js spaces"># </code><code class="js plain">}</code><code class="js string"></code><code class="js plain"> </code><code class="js string">});</code><code class="js plain"></code><code class="js keyword"> </code><code class="js plain"></code> </div> <div class="line number10 index9 alt1"> <code class="js spaces"></code><code class="js keyword"></code><code class="js plain"></code> </div> <div class="line number11 index10 alt2"> <code class="js spaces"></code><code class="js plain"></code> </div> <div class="line number12 index11 alt1"> <code class="js spaces"></code><code class="js plain"></code><code class="js string"># #document.getElementById(</code><code class="js plain">"box2"</code> </div>).addEventListener(######"click"######,#######function### ###(event){############ ######if######(event.target == event.currentTarget)########## #### ######{############ ######alert(######"你好,我是第二層div。"# #####);######<div class="line number13 index12 alt2"> <code class="js spaces"> </code><code class="js plain">}</code> </div> <div class="line number14 index13 alt1"> <code class="js spaces"> </code><code class="js plain">});</code> </div> <div class="line number15 index14 alt2"> <code class="js spaces"> </code><code class="js plain">#document.getElementById(</code><code class="js string">"span"</code><code class="js plain">).addEventListener(</code><code class="js string">"點擊"</code><code class="js plain">,</code><code class="js keyword">#function</code><code class="js plain">(事件){</code> </div> <div class="line number16 index15 alt1"> <code class="js spaces"> </code><code class="js keyword">if</code><code class="js plain">(event.target == event.currentTarget)</code> </div> <div class="line number17 index16 alt2"> <code class="js spaces"># </code><code class="js plain">{</code> </div> <div class="line number18 index17 alt1"> <code class="js spaces"> </code><code class="js plain">alert(</code><code class="js string">"您好,我是跨度。"</code> <code class="js plain">);</code> </div> <div class="line number19 index18 alt2"> <code class="js spaces"> </code># </div> <div class="line number20 index19 alt1"> <code class="js spaces">## </code><code class="js plain">}</code> </div> <div class="line number21 index20 alt2"> <code class="js spaces"></code><code class="js plain"></code> </div> <div class="line number22 index21 alt1"> <code class="js spaces"></code><code class="js plain"></code> </div> <div class="line number23 index22 alt2"><code class="js plain"></code></div> </div></td> </tr></tbody> </table>#});############ ######}############腳本>############# # ###############</div></div></div> <p> 比較:</p> <p> 從事件傳遞來看:方法一在於取消事件冒泡,即當某些節點取消冒泡後,事件不會再傳遞;方法二在於無法阻止冒泡,過濾需要處理的事件,事件處理後還會繼續傳遞;</p> <p> 優缺點:</p> <p> 方法一缺點:為了實現點選特定的元素所顯示的訊息,方法一要求每個元素的子元素也必須終止事件的冒泡傳遞,即跟別的元素功能上強關聯,這樣的方法會很脆弱。例如,如果span 元素的處理函數沒有執行冒泡終止,則事件會傳到div2 上,這樣會造成div2 的提示訊息;</p> <p> 方法二缺點:方法二為每個元素增加了事件監聽處理函數,事件的處理邏輯都很相似,即都有判斷if(event.target == event.currentTarget),這樣存在了很大的程式碼冗餘,現在是三個元素還好,當有10幾個,上百個該怎麼辦呢? <br>還有就是為每一個元素都有處理函數,在某種程度上增加邏輯和程式碼的複雜度。 </p> <p> 我們再來分析方法二:方法二的原理是元素收到事件後,判斷事件是否符合要求,然後做對應的處理,然後事件繼續冒泡往上傳遞;<br> 是冒泡傳遞的,那可不可以讓某個父節點統一處理事件,透過判斷事件的發生地(即事件產生的節點),然後再做出相對應的處理呢?答案是可以的,下面透過為body 元素添加事件監聽,然後透過判斷event.target 然後對不同的target產生不同的行為。 </p> <p> 將方法二的程式碼重構:</p> <div class="jb51code"><div><div class="syntaxhighlighter js">##?<div class="toolbar"></div> <table border="0"><tbody><tr class="firstRow"> <td class="gutter">#1 <div class="line number1 index0 alt2"></div>2<div class="line number2 index1 alt1"></div>3<div class="line number3 index2 alt2"></div>4<div class="line number4 index3 alt1"></div>5<div class="line number5 index4 alt2"></div>#6<div class="line number6 index5 alt1"></div>7<div class="line number7 index6 alt2"></div>8<div class="line number8 index7 alt1"></div>9<div class="line number9 index8 alt2"></div>10<div class="line number10 index9 alt1"></div>11<div class="line number11 index10 alt2"></div>12<div class="line number12 index11 alt1"></div>13<div class="line number13 index12 alt2"></div>14<div class="line number14 index13 alt1"></div>15<div class="line number15 index14 alt2"></div>16<div class="line number16 index15 alt1"></div>17<div class="line number17 index16 alt2"></div>18<div class="line number18 index17 alt1"></div>19<div class="line number19 index18 alt2"></div> </td> <td class="code"> <div class="container">##<script type=<div class="line number1 index0 alt2"><code class="js plain">"text/javascript"</code><code class="js string">><code class="js plain"></code> </div># <div class="line number2 index1 alt1"> <code class="js spaces">window.onload = </code><code class="js plain">function</code><code class="js keyword">#() {</code><code class="js plain"></code> </div> <div class="line number3 index2 alt2"> <code class="js spaces">document.getElementById(</code><code class="js plain">"body"</code><code class="js string">).addEventListener(</code> <code class="js plain">"click"</code><code class="js string">,eventPerformed);</code><code class="js plain"></code> </div># <div class="line number4 index3 alt1"> <code class="js spaces">}</code><code class="js plain"></code>## </div> <div class="line number5 index4 alt2">#function<code class="js spaces"> </code>eventPerformed(event) {<code class="js keyword"></code><code class="js plain"></code># </div> <div class="line number6 index5 alt1">var<code class="js spaces"> </code>target = event.target;<code class="js keyword"></code><code class="js plain"></code> </div> <div class="line number7 index6 alt2">switch<code class="js spaces"> </code>(target.id) {<code class="js keyword"></code><code class="js plain"></code> </div> <div class="line number8 index7 alt1">case<code class="js spaces"> </code>"span"<code class="js keyword"></code>: <code class="js string"></code><code class="js plain"></code> </div> <div class="line number9 index8 alt2">alert(<code class="js spaces"></code>"您好,我是span。"<code class="js plain"> </code>);<code class="js string"></code><code class="js plain"></code> </div> <div class="line number10 index9 alt1">break<code class="js spaces"></code>;<code class="js keyword"></code>#<div class="line number11 index10 alt2"> <code class="js spaces"> </code><code class="js keyword">case</code> <code class="js string">"div1"</code><code class="js plain">#:</code> </div> <div class="line number12 index11 alt1"> <code class="js spaces"> </code><code class="js plain">alert(</code><code class="js string">"您好,我是第二層div。"</code><code class="js plain">);</code> </div> <div class="line number13 index12 alt2"> <code class="js spaces"># </code><code class="js keyword">break</code><code class="js plain"> ;</code> </div> <div class="line number14 index13 alt1"> <code class="js spaces"> </code><code class="js keyword">case</code> <code class="js string">"div2"</code><code class="js plain">:</code> </div> <div class="line number15 index14 alt2"> <code class="js spaces"> </code><code class="js plain">alert(</code><code class="js string">"您好,我是最外層div。"</code><code class="js plain">#);</code> </div> <div class="line number16 index15 alt1"> <code class="js spaces"> </code><code class="js keyword"> break</code><code class="js plain">;</code> </div> <div class="line number17 index16 alt2"> <code class="js spaces"> </code><code class="js plain">}</code> </div> <div class="line number18 index17 alt1"> <code class="js spaces"> </code><code class="js plain">}</code> </div> <div class="line number19 index18 alt2"><code class="js plain"> ## 結果會被點擊不同的元素,只彈出相符合的提示,不會有多餘的提示。 透過以上方式,我們把原本每個元素都要有的處理函數,都交給了其祖父節點body 元素來完成了,也就是說,span,div2,div1 將自己的回應邏輯委託給body,讓它完成對應邏輯,自己不實作對應邏輯,這個模式,就是所謂的事件委託。 以下為示意圖: