Js 事件原理與事件委託
事件原理三階段
捕獲(有外向內)、目標、冒泡(由內向外)
事件冒泡(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,都是事件偵聽的物件