這篇聊聊事件冒泡,帶大家深入了解JS中的事件冒泡機制,希望對大家有幫助!
在瀏覽器用戶端套用平台,基本生活是以事件驅動的,即某個事件發生,然後做出對應的動作。
瀏覽器的事件所表示的是某些事情所發生的訊號。事件的闡述不是本文的重點,尚未了解的朋友,可以自行百度 進行了解,這將有助於更好地理解以下的內容 。
什麼是冒泡呢?
下面這個圖片大家應該心領神會吧,氣泡從水底開始往上升,由深到淺,升到最上面。在上升的過程中,氣泡會經過不同深度的水。
的整個dom樹;事件從dom 樹的底層層層往上傳遞,直到傳遞到dom的根節點。
簡單案例分析以下透過簡單的範例來闡述冒泡原則:
定義一個html, 裡面有三個簡單的dom 元素:div1,div2, span,div2包含span,div1 包含div2;而它們都在body 下:
#
<div>
<div>
<span>This is a span.</span>
</div>
</div>
#o在這個基礎上,我們「有」的基礎來實現「聽」功能: ,當body捕獲到event事件時,列印出事件發生的時間和觸發事件的節點資訊:
<script> window.onload = function() { document.getElementById("body").addEventListener("click",eventHandler); } function eventHandler(event) { console.log("时间:"+new Date(event.timeStamp)+" 产生事件的节点:" + event.target.id +" 当前节点:"+event.currentTarget.id); } </script>
當我們依序點擊"This is span",div2,div1,body後,輸出以下資訊:
分析上述的結果: ,還是div的子元素div2,還有span, 當這些元素被點擊click時,都會產生click事件,並且body都會捕獲到,然後調用相應的事件處理函數。就像水中的氣泡從底往上冒一樣,事件也會往上傳遞。
事件傳遞的示意圖如下:
一般地,事件在傳遞過程中會有一些訊息,這些是事件的組成部分:
事件發生的時間事件發生的地點事件的類型事件的當前處理者其他資訊
,完整的html程式碼如下:
nbsp;html> <script></script>Insert title here <script> window.onload = function() { document.getElementById("body").addEventListener("click",eventHandler); } function eventHandler(event) { console.log("时间:"+new Date(event.timeStamp)+" 产生事件的节点:" + event.target.id +" 当前节点:"+event.currentTarget.id); } </script> <div> <div> <span>This is a span.</span> </div> </div>
我們現在想實現這樣的功能,在div1 點擊的時候,彈出"你好,我是最外層div。",點擊div2 的時候,彈出"你好,我是第二層div";點擊span 的時候,彈出"您好,我是span。"。
由此我們會有下面的javascript片段:
<script> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ alert("您好,我是最外层div。"); }); document.getElementById("box2").addEventListener("click",function(event){ alert("您好,我是第二层div。"); }); document.getElementById("span").addEventListener("click",function(event){ alert("您好,我是span。"); }); } </script>
预期上述代码会单击span 的时候,会出来一个弹出框 "您好,我是span。" 是的,确实弹出了这样的对话框:
然而,不仅仅会产生这个对话框,当点击确定后,会依次弹出下列对话框:
这显然不是我们想要的! 我们希望的是点谁显示谁的信息而已。为什么会出现上述的情况呢? 原因就在于事件的冒泡,点击span的时候,span 会把产生的事件往上冒泡,作为父节点的div2 和 祖父节点的div1也会收到此事件,于是会做出事件响应,执行响应函数。现在问题是发现了,但是怎么解决呢?
方法一:我们来考虑一个形象一点的情况:水中的一个气泡正在从底部往上冒,而你现在在水中,不想让这个气泡往上冒,怎么办呢?——把它扎破!没了气泡,自然不会往上冒了。类似地,对某一个节点而言,如果不想它现在处理的事件继续往上冒泡的话,我们可以终止冒泡:
在相应的处理函数内,加入 event.stopPropagation() ,终止事件的广播分发,这样事件停留在本节点,不会再往外传播了。修改上述的script片段:
<script> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ alert("您好,我是最外层div。"); event.stopPropagation(); }); document.getElementById("box2").addEventListener("click",function(event){ alert("您好,我是第二层div。"); event.stopPropagation(); }); document.getElementById("span").addEventListener("click",function(event){ alert("您好,我是span。"); event.stopPropagation(); }); } </script>
经过这样一段代码,点击不同元素会有不同的提示,不会出现弹出多个框的情况了。
方法二:事件包含最初触发事件的节点引用 和 当前处理事件节点的引用,那如果节点只处理自己触发的事件即可,不是自己产生的事件不处理。event.target 引用了产生此event对象的dom 节点,而event.currrentTarget 则引用了当前处理节点,我们可以通过这 两个target 是否相等。
比如span 点击事件,产生一个event 事件对象,event.target 指向了span元素,span处理此事件时,event.currentTarget 指向的也是span元素,这时判断两者相等,则执行相应的处理函数。而事件传递给 div2 的时候,event.currentTarget变成 div2,这时候判断二者不相等,即事件不是div2 本身产生的,就不作响应处理逻辑。
<script> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ if(event.target == event.currentTarget) { alert("您好,我是最外层div。"); } }); document.getElementById("box2").addEventListener("click",function(event){ if(event.target == event.currentTarget) { alert("您好,我是第二层div。"); } }); document.getElementById("span").addEventListener("click",function(event){ if(event.target == event.currentTarget) { alert("您好,我是span。"); } }); } </script>
比较:
从事件传递上看:
方法一在于取消事件冒泡,即当某些节点取消冒泡后,事件不会再传递;
方法二在于不阻止冒泡,过滤需要处理的事件,事件处理后还会继续传递;
优缺点:
方法一缺点:为了实现点击特定的元素显示对应的信息,方法一要求每个元素的子元素也必须终止事件的冒泡传递,即跟别的元素功能上强关联,这样的方法会很脆弱。比如,如果span 元素的处理函数没有执行冒泡终止,则事件会传到p2 上,这样会造成p2 的提示信息;
方法二缺点:方法二为每一个元素都增加了事件监听处理函数,事件的处理逻辑都很相似,即都有判断 if(event.target == event.currentTarget),这样存在了很大的代码冗余,现在是三个元素还好,当有10几个,上百个又该怎么办呢?
还有就是为每一个元素都有处理函数,在一定程度上增加逻辑和代码的复杂度。
我们再来分析一下方法二:方法二的原理是 元素收到事件后,判断事件是否符合要求,然后做相应的处理,然后事件继续冒泡往上传递;
既然事件是冒泡传递的,那可不可以让某个父节点统一处理事件,通过判断事件的发生地(即事件产生的节点),然后做出相应的处理呢?答案是可以的,下面通过给body 元素添加事件监听,然后通过判断event.target 然后对不同的target产生不同的行为。
将方法二的代码重构一下:
<script> window.onload = function() { document.getElementById("body").addEventListener("click",eventPerformed); } function eventPerformed(event) { var target = event.target; switch (target.id) { case "span": alert("您好,我是span。"); break; case "div1": alert("您好,我是第二层div。"); break; case "div2": alert("您好,我是最外层div。"); break; } } </script>
结果会是点击不同的元素,只弹出相符合的提示,不会有多余的提示。
透過以上方式,我們把原本每個元素都要有的處理函數,都交給了其祖父節點body 元素來完成了,也就是說,span,p2, p1 將自己的回應邏輯委託給body,讓它完成對應邏輯,自己不實現對應邏輯,這個模式,就是所謂的事件委託。
下面是示意圖:
【相關推薦:javascript學習教程】
#以上是一文搞懂JS中的事件冒泡機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!