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