Js 事件原理與事件委託

碼上出彩發表於2020-04-18

事件原理三階段

捕獲(有外向內)、目標、冒泡(由內向外)

事件冒泡(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,都是事件偵聽的物件

相關文章