在本文中將閱讀到的內容有:
- 什么是事件
- 事件監聽
- 事件冒泡 事件捕獲
- 事件物件
- 事件代理
- 事件發布訂閱
1. 什么是事件
在介紹事件冒泡捕獲等理論之前,我們首先要明確什么是事件,
事件是您在編程時系統內發生的動作或者發生的事情——系統會在事件出現時產生或觸發某種信號,并且會提供一個自動加載某種動作(列如:運行一些代碼)的機制,
在 Web 中, 事件在瀏覽器視窗中被觸發并且通常被系結到視窗內部的特定部分 — 可能是一個元素、一系列元素、被加載到這個視窗的 HTML 代碼或者是整個瀏覽器視窗,
幾個比較常見的事件舉例
- 用戶在某個元素上點擊滑鼠或懸停游標,
- 用戶在鍵盤中按下某個按鍵,
- 用戶調整瀏覽器的大小或者關閉瀏覽器視窗,
- 一個網頁停止加載,
- 提交表單,
- 播放、暫停、關閉視頻,
更多事件型別可以參考這里
2. 事件監聽
2.1 方法一
每個可用的事件都會有一個事件監聽器,監聽器留意事件是否發生,然后處理器就是對事件發生做出的回應,
舉例說明:
假設界面上有一個
<button>Change color</button>
const btn = document.querySelector('button');
function random(number) {
return Math.floor(Math.random()*(number+1));
}
btn.onclick = function() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
我們使用 btn 變數存盤 button,并使用了Document.querySelector() 函式,我們也定義了一個回傳亂數字的函式,代碼第三部分就是事件處理器,btn變數指向 button 元素,在 button 這種物件上可觸發一系列的事件,因此也就可以使用事件處理器,我們通過將一個匿名函式(這個賦值函式包括生成隨機色并賦值給背景色的代碼)賦值給“點擊”事件處理器引數,監聽“點擊”這個事件,
只要點擊事件在元素上觸發,該段代碼就會被執行,即每當用戶點擊它時,都會運行此段代碼
這個btn.onclick還有許多
- btn.onfocus及btn.onblur —顏色將于按鈕被置于焦點或解除焦點時改變(嘗試使用Tab移動至按鈕上,然后再移開),這些通常用于顯示有關如何在置于焦點時填寫表單欄位的資訊,或者如果表單欄位剛剛填入不正確的值,則顯示錯誤訊息,
- btn.ondblclick — 顏色將僅于按鈕被雙擊時改變, window.onkeypress, window.onkeydown,
- window.onkeyup — 當按鈕被按下時顏色會發生改變. keypress 指的是通俗意義上的按下按鈕 (按下并松開), 而keydown 和 keyup 指的是按鍵動作的一部分,分別指按下和松開. 注意如果你將事件處理器添加到按鈕本身,它將不會作業 —我們只能將它添加到代表整個瀏覽器視窗的 window物件中,
- btn.onmouseover 和 btn.onmouseout —顏色將會在滑鼠移入按鈕上方時發生改變, 或者當它從按鈕移出時. 一些事件非常通用,幾乎在任何地方都可以用(比如 onclick 幾乎可以用在幾乎每一個元素上),然而另一些元素就只能在特定場景下使用,比如我們只能在 video 元素上使用 onplay ,
2.2 方法二 addEventListener() 和removeEventListener()
2.2.1 基本使用方法
新的事件觸發機制被定義在 Document Object Model (DOM) Level 2 Events Specification, 這個細則給瀏覽器提供了一個函式 — addEventListener(),這個函式和事件處理屬性是類似的,但是語法略有不同,我們可以重寫上面的隨機顏色背景代碼:
const btn = document.querySelector('button');
function bgChange() {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
document.body.style.backgroundColor = rndCol;
}
btn.addEventListener('click', bgChange);
//清除不使用的事件處理器
btn.removeEventListener('click', bgChange);
您也可以給同一個監聽器注冊多個處理器,下面這種方式不能實作這一點:
myElement.onclick = functionA;
myElement.onclick = functionB;
第二行會覆寫第一行,但是下面這種方式就會正常作業了:
myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);
當元素被點擊時兩個函式都會作業,
2.2.2 addEventListener() 詳解
EventTarget.addEventListener() 方法將指定的監聽器注冊到 EventTarget 上,當該物件觸發指定的事件時,指定的回呼函式就會被執行, 事件目標可以是一個檔案上的元素 Element,Document和Window或者任何其他支持事件的物件 (比如 XMLHttpRequest),
語法:
target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
引數說明:
type 表示監聽事件型別的字串(詳細事件型別參考)
listener 當所監聽的事件型別觸發時,會接收到一個事件通知(實作了 Event 介面的物件)物件,listener 必須是一個實作了 EventListener 介面的物件,或者是一個函式,
options 可選 一個指定有關 listener 屬性的可選引數物件,可用的選項如下:
- capture: Boolean,表示 listener 會在該型別的事件捕獲階段傳播到該 EventTarget 時觸發,
- once: Boolean,表示 listener 在添加之后最多只呼叫一次,如果是 true, listener 會在其被呼叫之后自動移除,
- passive: Boolean,設定為true時,表示 listener 永遠不會呼叫 preventDefault(),如果 listener 仍然呼叫了這個函式,客戶端將會忽略它并拋出一個控制臺警告,查看 使用 passive 改善的滾屏性能 了解更多.
- signal:AbortSignal,該 AbortSignal 的 abort() 方法被呼叫時,監聽器會被移除,
useCapture 可選 Boolean,在DOM樹中,注冊了listener的元素, 是否要先于它下面的EventTarget,呼叫該listener, 當useCapture(設為true) 時,沿著DOM樹向上冒泡的事件,不會觸發listener,當一個元素嵌套了另一個元素,并且兩個元素都對同一事件注冊了一個處理函式時,所發生的事件冒泡和事件捕獲是兩種不同的事件傳播方式,事件傳播模式決定了元素以哪個順序接收事件, 如果沒有指定, useCapture 默認為 false ,
注意:
對于事件目標上的事件監聽器來說,事件會處于“目標階段”,而不是冒泡階段或者捕獲階段,在目標階段的事件會觸發該元素(即事件目標)上的所有監聽器,而不在乎這個監聽器到底在注冊時useCapture
引數值是true還是false,
使用這些引數的更多列子參考這里
3. 事件冒泡 事件捕獲
在頁面中點擊一個元素,事件是從這個元素的祖先元素中逐層傳遞下來的,這個階段為事件的捕獲階段,當事件傳遞到這個元素之后,又會把事件逐成傳遞回去,直到根元素為止,這個階段是事件的冒泡階段,
事件捕獲階段:

事件冒泡階段:

4. 事件物件
有時候在事件處理函式內部,您可能會看到一個固定指定名稱的引數,例如event,evt或簡單的e, 這被稱為事件物件,它被自動傳遞給事件處理函式,以提供額外的功能和資訊, 例如,讓我們稍稍重寫一遍我們的隨機顏色示例:
function bgChange(e) {
const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
e.target.style.backgroundColor = rndCol;
console.log(e);
}
btn.addEventListener('click', bgChange);
event.target vs event.currentTarget
舉例說明這倆者的區別:
<div id="a">
<div id="b">
<div id="c">
<div id="d"></div>
</div>
</div>
</div>
<script>
document.getElementById('a').addEventListener('click', function(e) {
console.log('target:' + e.target.id + '¤tTarget:' + e.currentTarget.id);
});
document.getElementById('b').addEventListener('click', function(e) {
console.log('target:' + e.target.id + '¤tTarget:' + e.currentTarget.id);
});
document.getElementById('c').addEventListener('click', function(e) {
console.log('target:' + e.target.id + '¤tTarget:' + e.currentTarget.id);
});
document.getElementById('d').addEventListener('click', function(e) {
console.log('target:' + e.target.id + '¤tTarget:' + e.currentTarget.id);
});
</script>
上面事件的系結都是在冒泡階段的(因為addEventListener useCapture默認為false,代表在冒泡階段系結),當我們點擊最里層的元素d的時候,會依次輸出:
target:d¤tTarget:d
target:d¤tTarget:c
target:d¤tTarget:b
target:d¤tTarget:a
從輸出中我們可以看到,event.target指向引起觸發事件的元素,而event.currentTarget則是事件系結的元素,只有被點擊的那個目標元素的event.target才會等于event.currentTarget,
如果我們把事件都系結在捕獲階段,然后還是點擊最里層的元素d,這時event.target還依舊會指向d,因為那是引起事件觸發的元素,因為event.currentTaget指向事件系結的元素,所以在捕獲階段,最先來到的元素是a,然后是b,接著是c,最后是d,所以輸出的內容如下:
target:d¤tTarget:a
target:d¤tTarget:b
target:d¤tTarget:c
target:d¤tTarget:d
preventDefault()
有時,你會遇到一些情況,你希望事件不執行它的默認行為, 最常見的例子是Web表單,例如自定義注冊表單, 當你填寫詳細資訊并按提交按鈕時,自然行為是將資料提交到服務器上的指定頁面進行處理,并將瀏覽器重定向到某種“成功訊息”頁面(或 相同的頁面,如果另一個沒有指定,)
使用preventDefault()可以阻止默認行為,
stopPropagation()
準事件物件具有可用的名為 stopPropagation()的函式, 當在事件物件上呼叫該函式時,它只會讓當前事件處理程式運行,但事件不會在冒泡鏈上進一步擴大,因此將不會有更多事件處理器被運行(不會向上冒泡),
5. 事件代理(委托)
冒泡還允許我們利用事件委托——這個概念依賴于這樣一個事實,如果你想要在大量子元素中單擊任何一個都可以運行一段代碼,您可以將事件監聽器設定在其父節點上,并讓子節點上發生的事件冒泡到父節點上,而不是每個子節點單獨設定事件監聽器,
一個很好的例子是一系列串列項,如果你想讓每個串列項被點擊時彈出一條資訊,您可以將click單擊事件監聽器設定在父元素<ul>上,這樣事件就會從串列項冒泡到其父元素<ul>上,
事件代理的優點:
- 可以大量節省記憶體占用,減少事件注冊
- 可以實作當新增子物件時無需再次對其系結(動態系結事件)
6. 事件發布訂閱
發布-訂閱模式里面包含了三個模塊,發布者,訂閱者和處理中心,這里處理中心相當于報刊辦事大廳,發布者相當與某個雜志負責人,他來中心這注冊一個的雜志,而訂閱者相當于用戶,我在中心訂閱了這分雜志,每當發布者發布了一期雜志,辦事大廳就會通知訂閱者來拿新雜志,這樣在結合下面的圖應該很好理解了,

其實就是將發布者和訂閱者解耦了,在實際開發中,經常會遇到某個方法內處理很多的邏輯,最簡單的就是直接在方法內直接寫,這種是高度耦合的面向程序的寫法,對于代碼維護不友好,而發布-訂閱模式就是將兩者分離,我觸發了某個事件(這里我們將觸發該方法定義為事件),我只向調度中心通知,我并不知道調度中心內會怎么處理,有多少個人回應,我只管通知,而訂閱者只管在調度中心訂閱,有人呼叫它才回應,
還有一點就是假設我們有3個js檔案,事件觸發在a.js內,而回應該事件的在b.js和c.js內,要是用常規呼叫的方法的話,就要把b.js和c.js的方法傳到a.js內,這是一個非常麻煩的操作,而發布-訂閱模式是將調度中心掛在了全域,我們只管呼叫調度中心相應的方法注冊和訂閱,
參考網頁:
https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Events
https://www.cnblogs.com/yzhihao/p/9398917.html
https://www.cnblogs.com/suyuanli/p/9655699.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/310590.html
標籤:其他
上一篇:vue學習(二)
