事件的執行順序
先看如下代碼:
<div>
<ul>
<li>冒泡/捕獲</li>
</ul>
</div>
<script>
var html = document.documentElement;
var body = document.body;
var div = body.querySelector('div');
var ul = body.querySelector('ul');
var li = body.querySelector('li');
ul.addEventListener('click',callback,true);
li.addEventListener('click',callback,true);
div.addEventListener('click',callbackdiv,true);
body.addEventListener('click',callback,true);
html.addEventListener('click',callback,true);
ul.addEventListener('click',callback2);
li.addEventListener('click',callback2);
div.addEventListener('click',callbackdiv2);
body.addEventListener('click',callback2);
html.addEventListener('click',callback2);
function callback(event){
var target = event.currentTarget;
console.log(target.tagName + '--事件捕獲');
}
function callback2(event){
var target = event.currentTarget;
console.log(target.tagName + '--事件冒泡');
}
function callbackdiv(event){
//event.stopPropagation();//阻止事件冒泡
console.log('div callback--事件捕獲');
}
function callbackdiv2(event){
//event.stopPropagation();//阻止事件冒泡
console.log('div callback--事件冒泡');
}
</script>
在問問題之前先補充一個知識點:
element.addEventListener(event, function, useCapture)
第三個參數 useCapture:可選。布爾值,指定事件是否在捕獲或冒泡階段執行。可能值:true - 事件句柄在捕獲階段執行;false- 默認。事件句柄在冒泡階段執行。
那么問題來了,點擊Li,js的執行順序是什么?
結果如下圖:
總結就是:先捕獲,后冒泡,捕獲從上到下,冒泡從下到上(形象點說法:捕獲像石頭沉入海底,冒泡則像氣泡冒出水面)
假如放開callbackdiv2的 event.stopPropagation(); 結果又會輸出什么?
結果如下圖:
callbackdiv2 監聽的是div的事件冒泡,所以當callbackdiv2被觸發時event.stopPropagation()阻止了事件的冒泡,事件冒泡就不會繼續往上傳播了。
接下來我們放開callbackdiv的 event.stopPropagation(); 結果又會輸出什么?(至此,兩個阻止冒泡的注釋已經全部放開)
結果如下圖:
callbackdiv監聽的是div的時間捕獲,我們知道事件的順序是先捕獲后冒泡,所以放開callbackdiv的 event.stopPropagation(); 后,在時間捕獲傳播到div的時候就會阻止繼續往下傳播了。
通過以上我們可知,event.stopPropagation();可以阻止捕獲和冒泡階段中當前事件的進一步傳播。
看到這里我想大家對事件捕獲和事件冒泡的執行順序已經有了一個大概的了解,接下來我又做了幾個有趣的實驗,我們一起來看一下。
此為html代碼:
<body>
<ul class="ulclass">
<li class="li1class">1</li>
</ul>
</body>
實驗一:
<script type="text/javascript">
var $El = document.querySelector(".ulclass");
var $El1 = document.querySelector(".li1class");
//冒泡
$El.addEventListener("click",function(){
alert("ul");
},false);
//捕獲
$El1.addEventListener("click",function(){
alert("li");
},true);
</script>
實驗分析:冒泡執行過程中是從最內層元素 li 開始往外冒泡,在 經過 li 一層元素后到達 ul 被觸發。而捕獲過程則一共經歷 window-> document -> body -> ul 四層元素后到達 li ,被觸發,看起來好像是應該先彈出 ul ,然后再彈出 li。
實驗結果:點擊 li 內的 1 的時候,第一次彈出 “li”,第二次彈出 “ul” 。
實驗二
<script type="text/javascript">
var $El = document.querySelector(".ulclass");
var $El1 = document.querySelector(".li1class");
//捕獲
$El.addEventListener("click",function(){
alert("ul");
},true);
//冒泡
$El1.addEventListener("click",function(){
alert("li");
},false);
</script>
實驗分析:如果上述猜想是正確的,那么這次的執行結果就應該是先彈出 “ul” 再彈出 “li”。
實驗結果:先彈出 “ul” 再彈出 “li”。
實驗四
再來試一下兩個事件都冒泡的情況:
//冒泡
$El.addEventListener("click",function(){
alert("ul");
},false);
//冒泡
$El1.addEventListener("click",function(){
alert("li");
},false);
實驗分析:如果兩個都為冒泡的情況下,點擊后應從最里面的元素往外冒,也就是先彈出 li , 再彈出 ul.
實驗結果:無誤。
然后將兩個事件都改成捕獲,則先彈出 ul 再 彈出 li ,與預期完全一樣。
實驗五
這里把本來在 ul 上綁定的事件放到 li 上,也就是 li 綁定兩個事件,一個為冒泡,一個為捕獲。
var $El = document.querySelector(".ulclass");
var $El1 = document.querySelector(".li1class");
//冒泡
$El1.addEventListener("click",function(){
alert("冒泡");
},false);
//捕獲
$El1.addEventListener("click",function(){
alert("捕獲");
},true);
實驗分析:按照之前的猜測,應該是先彈出捕獲,然后彈出冒泡。(如果是這樣的話我也不會寫這么多了...) 實驗結果:先彈出了 冒泡,然后彈出了捕獲。
為什么??
猜想:在同一個元素的綁定事件中,冒泡和捕獲沒有次序之分,遵循Javascript代碼的先后順序執行。
實驗六
為了進一步證實這個猜測...我們將冒泡和捕獲的位置調換一下
var $El = document.querySelector(".ulclass");
var $El1 = document.querySelector(".li1class");
//捕獲
$El1.addEventListener("click",function(){
alert("捕獲");
},true);
//冒泡
$El1.addEventListener("click",function(){
alert("冒泡");
},false);
實驗結果: 先捕獲,后冒泡。
看似是5的猜測是對的,但是在重復實驗五 的時候,li下一級有一個元素 a ,直接點擊 li 很符合上面的推測,但是在點擊a元素的時候,先彈出了捕獲,后冒泡。
為什么??
猜想:在元素上同時綁定捕獲事件和冒泡事件,如果通過此元素的子級元素觸發,則優先觸發捕獲事件,若通過該元素自身觸發,那么冒泡和捕獲沒有次序之分,則按照Javascript代碼的先后順序執行。
附:
冒泡經常用於需要綁定很多事件的時候,給他們父級元素綁定一個事件,可以有效的提高代碼執行效率。 比如:
<body>
<div class="">
<ul class="ulclass">
<li class="li1class">1</li>
<li class="li1class">1</li>
<li class="li1class">1</li>
...
...
<li class="li1class">1</li>
</ul>
</div>
</body>
假如此處有100個li,每個li元素上都綁定一個事件的話就驗證影響了執行效率,那么就可以在ul 上綁定一個事件:
<script type="text/javascript">
var $El = document.querySelector(".ulclass");
//冒泡
$El.addEventListener("click",function(){
console.log(event.target);
},false);
</script>
其中 event.target 就是獲取觸發來源。