詳解JavaScript 事件流
事件
HTML中與javascript互動是通過事件驅動來實現的,例如滑鼠點選事件、頁面的滾動事件onscroll等等,可以向文件或者文件中的元素新增事件偵聽器來預訂事件。想要知道這些事件是在什麼時候進行呼叫的,就需要了解一下“事件流”的概念。
事件流
事件流描述的就是從頁面中接收事件的順序。而早期的IE和Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而Netscape的事件流就是事件捕獲。
事件流類別
事件冒泡
即從下至上,從目標觸發的元素逐級向上傳播,直到window物件。
事件捕獲
即從上至下,從document逐級向下傳播到目標元素。
後來ECMAScript在DOM2中對事件流進行了進一步規範,基本上就是上述二者的結合。
DOM2級事件規定的事件流包括三個階段:
- 事件捕獲階段
- 處於目標階段
- 事件冒泡階段
注意⚠️:先捕獲後冒泡,但是在目標節點上誰寫在前面誰先執行。但是在目標元素上不區分冒泡還是捕獲,按繫結的順序來執行。
DOM事件級別
分為四個級別
DOM0:不是W3C規範。
DOM1:開始是W3C規範。專注於HTML文件和XML文件。
DOM2:對DOM1增加了樣式表物件模型
DOM3:對DOM2增加了內容模型 (DTD 、Schemas) 和文件驗證。
DOM0級
DOM0級事件具有極好的跨瀏覽器優勢,會以最快的速度繫結。繫結方式有如下兩種
行內繫結(內聯模型)
將函式名直接作為html標籤中屬性的屬性值。
<div onclick="btnClick()">按鈕</div> <script> function btnClick(){ console.log("hello"); } </script>
動態繫結(指令碼模型)
通過在JS中選中某個節點,然後給節點新增onclick屬性
<div id="btn">按鈕</div> <script> var btn = document.getElementById("btn"); btn.onclick = function(){ console.log("點選"); } </script>
注意⚠️
- DOM0級同一個節點只能新增一次同類型事件,後新增的同類型事件會覆蓋前面的事件
- DOM0級只支援冒泡
DOM1級
其中DOM1級事件處理標準中並沒有定義事件相關的內容,所以沒有所謂的DOM1事件處理
DOM2級
DOM2級定義了兩個事件處理程式。(觀察者模式)
- addEventListener() ---新增事件偵聽器
- removeEventListener() ---刪除事件偵聽器
函式均有3個引數, 第一個引數是要處理的事件名 第二個引數是作為事件處理程式的函式 第三個引數是一個boolean值,預設false表示使用冒泡機制,true表示捕獲機制。
<div id="btn">按鈕</div> <script> var btn=document.getElementById("btn"); btn.addEventListener("click",hello,false); btn.addEventListener("click",helloagain,false); function hello(){ console.log("hello"); } function helloagain(){ console.log("hello again"); } </script> // 點選後結果: // hello // hello again
注意⚠️
如果定義了一模一樣的監聽方法時,是會發生覆蓋的。
<div id="btn">點選</div> <script> var btn=document.getElementById("btn"); btn.addEventListener("click",false); function hello(){ console.log("hello"); } </script> // 點選後結果: // hello
DOM3級
對DOM2增加了內容模型 (DTD 、Schemas) 和文件驗證。定義了一些新的事件,比如鍵盤事件,還可以自定義事件。
自定義事件
自定義事件不是由DOM原生觸發的,它的目的是讓開發人員建立自己的事件。要建立的自定義事件可以由createEvent("CustomEvent"); 返回的物件有一個initCustomEvent()方法接收如下四個引數。
- type:字串,觸發的事件型別,自定義。例如 “keyDown”,“selectedChange”;
- bubble(布林值):標示事件是否應該冒泡;
- cancelable(布林值):標示事件是否可以取消;
- detail(物件):任意值,儲存在event物件的detail屬性中;
可以像分配其他事件一樣在DOM中分派建立的自定義事件物件。如:
var div = document.getElementById("myDiv"); EventUtil.addEventHandler(div,"myEvent",function () { alert("div myEvent!"); }); EventUtil.addEventHandler(document,function(){ alert("document myEvent!"); }); if(document.implementation.hasFeature("CustomEvents","3.0")){ var e = document.createEvent("CustomEvent"); e.initCustomEvent("myEvent",true,false,"hello world!"); div.dispatchEvent(e); }
這個例子中建立了一個冒泡事件“myEvent”。而event.detail的值被設定成了一個簡單的字串,然後在div和document上偵聽該事件,因為在initCustomEvent中設定了事件冒泡。所以當div激發該事件時,瀏覽器會將該事件冒泡到document。
阻止冒泡
stopPropagation函式
btn.addEventListener('click',function(ev){ ev.stopPropagation(); console.log('阻止冒泡') },false)
事件委託(事件代理)
原理
如果有多個DOM節點需要監聽事件的情況下,給每個DOM繫結監聽函式,會極大的影響頁面的效能,因為我們通過事件委託來進行優化,事件委託利用的就是冒泡的原理。
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <script> var li_list = document.getElementsByTagName('li') for(let index = 0;index<li_list.length;index++){ li_list[index].addEventListener('click',function(ev){ console.log(ev.currentTarget.innerHTML) }) } </script>
正常情況我們給每一個li都會繫結一個事件,但是如果這時候li是動態渲染的,資料又特別大的時候,每次渲染後(有新增的情況)我們還需要重新來繫結,又繁瑣又耗效能;這時候我們可以將繫結事件委託到li的父級元素,即ul。
var ul_dom = document.getElementsByTagName('ul') ul_dom[0].addEventListener('click',function(ev){ console.log(ev.target.innerHTML) })
target和currentTarget區別:
- target返回觸發事件的元素,不一定是繫結事件的元素
- currentTarget返回的是繫結事件的元素
優點
- 提高效能: 每一個函式都會佔用記憶體空間,只需新增一個事件處理程式代理所有事件,所佔用的記憶體空間更少。
- 動態監聽: 使用事件委託可以自動繫結動態新增的元素,即新增的節點不需要主動新增也可以一樣具有和其他元素一樣的事件。
以上就是詳解JavaScript 事件流的詳細內容,更多關於JavaScript 事件流的資料請關注我們其它相關文章!