JS事件(常用事件,事件傳播,事件循環)

事件

  1. 在元素對象有一些天生自帶的屬性名,比如onclick、onmouseover… 當鼠標觸發了這些相關的操作,那麼就會執行這些屬性對應的屬性值函數;
  2. 事件是元素天生自帶的一種行爲
  3. 事件是瀏覽器的一種行爲,也可以是用戶行爲
  4. 發生在HTML元素上的事

事件綁定

  • DOM0級事件綁定:都是冒泡階段的綁定
  • DOM2級事件可以控制階段
添加事件的幾種方法(DOM0級)
行內加
<div id="box" onclick="fn()">可以點擊</div>
<script>
  	function fn(){
    	console.log(100);
    }
  
js加
box.onclick = fn;
  
 jquery加
 $("#box").click(fn);
 $("#box").on("click",fn);
</script>
  1. addEventListener(事件行爲(去掉on),事件回調函數,布爾) ( DOM2級事件綁定
    1. 第三個參數是布爾值:false代表冒泡階段執行, true:捕獲階段執行
    2. 只能給同一個元素的同一個事件行爲綁定同一個方法,如果都相同,就會覆蓋
    3. 可以給同一個元素的同一個事件行爲綁定多個不同的方法
    4. removeEventListener() 移除事件,需要和添加的時候參數保持一致
    5. 只有元素才能調用這個方法,因爲只有元素的原型鏈上纔有
    6. 先綁定誰,誰先執行
    7. 函數中的this指向被點擊的那個元素
    8. 正序執行(包括 IE9、IE10、IE11)
		function fn1(){
           console.log(1);
       }
       function fn2(){
           console.log(2);
       }
		//
    	box.addEventListener("click",fn1,true);
    	box.removeEventListener("click",fn1,false);
		//
    	box.addEventListener("mouseover",fn1,false)
    	box.addEventListener("click",fn2,false);
		//
		box.addEventListener("click",fn1,false)
      	box.addEventListener("click",fn2,false)
		//只會執行一次,執行第二個 
  1. attachEvent(“on”+事件,fn) DOM2級事件綁定
    1. 在IE8以上不兼容的; 谷歌不能用
    2. 倒序執行(在IE8)
    3. 方法中的this指向全局下的window對象
    4. 可以重複綁定
    5. detachEvent 刪除事件
		function fn1(){
           console.log(1);
       }
       function fn2(){
           console.log(2);
       }	
			
		box.attachEvent("onclick",fn1)
    	box.attachEvent("onclick",fn2)
		box.attachEvent("onclick",fn1)
        box.attachEvent("onclick",fn1)
		//每個fn1都會執行

事件對象

  1. 事件對象:當用戶通過鼠標鍵盤去操作或觸發元素的事件行爲時,瀏覽器會默認將一些事件的信息傳遞給這個函數的第一個參數(比如鼠標點擊的位置距離頁面左右的距離,或距離點擊元素邊框的距離)
    1. clientX : 當前鼠標點擊的位置距離可視窗口左邊的距離
    2. clientY : 當前鼠標點擊的位置距離可視窗口上邊的距離
    3. offsetX : 當前鼠標點擊的位置距離盒子左邊框的距離
    4. offsetY : 當前鼠標點擊的位置距離盒子上邊框的距離
    5. pageX :當前鼠標點擊的位置距離頁面左邊框的距離
    6. pageY :當前鼠標點擊的位置距離頁面上邊框的距離
    7. target :事件源,事件在哪個元素上觸發,事件源就是誰
    8. type :事件類型(‘click’)
    9. e.cancelBubble=true :取消事件默認的冒泡傳播
    10. e.stopPropagation();// IE8及以下不兼容 取消事件默認的冒泡傳播
  2. 在IE8及以下,瀏覽器將事件信息放到了window.event上,並沒有傳遞給函數的形參
box.onclick=function(e){
  e=e||window.event;
}

事件的默認行爲

  1. a標籤的點擊默認跳轉就是a的默認行爲
  2. form表單subimt,會默認提交
  3. 阻止事件的默認行爲:
    1. 形參.preventDefault() : 阻止事件的默認行爲
    2. 形參.returnValue = false
<form action="">
        <input type="text" name="user">
        <input type="text" value="提交">
</form>
<a href="" id="abc">點我一下</a>

<script>
	var abc = document.getElementById("abc");
  abc.onclick = function(e){
  	//e.preventDefault();
  	e.returnValue=false;
  }
</script>

input框事件

  1. onfocus : 獲取鼠標焦點
  2. onblur : 失去鼠標焦點
  3. onchange : 當鼠標離開input框,並且input框中的內容發生改變,會觸發改事件
  4. oninput : 當input框每改變一次,就會執行一次
  5. onkeydown : 當鍵盤按下時,觸發的事件(獲取到上一次的值)
  6. onkeyup : 當鍵盤擡起獲取到最新的值
<input type="text" id="btn">

<script>
	let btn = document.getElementById("btn");
  
  	btn.onfocus=function(){
        console.log(100);
    }
    btn.onclick = function(){
        console.log(200);
    }
    btn.onblur=function(){
        console.log(300);
    }
    btn.onchange=function(){
        console.log(400);
    }
    btn.oninput=function(e){
        console.log(e);
        console.log(this.value); //獲取input框的值 
    }
    btn.onkeydown=function(){
        console.log(this.value);
    }
  
  	btn.onkeyup=function(e){
        console.log(e);// 鍵盤事件對象
        // 在鍵盤上,每一個鍵盤的鍵都有一個對應的keyCode;根據keyCode可以判斷當前點擊的是哪一個鍵;
    }
</script>

keyCode表

在這裏插入圖片描述

事件的傳播

  1. 捕獲階段–>目標階段–>冒泡階段
  2. 冒泡傳播
    1. 事件有冒泡傳播的機制 : 當觸發子元素的事件時,會依次觸發當前元素祖先元素上對應的事件
    2. 取消事件的默認冒泡傳播
      1. 形參.cancelBubble = true;
      2. 形參.stopPropagation(); IE8及以下不兼容
  3. center.addEventListener(“click”,fn4,true)
    1. 第三個參爲true時捕獲階段執行,從外向裏
    2. 爲false時冒泡階段執行,從裏向外
    3. 當找到事件源,誰先綁定誰先執行
  4. 事件冒泡執行過程:

從最具體的的元素(你單擊的那個元素)開始向上開始冒泡,拿我們上面的案例講它的順序是:child->box
事件捕獲執行過程:
從最不具體的元素(最外面的那個盒子)開始向裏面冒泡,拿我們上面的案例講它的順序是:box->child

 		<style>
        *{
            margin: 0;
            padding: 0;
        }
        #outer{
            width: 300px;
            height: 300px;
            background: red;
            margin: 100px auto;
        }
        #inner{
            width: 200px;
            height: 200px;
            background: green;
            margin: auto;
        }
        #center{
            width: 100px;
            height: 100px;
            background: yellow;
            margin: auto;
        }
    </style>


