這篇文章主要介紹了Javascript 事件冒泡機制詳細介紹的相關資料,需要的朋友可以參考下
1. 事件
在瀏覽器客戶端應用平台,基本生都是以事件驅動的,也就是某個事件發生,然後做出對應的動作。
瀏覽器的事件所表示的是某些事情所發生的訊號。事件的闡述不是本文的重點,尚未了解的朋友,可以訪問W3school教程 進行了解,這將有助於更好地理解以下的內容 。
2.冒泡機制
為冒泡呢?
下面這個圖片大家應該心領神會吧,氣泡從水底開始往上升,由深到淺,升到最上面。在上升的過程中,氣泡會經過不同深度的水。
相對應地:這個氣泡相當於我們這裡的事件,而水則相當於我們整個的dom樹;事件從dom 樹的底層層往上傳遞,直至傳遞到dom的根節點。
簡單案例分析
下面透過一個簡單的範例來闡述冒泡原則:
定義一個html, 裡面有三個簡單的dom 元素:p1,p2, span,p2包含span,p1 包含p2;而它們都在body 下:
<body id="body"> <p id="box1" class="box1"> <p id="box2" class="box2"> <span id="span">This is a span.</span> </p> </p> </body>
介面原型如下:
# 在這個基礎上,我們實現下面的功能: a.body新增click 事件監聽,當body捕捉到event事件時,列印出事件發生的時間和觸發事件的節點資訊:
<script type="text/javascript"> 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>
o ##事件發生的時間事件發生的地點事件的類型事件的當前處理者其他信息,
##
完整的html代碼如下:
Insert title here <script type="text/javascript"> 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> <body id="body"> <p id="box1" class="box1"> <p id="box2" class="box2"> <span id="span">This is a span.</span> </p> </p> </body>
我們現在想實現這樣的功能,在p1 點擊的時候,彈出"你好,我是最外層p。 ",點擊p2 的時候,彈出"你好,我是第二層p";點擊span 的時候,彈出"您好,我是span。 由此我們會有下面的javascript片段:
<script type="text/javascript"> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ alert("您好,我是最外层p。"); }); document.getElementById("box2").addEventListener("click",function(event){ alert("您好,我是第二层p。"); }); document.getElementById("span").addEventListener("click",function(event){ alert("您好,我是span。"); }); } </script>
預期上述程式碼會點擊span 的時候,會出來一個彈出框"您好,我是span。" 是的,確實彈出了這樣的對話框:
然而,並僅產生此對話框,點選確定後,依序跳出下列對話方塊:
这显然不是我们想要的! 我们希望的是点谁显示谁的信息而已。为什么会出现上述的情况呢? 原因就在于事件的冒泡,点击span的时候,span 会把产生的事件往上冒泡,作为父节点的p2 和 祖父节点的p1也会收到此事件,于是会做出事件响应,执行响应函数。现在问题是发现了,但是怎么解决呢?
方法一:我们来考虑一个形象一点的情况:水中的一个气泡正在从底部往上冒,而你现在在水中,不想让这个气泡往上冒,怎么办呢?——把它扎破!没了气泡,自然不会往上冒了。类似地,对某一个节点而言,如果不想它现在处理的事件继续往上冒泡的话,我们可以终止冒泡:
在相应的处理函数内,加入 event.stopPropagation() ,终止事件的广播分发,这样事件停留在本节点,不会再往外传播了。修改上述的script片段:
<script type="text/javascript"> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ alert("您好,我是最外层p。"); event.stopPropagation(); }); document.getElementById("box2").addEventListener("click",function(event){ alert("您好,我是第二层p。"); 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元素,这时判断两者相等,则执行相应的处理函数。而事件传递给 p2 的时候,event.currentTarget变成 p2,这时候判断二者不相等,即事件不是p2 本身产生的,就不作响应处理逻辑。
<script type="text/javascript"> window.onload = function() { document.getElementById("box1").addEventListener("click",function(event){ if(event.target == event.currentTarget) { alert("您好,我是最外层p。"); } }); document.getElementById("box2").addEventListener("click",function(event){ if(event.target == event.currentTarget) { alert("您好,我是第二层p。"); } }); 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 type="text/javascript"> 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 "p1": alert("您好,我是第二层p。"); break; case "p2": alert("您好,我是最外层p。"); break; } } </script>
结果会是点击不同的元素,只弹出相符合的提示,不会有多余的提示。
透過上述方式,我們把原本每個元素都要有的處理函數,都交給了其祖父節點body 元素來完成了,也就是說,span,p2,p1 將自己的回應邏輯委託給body,讓它完成對應邏輯,自己不實作對應邏輯,這個模式,就是所謂的事件委託。
以下是示意圖:
上面是我整理給大家的,希望今後對大家有幫助。
相關文章:
JavaScript cookie詳解及簡單實例應用程式(圖文教學)
原生強大的DOM選擇器querySelector詳細介紹(程式碼附上)
以上是Javascript 事件冒泡機制詳細介紹(圖文教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!