舉一個小例子,說明下對冒泡的理解javascript
首先,先寫html,就是一個按鈕+一個浮層,再加上點csscss
<body> <div id="wrap"> <button id="clickme">Click Me</button> <div id="fuceng"> gone with the wind </div> </div> </body>
而後,寫點js實現功能:點擊ClickMe按鈕彈出gone with the wind浮層,點擊別的地方隱藏浮層html
<script> $("#clickme").on("click",function(){ console.log("clickme-clicked"); $("#fuceng").show(); }) $(document).on("click",function(){ console.log("document-clicked"); $("#fuceng").hide(); }) </script>
不難理解,點擊$("#clickme")
把$("#fuceng")
show()
出來,點擊文檔$(document)
把$("#fuceng")
hide()
掉java
在單擊$("#clickme")
的時候並無顯示$("#fuceng")
,爲何???見下圖ide
問題解釋:遇到事件時,先按照黑色箭頭方向從祖先問到本身:捕捉事件,而後再按照紅色箭頭從本身問到祖先:冒泡事件。問的內容是:某某元素被xxx(事件名)了,你是否有函數要操做?函數
本例中,沒有捕捉階段,直接到冒泡階段。$("#clickme")
被點擊後,操做了本身的函數:$("#fuceng").show();
。那爲何$("#fuceng")
不顯示呢???由於冒泡還沒結束,問過$("#clickme")
後又問到了它的祖先$(document)
:你的一個子孫被點擊了,你有函數要操做嗎?有!!!而後操做了它的函數$("#fuceng").hide();
,因此又被隱藏了。。。優化
爲了顯示過程,在兩個函數裏分別加上console.log
,結果見下圖。
說明,先執行了$("#clickme")
的函數,後執行了$(document)
的函數。spa
簡單再描述一遍,就是咱們同時在按鈕和頁面上綁定了onclick
事件,結果就是觸發按鈕的onclick
同時也觸發了頁面的onclick
,由於按鈕在頁面裏面。code
其中一個解決方法就是,當觸發按鈕的onclick
時,阻止冒泡事件再往上通知,示意圖以下。htm
代碼以下:
<script> $("#clickme").on("click",function(){ console.log("clickme-clicked"); $("#fuceng").show(); }) //新添加的代碼 $("#wrap").on("click",function(e){ e.stopPropagation(); }) $(document).on("click",function(){ console.log("document-clicked"); $("#fuceng").hide(); }) </script>
在按鈕的父元素$("#wrap")
上添加onclick
事件,來阻止按鈕的onclick
再向上通知。在$("#wrap")
上阻止冒泡事件,使得不管在點擊按鈕仍是顯現出來的浮層都不會使浮層消失。
假使頁面中有不少個浮層,像上面這樣每個浮層都監聽一次$(document)
會大大的佔用內存,因此思路是使用one()
方法,只在點擊按鈕的時候監聽一次$(document)
,代碼以下:
<script> $("#clickme").on("click",function(e){ $("#fuceng").show(); //新增代碼 $(document).one("click",function(){ $("#fuceng").hide(); }) }) $("#wrap").on("click",function(e){ e.stopPropagation(); }) </script>
上述優化代碼可否簡單成下面這樣呢?
<script> $("#clickme").on("click",function(e){ $("#fuceng").show(); //新增代碼 $(document).one("click",function(){ $("#fuceng").hide(); }) }) </script>
你看,我在點擊按鈕的時候只監聽一次$("document")
,又沒有再點擊頁面了,應該沒問題吧?有問題!!!以下圖。
點擊按鈕(藍色箭頭),調用裏面的函數,執行①、執行②。注意,這裏執行②就是再給頁面添加了一個點擊事件,且,在執行按鈕的點擊事件時,冒泡事件的追問是中止的。等調用完了,再接着問按鈕的父輩、祖輩們按鈕被點擊了,你有沒有函數須要調用啊?
又問到了頁面,這時候頁面是有函數的,就是以前執行的②。
其實,除了使用阻止冒泡的方式阻止事件的傳播,還可使用setTimeout
方法,使須要綁定給頁面的函數在冒泡詢問完成後再綁定。
<script> $("#clickme").on("click",function(e){ $("#fuceng").show(); setTimeout(function(){ $(document).one("click",function(){ $("#fuceng").hide(); }) },0) }) </script>
html:
<body> <div id="red"> <div id="orange"> <div id="yellow"> <div id="green"> <div id="blue"> <div id="indigo"> <div id="purple"></div> </div> </div> </div> </div> </div> </div> </body>
css:
#red.active{ background: red; } #orange.active{ background: orange; } #yellow.active{ background: yellow; } #green.active{ background: green; } #blue.active{ background: blue; } #indigo.active{ background: indigo; } #purple.active{ background: purple; } #purple{ min-height: 100px; } div{ margin:10px; border: 1px solid black; }
js:
var n=0; $("div").on("click",function(e){ setTimeout(function(){ $(e.currentTarget).addClass("active"); },n*1000) n+=1; })
效果:
解釋:
html和css很好理解。js的意思就是點擊$("div")
的時候,添加active
類,即展示了背景顏色。本例中,點擊最內層<div id="purple"></div>
的時候,冒泡事件開始詢問。
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
n*1000
後添加active
類,當即執行n+=1
咱們該明白的是,全部事情是一步一步作的,即詢問下一個div時,n都是已經加過1了,並且,選擇器是$("div")
,因此,全部div都綁定了事件。