<body>
    <div id="outer">
        <div id="inner">
            <div id="center"></div>
        </div>
    </div>
  <script>
        let outer = document.getElementById('outer');
        let inner= document.getElementById('inner');
        let center = document.getElementById('center');
    
   		 function fn1(e) {
            console.log('center');
            
        }
        function fn2(e) {
            console.log('inner');
            
        }
        function fn3(e) {
            console.log('outer');
        }
          
          //冒泡階段
         outer.onclick = function (e) {
           // console.log(e.target);
            
            console.log('outer');
            
        }
        inner.onclick = function (e) {
           // console.log(e.target);
            
            console.log('inner');
            
        }
        center.onclick = function (e) {
           // console.log(e.target);
            
            console.log('center');
            
        }
        //fn1 fn2 fn3
          
        //執行階段
        center.addEventListener('click',fn1,true);
        inner.addEventListener('click',fn2,true);
        outer.addEventListener('click',fn3,true);
        //fn3 fn2 fn1
          
        //冒泡執行階段  
        function fn1(e) {
            console.log('center 冒泡');
            
        }
        function fn4(e) {
            console.log('center 捕獲');
            
        }
        function fn2(e) {
            console.log('inner 冒泡');
            
        }
        function fn5(e) {
            console.log('inner 捕獲');
            
        }
        function fn3(e) {
            console.log('outer 冒泡');
            
        }
        function fn6(e) {
            console.log('outer 捕獲');
            
        }
        center.addEventListener('click',fn1,false);
        inner.addEventListener('click',fn2,false);
        outer.addEventListener('click',fn3,false);
        center.addEventListener('click',fn4,true);
        inner.addEventListener('click',fn5,true);
        outer.addEventListener('click',fn6,true);
        // fn6 fn5 fn1 fn4 fn2 fn3

