事件原理三階段
捕獲(由外向內)、目標、冒泡(由內向外)
事件冒泡(event bubbling),即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然后逐級向上傳播到較為不具體的節點(文檔)。即子標簽發生事件后,向父級發送該事件,一直追溯到document。如:點擊一個嵌套在 body中的button,則該button的onclick事件也會傳遞給body、document中,觸發他們的onclick里觸發的函數。
案例
<style>
div{
position: absolute;
left:0;
right:0;
top:0;
bottom:0;
}
.div0
{
width: 200px;
height: 200px;
background-color: skyblue;
margin: auto;
}
.div1{
width: 100px;
height: 100px;
background-color: yellowgreen;
margin: auto;
}
.div2{
width: 50px;
height: 50px;
background-color: orange;
margin: auto;
}
</style>
</head>
<body>
<div class="div0">
<div class="div1">
<div class="div2"></div>
</div>
</div>
<script>
div0.addEventListener("click",clickHandler0);
div1.addEventListener("click",clickHandler1);
div2.addEventListener("click",clickHandler2);
function clickHandler0(e){
console.log("點擊div0")
}
function clickHandler1(e){
console.log("點擊div1")
}
</script>
三階段原理過程:
阻止事件冒泡
當對子元素添加了事件偵聽后,執行的時候會觸發父元素相同類型的事件,此時需要阻止事件冒泡。
早期IE是沒有捕獲階段的,只有冒泡,cancelBubble為阻止冒泡。后來的stopPropagaiton,既有阻止冒泡的功能,也有阻止捕獲的功能,但如果譯為阻止傳播,那么跟cancel就是兩個東西了,所以還是叫做阻止冒泡。阻止事件冒泡(傳播)的方法是:
- e.stopPropagation();通用
- e.cancelBubble=true;僅適用在IE8及以下
<script>
div0.addEventListener("click",clickHandler0,true);//開啟捕獲時就執行
div1.addEventListener("click",clickHandler1);
div2.addEventListener("click",clickHandler2,true);//開啟捕獲時就執行
function clickHandler2(e){
console.log("點擊div2")
// console.log(e);
// 停止冒泡,后面的就不會冒泡了
e.stopPropagation();
// 僅適用在IE8及以下
// e.cancelBubble=true;
}
/script>
div0.addEventListener(事件類型,事件回調函數,是否捕獲時執行);
事件類型 必須是字符串,可以設置為任意字符串,但是部分字符串是系統事件類型
事件回調函數 指向一個函數,當收到事件時執行該函數,如果沒有收到不執行函數,寫偵聽事件時不執行函數
是否捕獲時執行 默認值是false,在冒泡時執行,捕獲時不執行,
點擊div2發現執行順序發生改變
事件委托
事件偵聽添加(注冊事件)占有內存的,盡量減少事件偵聽的數量,將子元素的事件委托給父元素來執行,叫做事件委托。
當刪除對象時,一定要將對象上的偵聽事件移除,否則會造成內存泄露。
應用於:在多個元素進行偵聽事件中,如果這些元素有容器嵌套關系,就需要考慮阻止冒泡。
當多個元素需要偵聽事件時,可以給這些元素的父容器增加事件,達到偵聽所有元素,即是事件委托效果。
案例:點擊 li ,讓其子元素 ul 切換顯示。
<body>
<ul id="skils">
<li>H5
<ul>
<li>JS
<ul>
<li>原生</li>
<li>框架
<ul>
<li>VUEJs</li>
<li>ReactJs</li>
<li>AngularJs</li>
</ul>
</li>
<li>App</li>
<li>小程序</li>
<li>網頁開發</li>
</ul>
</li>
</ul>
</li>
<li>JAVA</li>
<li>PHP</li>
<li>LINUX</li>
<li>PYTHON</li>
</ul>
<script>
//把子元素的偵聽事件全部委托給最外層的父元素,叫做事件委托
init();
function init(){
var skils = document.getElementById("skils");
//給父元素添加偵聽事件
skils.addEventListener("click",clickHandler);
}
function clickHandler(e){
//e.target 事件的目標 真實點擊到最終的目標對象
//阻止冒泡,到此就結束,不再冒泡
e.stopPropagation();
//判斷點擊目標的節點名是不是“LI” ,不是就不執行
if(e.target.nodeName !== "LI") return;
//判斷點擊目標有沒有子元素
if(e.target.firstElementChild){
// 設置開關,顯示和隱藏ul
// 第一次默認是隱藏
if(!e.target.bool){
e.target.firstElementChild.style.display = "none";
}else{
e.target.firstElementChild.style.display = "block";
}
//點擊完后將e.target.bool 取反,進行顯示操作
e.target.bool = !e.target.bool;
}
}
</script>
</body>
擴展:
- e.currentTarget 是事件偵聽事件對象(什么對象執行addEventListener函數就是誰)
- e.target 事件的目標對象 真實點擊的最終目標對象
- e.srcElement 事件的目標對象,兼容IE
- 事件函數中this默認等同於e.currentTarget,都是事件偵聽的對象