事件委託

  1. 事件委託:主要利用事件的冒泡傳播機制,給最外層的盒子綁定事件,根據事件源的不同,進行判斷,處理不一樣的需求
	<div id="parent">
        <div>1</div>
        <div>2</div>
        <div>3</div>
    </div>
			
		//三個div將onmouseover這個事件委託給這三個盒子的父元素 
		<script>
 			  let parent  = document.getElementById("parent");
      
	    	  parent.onmouseover=function(e){
			            if(e.target.innerHTML==="1"){
			                console.log("紅色");
			            }else if(e.target.innerHTML==="2"){
			                console.log("綠色"); 
			            }else if(e.target.innerHTML==="3"){
			                console.log("黃色");
			            }
	       	 }
		</script>


封裝拖拽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        #box,#box1{
            width:100px;
            height:100px;
            background: red;
            position: absolute;
            left:0;
            top:0;
        }
        #box1{
            top:200px;
        }
    </style>
</head>
<body>
    <div id="box">
    </div>
    <div id="box1"></div>
    <script>
       
       function Drag(str){
           // this--> Drag的實例;可以獲取到當前Drag原型上的方法
           let ele = document.getElementById(str.slice(1));
           this.ele = ele;// 把元素放到了實例this的自定義屬性上;
           this.ele.onmousedown  = this.down.bind(this);
       }
       Drag.prototype.down =function(e){
            // 記錄位置  綁定onmousemove 和onmouseup;
            // console.log(e);
            console.log(this);// Drag的實例;需要將該函數中的this指向Drag的實例;方便後期使用;
            this.x = e.clientX;
            this.y = e.clientY;
            this.startL = parseFloat(getComputedStyle(this.ele).left);
            this.startT = parseFloat(getComputedStyle(this.ele).top);
            document.onmousemove = this.move.bind(this);
            document.onmouseup = this.up;
       }
       Drag.prototype.move =function(e){
           // 用鼠標變化的距離+ 盒子初始的位置=盒子最新的位置;
            this.ele.style.left =  e.clientX-this.x+this.startL+"px";
            this.ele.style.top = e.clientY-this.y+this.startT+"px";
       }
       Drag.prototype.up =function(){
           // 清除document的事件上的方法;
           document.onmousemove = null;
           document.onmouseup = null;
       }
       new Drag("#box");
       new Drag("#box1")
    //    let  obj = {
    //        fn:function(){
    //            console.log(this);
    //        }
    //    }
    //    obj.fn();
    //    box.onclick = obj.fn;
    </script>
</body>
</html>

事件循環機制(Event Loop)

微任務 : await ,promise的then
宏任務: 定時器 ajax
事件循環機制: JS代碼執行分爲主任務隊列和等待任務隊列;在執行主棧的代碼遇到同步的代碼會立即執行,遇到異步的代碼會先放到等待隊列中,放入時區分是宏任務還是微任務,按照不同的任務放到等待隊列不同的池子中;當主棧執行完成時,那麼要先去等待隊列的微任務中去遍歷,按照放入時間先後依次執行,把微任務放到主棧中去執行,微任務執行完畢,再去執行宏任務。

        function fn1(){console.log(666);}
        setTimeout(function(){
            console.log(800);
        },0)
        console.log(900);
        async function fn(){
            console.log(999);
            await fn1();
            console.log(888); 
        }
        let  p = new Promise(function(resolve,reject){
            console.log(200);
            resolve();
            console.log(300);
        })
        p.then(function(){
            console.log(100);// 異步的
        });
        fn();
        // 900  200  300 999  666  888 100 800
        // 微任務執行的順序要看誰先放進任務隊列中,誰先執行;
        // async function async1() {
        //     console.log('async1 start');// 2
        //     await async2();// await 後面的是同步
        //     console.log('async1 end'); // 微1  6
        // }
        // async function async2() {
        //     console.log('async2'); // 3
        // }
        // console.log('script start');// 1
        // setTimeout(() => {
        //     console.log('setTimeout'); // 8
        // }, 0);
        // async1();
        // new Promise(resolve => {
        //     console.log('promise1');// 4
        //     resolve();
        // }).then(() => {
        //     console.log('promise2');// 微2  7
        // });
        // console.log('script end'); // 5

        // 微任務 : await  promise的then 
        // 宏任務: 定時器  ajax  
        // 事件循環機制: JS代碼執行分爲主任務隊列和對待任務隊列;
		在執行主棧的代碼遇到同步的代碼會立即執行,遇到異步的代碼會先放到等待隊列中,
		放入時區分是宏任務還是微任務,按照不同的任務放到等待隊列不同的池子中;當主棧執行完成時,
		那麼要先去等待隊列的微任務中去遍歷,按照放入時間先後依次執行,把微任務放到主棧中去執行,
		微任務執行完畢,再去執行宏任務。

        // 

            console.log('1');
            setTimeout(function () {
                console.log('2');
                new Promise(function (resolve) {
                    console.log('4');
                    resolve();
                }).then(function () {
                    console.log('5');
                });
            });
            new Promise(function (resolve) {
                console.log('7');
                resolve();
            }).then(function () {
                console.log('8');
            });
            setTimeout(function () {
                console.log('9');
                new Promise(function (resolve) {
                    console.log('11');
                    resolve();
                }).then(function () {
                    console.log('12');
                });
            });

在這裏插入圖片描述

事件詳解

查閱更多事件方式:
https://developer.mozilla.org/zh-CN/docs/Web/Events
或者查看元素的屬性(屬性中onxxx就是元素擁有的事件行爲)

一、常用事件

1. 鼠標事件

click 點擊(移動端 click 被識別爲單擊)

  1. dblclick 雙擊
  2. mousedown 鼠標按下
  3. mouseup 鼠標擡起
  4. mousemove 鼠標移動
  5. mouseover 鼠標滑過
  6. mouseout 鼠標滑出
  7. mouseenter 鼠標進入
  8. mouseleave 鼠標離開
  9. mousewhell 鼠標滾輪滾動
  • 鼠標滾輪
/*註冊事件*/
/*IE、Opera註冊事件*/
if (document.attachEvent) {
  outer1.attachEvent("onmousewheel", self.scrollFunc);
}
//Firefox使用addEventListener添加滾輪事件
if (document.addEventListener) {
  //firefox
  outer1.addEventListener(
    "DOMMouseScroll",
    self.scrollFunc,
    false
  );
}
//Safari與Chrome屬於同一類型
outer1.onmousewheel = self.scrollFunc;

// 滾輪
        scrollFunc(e) {
            e = e || window.event;
            console.dir(e);
            if (e.wheelDelta) {
                //判斷瀏覽器IE,谷歌滑輪事件
                if (e.wheelDelta > 0) {
                    //當滑輪向上滾動時
                    // alert("上滾");
                }
                if (e.wheelDelta < 0) {
                    //當滑輪向下滾動時
                    // alert("下滾");
                }
            } else if (e.detail) {
                //Firefox滑輪事件
                if (e.detail > 0) {
                    //當滑輪向下滾動時
                    // alert("下滾");
                }
                if (e.detail < 0) {
                    //當滑輪向上滾動時
                    // alert("上滾");
                }
            }
        },

2. 鍵盤事件

key…

  1. keydown 按下某個鍵
  2. keyup 擡起某個鍵
  3. keypress 除 shift / Fn / CapsLock 鍵以外,其他鍵按住(連續觸發)

3. 移動端手指事件

單手指事件模型 Touch

  1. touchstart 手指按下
  2. touchmove 手指移動
  3. touchend 手指鬆開
  4. touchcancel 操作取消(一般應用於非正常狀態下操作結束)

多手指事件模型 Gestrue

  1. gestruestart
  2. gesturechange / gestrueundate
  3. gestureend
  4. gesturecancel

4. 表單元素常用事件

  1. onchange : 當鼠標離開input框,並且input框中的內容發生改變,會觸發改事件
  2. onfocus : 獲取鼠標焦點
  3. onblur : 失去鼠標焦點
  4. oninput : 當input框每改變一次,就會執行一次
  5. onkeydown : 當鍵盤按下時,觸發的事件(獲取到上一次的值)
  6. onkeyup : 當鍵盤擡起獲取到最新的值

5. 音視頻常用事件

  1. canplay 可以播放(資源沒有加載完,播放中可能會卡頓)
  2. canplaythrough 可以播放(資源已經加載完,播放中不會卡頓)
  3. play 開始播放
  4. playing 播放中
  5. pause 暫停播放

6. 其他常用事件

  1. load 資源加載完
  2. unload 資源卸載
  3. beforeunload 當前頁面關閉之前
  4. error 資源加載失敗
  5. scroll 滾動事件
  6. readystatechange AJAX請求狀態改變事件
  7. contextmenu 鼠標右鍵觸發

二、DOM 0級事件

dom 0級事件綁定:
dom 0級事件綁定的原理:給元素的私有屬性賦值,當事件觸發,瀏覽器會幫我們把賦的值執行,但是這樣也導致 “只能給當前元素某一個事件行爲綁定一個方法”

元素.on事件行爲=function(){}
	box.onclick = function () {
		console.log('哈哈哈~~');
	}
	box.onclick = function () {
		console.log('呵呵呵~~');
	} 
	box.onclick = function () {
		console.log('哈哈哈~~');
		// 移除事件綁定:DOM0 直接賦值爲 null 即可
		box.onclick = null;
	} 

三、DOM 2級事件

dom 2級事件綁定:
dom 2級事件綁定的原理:基於原型鏈查找機制,找到 EventTarget.prototype 上的方法並且執行,此方法執行,會把給當前元素某個事件行爲綁定的所有方法,存放到瀏覽器默認的事件池中(綁定幾個方法,會向事件池存儲幾個),當事件行爲觸發,會把事件池中存儲的對應方法,依次按照順序執行 “給當前元素某一個事件行爲綁定多個不同方法”


在這裏插入圖片描述

元素.addEventListener(事件行爲,function(){},true/false)
IE6~8中:元素.attachEvent('on事件行爲',function(){})

box.addEventListener('click', function () {
		console.log('哈哈哈~~');
}, false);
box.addEventListener('click', function () {
	  console.log('呵呵呵~~');
}, false);

function fn() {
	console.log('哈哈哈~~');
	// 移除事件綁定:從事件池中移除,所以需要指定好事件類型、方法等信息(要和綁定的時候一樣纔可以移除)
	box.removeEventListener('click', fn, false);
}
box.addEventListener('click', fn, false);
  1. dom 2級事件綁定的時候我們一般都採用實名函數
  2. 目的:這樣可以基於實名函數去移除事件綁定
  3. 基於 addEventListener 向事件池增加方法,存在去重機制:“同一個元素,同一個事件類型,在事件池中只能存儲一遍這個方法,不能重複存儲”
  4. dom 0級事件和 dom 2級事件可以混在一起用:執行的順序以綁定的順序爲主
box.addEventListener('click', function () {
		console.log('嗶咔嗶咔~~');
});
box.onclick = function () {
		console.log('哇咔咔~~');
}
box.addEventListener('click', function () {
		console.log('call~~');
});
  1. dom 0級事件中能做的事件行爲,dom 2級事件都支持,dom 0級事件不一定能處理綁定,例如:transitionend、DOMContentLoaded…
window.addEventListener('load', function () {
		// 所有資源都加載完成觸發
		console.log('LOAD');
});
window.addEventListener('DOMContentLoaded', function () {
		// 只要DOM結構加載完就會觸發
		console.log('DOMContentLoaded');
});

// $(document).ready(function(){})
$(function () {
    // JQ 中的這個處理(DOM 結構加載完觸發)採用的就是 DOMContentLoaded 事件,並且依託 DOM2 事件綁
    // 定來處理,所以同一個頁面中,此操作可以被使用多次
});
$(function () {

}); */
// JQ 中的事件綁定採用的都是 DOM2 事件綁定,例如:on / off / one
  • window.onload VS $(document).ready()
  1. $(document).ready() 採用的是 DOM2 事件綁定,監聽的是 DOMContentLoaded 這個事件,所以只要DOM 結構加載完成就會被觸發執行,而且同一個頁面中可以使用多次(綁定不同的方法,因爲基於 DOM2 事件池綁定機制完成的)
  2. window.onload 必須等待所有資源都加載完成纔會被觸發執行,採用 DOM0 事件綁定,同一個頁面只能綁定一次(一個方法),想綁定多個也需要改爲 window.addEventListener(‘load’, function () {})DOM2 綁定方式

三、給元素的事件行爲綁定方法

給元素的事件行爲綁定方法,當事件行爲觸發方法會被執行,不僅被執行,而且還會把當前操作的相關信息傳遞給這個函數 =》事件對象

  1. 如果是鼠標操作:獲取的是 MouseEvent 類的實例 =》 鼠標事件對象
    鼠標事件對象 -> MOuseEvent.prototoye -> UIEvent.prototype ->Event.prototype -> Object.prototype
  2. 如果是鍵盤操作:獲取的是KeyboardEvent類的實例 =》 鍵盤事件對象
  3. 除了以上還有:普通事件對象(Event)、手指事件對象(TouchEvent)等

事件對象

box.onclick = function (ev) {
		// 鼠標事件對象
		// clientX / clientY:當前鼠標觸發點距離當前窗口左上角的X / Y軸座標
		// pageX / pageY:觸發點距離當前頁面左上角的X / Y軸座標
		// type:觸發事件的類型
		// target:事件源(操作的是哪個元素,哪個元素就是事件源),在不兼容的瀏覽器中可以使用srcElement
  獲取,也代表的是事件源
		// preventDefault():用來阻止默認行爲的方法,不兼容的瀏覽器中用 ev.returnValue = false 也可以
  阻止默認行爲
		// stopPropagation():阻止冒泡傳播,不兼容的瀏覽器中用 ev.cancelBubble = true 也可以阻止默認
  行爲
		console.log(ev);
} 

四、當事件觸發時瀏覽器會做什麼

事件對象和函數以及給誰綁定的事件沒啥必然關係,它存儲的是當前本次操作的相關信息,操作一次只能有一份信息,所以在那個方法中獲取的信息都是一樣的,第二次操作,存儲的信息會把上一次操作存儲的信息替換掉…

每一次事件觸發,瀏覽器都會這樣處理一下:

  1. 捕獲到當前操作的行爲(把操作信息獲取到),通過創建 MouseEvent 等類的實例,得到事件對象 EV
  2. 通知所有綁定的方法(符和執行條件的)開始執行,並且把 EV 當作實參傳遞給每個方法,在每個方法中得到事件對象
  3. 後面再重新觸發這個事件行爲,會重新獲取本次操作的信息,用新的信息替換老的信息,然後繼續之前的步驟
let obj = null;
    box.addEventListener('click', function (ev) {
    console.log(ev);
    obj = ev;
});
box.addEventListener('click', function (ev) {
    console.log(ev === obj); // true
});
document.body.onclick = function (ev) {
    console.log(ev === obj); // true
}

五、事件傳播機制

事件的傳播機制:

  1. 捕獲階段:從最外層向最裏層事件源依次進行查找(目的:是爲冒泡階段事先計算好傳播的層級路徑) CAPTURING_PHASE
  2. 目標階段:當前元素的相關事件行爲觸發 AT_TARGET
  3. 冒泡傳播:觸發當前元素的某一個事件行爲,不僅它的這個行爲被觸發了,而且它所有的祖先元素(一直到window)相關的事件行爲都會被依次觸發(從內到外的順序) BUBBLING_PHASE
let $ = selector => document.querySelector(selector);

let inner = $('.inner');
let outer = $('.outer');

inner.onclick = function (e) {
  console.log('inner 的click事件觸發了');
};
outer.onclick = function (e) {
  console.log('outer 的click事件觸發了');
  e.stopPropagation(); // 阻止事件冒泡
};
document.body.onclick = function () {
  console.log('body 的click事件被觸發了');
};
document.onclick = function () {
  console.log('document 的click事件觸發了');
};

事件冒泡

事件冒泡:我們點擊 inner,inner 的父級元素 outer 的點擊事件以及整個文檔頂端的 document 的點擊事件都會被依次觸發。這種從低層級的 html 元素向高層級依次觸發事件的現象稱爲事件冒泡;
取消冒泡:e.stopPropagation() 阻止事件冒泡;
IE 的阻止冒泡: e.cancelBubble = true;


在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章