JavaScript的addEventListener方法是什么?如何使用?

JavaScript的addEventlistener方法是現(xiàn)代web開發(fā)中為dom元素添加事件監(jiān)聽器的核心機制,它允許指定事件觸發(fā)時執(zhí)行的函數(shù),并相比舊的onclick等屬性提供了更強大和靈活的控制。與舊方法不同,addeventlistener支持為同一事件類型添加多個監(jiān)聽器,且不會相互覆蓋;它還提供對事件流(捕獲與冒泡階段)的精細控制,并可通過options參數(shù)實現(xiàn)once(只觸發(fā)一次)、passive(優(yōu)化滾動性能)、signal(通過abortcontroller取消監(jiān)聽)等高級功能。此外,使用removeeventlistener移除監(jiān)聽器時,必須引用最初綁定的具名函數(shù),匿名函數(shù)無法被直接移除,但可通過設置once: true實現(xiàn)自動解除綁定。

JavaScript的addEventListener方法是什么?如何使用?

JavaScript的addEventListener方法是現(xiàn)代Web開發(fā)中用來為DOM元素添加事件監(jiān)聽器的核心機制。它允許你指定當特定事件(比如點擊、鼠標移動、鍵盤輸入等)發(fā)生時要執(zhí)行的函數(shù),并且相比于舊的onclick等屬性,它提供了更強大的功能和更靈活的控制。簡單來說,它讓你的網(wǎng)頁能夠“聽懂”用戶的各種操作,并作出相應的響應。

JavaScript的addEventListener方法是什么?如何使用?

解決方案

addEventListener方法是連接DOM元素和事件處理函數(shù)的橋梁。它的基本用法是:

target.addEventListener(type, listener, [options]);
  • target: 這是你要監(jiān)聽事件的DOM元素,比如一個按鈕、一個div、document甚至window對象。
  • type: 一個字符串,表示要監(jiān)聽的事件類型,例如 ‘click’、’mouseover’、’keydown’、’load’等等。注意,這里不帶on前綴。
  • listener: 這是一個函數(shù),當指定的事件發(fā)生時,這個函數(shù)就會被調(diào)用。它通常會接收一個Event對象作為參數(shù),這個對象包含了事件的詳細信息。
  • options (可選): 這是一個對象,用于配置事件監(jiān)聽器的行為,比如是否在捕獲階段觸發(fā)、是否只觸發(fā)一次、是否是“被動”事件等。后面我們會詳細聊聊這個。

舉個例子,假設你有一個按鈕,你想在用戶點擊它的時候在控制臺打印一條消息:

JavaScript的addEventListener方法是什么?如何使用?

<button id="myButton">點擊我</button>
// 獲取按鈕元素 const myButton = document.getElementById('myButton');  // 添加一個點擊事件監(jiān)聽器 myButton.addEventListener('click', function() {     console.log('按鈕被點擊了!這是一個匿名函數(shù)。');     // 你可以在這里執(zhí)行任何你希望的操作,比如修改元素樣式、發(fā)起網(wǎng)絡請求等 });  // 也可以使用具名函數(shù),這樣方便后續(xù)移除 function handleButtonClick(event) {     console.log('按鈕再次被點擊!這是具名函數(shù)。');     console.log('事件類型:', event.type);     console.log('點擊的元素:', event.target); }  myButton.addEventListener('click', handleButtonClick);  // 甚至可以是箭頭函數(shù),寫法更簡潔 myButton.addEventListener('mouseover', (event) => {     event.target.style.backgroundColor = 'lightblue'; });  myButton.addEventListener('mouseout', (event) => {     event.target.style.backgroundColor = ''; // 恢復背景色 });

addEventListener的強大之處在于,你可以為同一個元素、同一個事件類型添加多個不同的監(jiān)聽器,它們都會按順序執(zhí)行,而不會互相覆蓋。這是它與舊的事件處理方式最大的不同點之一。

addEventListener與onclick等舊方法的區(qū)別在哪里?

談到事件處理,很多初學者或者維護老代碼的開發(fā)者可能會遇到onclick、onmouseover這類直接賦值給DOM元素屬性的方式。在我看來,addEventListener和這些老舊方法之間,簡直是現(xiàn)代與傳統(tǒng)的巨大鴻溝,而這個鴻溝帶來的影響,遠不止是語法上的差異。

JavaScript的addEventListener方法是什么?如何使用?

最核心的區(qū)別,也是最容易導致問題的,就是事件處理函數(shù)的覆蓋性。當你使用element.onclick = functionA;然后又寫了element.onclick = functionB;,那么functionA就會被無情地覆蓋掉,只有functionB會在事件發(fā)生時執(zhí)行。這在團隊協(xié)作或者模塊化開發(fā)中是災難性的,因為不同的腳本或者組件可能會不經(jīng)意間覆蓋掉彼此的事件處理邏輯,導致難以追蹤的bug。

比如,你寫了一個通用的點擊統(tǒng)計腳本:

document.getElementById('myButton').onclick = function() {     console.log('統(tǒng)計點擊次數(shù)...'); };

然后你的同事又寫了一個業(yè)務邏輯腳本:

document.getElementById('myButton').onclick = function() {     console.log('執(zhí)行業(yè)務邏輯...'); };

結果就是,只有“執(zhí)行業(yè)務邏輯”會打印,你的點擊統(tǒng)計功能就失效了。這在我的開發(fā)經(jīng)歷中,是早期調(diào)試時非常頭疼的問題。

而addEventListener則完全不同。它允許你為同一個元素、同一個事件類型添加多個監(jiān)聽器,它們會按照添加的順序依次執(zhí)行,互不干擾。

const myButton = document.getElementById('myButton');  myButton.addEventListener('click', function() {     console.log('統(tǒng)計點擊次數(shù)...'); });  myButton.addEventListener('click', function() {     console.log('執(zhí)行業(yè)務邏輯...'); }); // 兩個函數(shù)都會在點擊時執(zhí)行

除了這個本質(zhì)區(qū)別,addEventListener還提供了對事件流(Event Flow)更精細的控制。它支持第三個參數(shù)(或者options對象中的capture屬性),讓你能夠選擇在事件的“捕獲階段”還是“冒泡階段”處理事件。而onclick等屬性只能在冒泡階段工作。這種控制對于事件委托(Event Delegation)和處理復雜ui交互邏輯至關重要。

addEventListener還支持更高級的選項,比如once(事件只觸發(fā)一次)、passive(優(yōu)化滾動性能)和signal(通過AbortController取消監(jiān)聽),這些都是舊方法望塵莫及的??梢哉f,addEventListener是現(xiàn)代JavaScript事件處理的基石,它讓代碼更健壯、更靈活、更易于維護。如果你還在用onclick,是時候徹底擁抱addEventListener了。

如何移除通過addEventListener綁定的事件監(jiān)聽器?

事件監(jiān)聽器并不是永遠都需要存在的。有時候,一個事件只需要觸發(fā)一次,或者在某個條件滿足后就不再需要監(jiān)聽了。如果不及時移除不再需要的監(jiān)聽器,可能會導致內(nèi)存泄漏(尤其是當監(jiān)聽器引用了大量數(shù)據(jù)或DOM元素,而這些元素本身已經(jīng)被移除但監(jiān)聽器還在),或者產(chǎn)生意外的行為。

移除addEventListener綁定的事件監(jiān)聽器,你需要使用removeEventListener方法。它的語法和addEventListener非常相似:

target.removeEventListener(type, listener, [options]);

這里的關鍵點在于,listener參數(shù)必須是對你最初添加到addEventListener的那個函數(shù)引用。如果你嘗試移除一個匿名函數(shù),即使它們的定義看起來一模一樣,也無法成功,因為它們在內(nèi)存中是不同的函數(shù)實例。

一個成功的移除示例:

<button id="removableButton">點擊我,但很快就會失效</button>
const removableButton = document.getElementById('removableButton');  // 定義一個具名函數(shù)作為事件監(jiān)聽器 function handleClickOnce() {     console.log('這個按鈕被點擊了!');     // 在第一次點擊后就移除自身     removableButton.removeEventListener('click', handleClickOnce);     console.log('點擊監(jiān)聽器已移除,再次點擊將不再有反應。'); }  // 添加監(jiān)聽器 removableButton.addEventListener('click', handleClickOnce);  // 另一個例子:在一段時間后移除監(jiān)聽器 function handleMouseOverMessage() {     console.log('鼠標移到按鈕上了!'); }  removableButton.addEventListener('mouseover', handleMouseOverMessage);  // 5秒后自動移除鼠標移入監(jiān)聽器 setTimeout(() => {     removableButton.removeEventListener('mouseover', handleMouseOverMessage);     console.log('鼠標移入監(jiān)聽器已在5秒后移除。'); }, 5000);

失敗的移除示例(匿名函數(shù))

const anotherButton = document.getElementById('anotherButton');  // 添加一個匿名函數(shù)作為監(jiān)聽器 anotherButton.addEventListener('click', function() {     console.log('這個匿名函數(shù)無法被直接移除。'); });  // 嘗試移除這個匿名函數(shù),這行代碼是無效的! // anotherButton.removeEventListener('click', function() { /* 這是另一個函數(shù)實例 */ });

如果你確實需要讓一個匿名函數(shù)只執(zhí)行一次,最優(yōu)雅的方式不是手動移除它,而是在addEventListener的options參數(shù)中使用once: true。

const oneTimeButton = document.getElementById('oneTimeButton'); oneTimeButton.addEventListener('click', () => {     console.log('我只會執(zhí)行一次,然后就會自動被移除。'); }, { once: true }); // 設置 once 為 true

在我看來,強制使用函數(shù)引用來移除監(jiān)聽器,其實是在提醒開發(fā)者要更嚴謹?shù)毓芾硎录芷凇_@促使我們思考事件處理函數(shù)的復用性和可管理性,而不是隨意創(chuàng)建大量一次性的匿名函數(shù)。

addEventListener中的options參數(shù)有哪些高級用法?

addEventListener的第三個參數(shù),那個可選的options對象,是它真正展現(xiàn)其高級能力的地方。通過這個對象,我們可以對事件的行為進行非常精細的控制,這對于構建高性能、響應迅速且資源管理得當?shù)腤eb應用至關重要。

這里我們主要關注幾個常用的屬性:capture、once、passive和signal。

1. capture (布爾值)

  • 默認值: false
  • 作用: 控制事件是在捕獲階段還是冒泡階段被處理。
    • false (或省略): 監(jiān)聽器在冒泡階段觸發(fā)。事件從目標元素向上冒泡到document。
    • true: 監(jiān)聽器在捕獲階段觸發(fā)。事件從window向下捕獲到目標元素。

理解事件流對于事件委托和阻止事件傳播非常重要。想象一下,你點擊了一個嵌套在多個div里的按鈕。如果capture為true,那么從window到document,再到父div,最后到按鈕,事件會依次觸發(fā)捕獲階段的監(jiān)聽器。然后,事件會從按鈕冒泡回document,依次觸發(fā)冒泡階段的監(jiān)聽器。

<div id="outer">     <div id="inner">         <button id="myBtn">點擊</button>     </div> </div>
const outer = document.getElementById('outer'); const inner = document.getElementById('inner'); const myBtn = document.getElementById('myBtn');  outer.addEventListener('click', () => console.log('Outer (冒泡)'), false); // 默認就是false inner.addEventListener('click', () => console.log('Inner (冒泡)'), false); myBtn.addEventListener('click', () => console.log('Button (冒泡)'), false);  outer.addEventListener('click', () => console.log('Outer (捕獲)'), { capture: true }); inner.addEventListener('click', () => console.log('Inner (捕獲)'), { capture: true }); myBtn.addEventListener('click', () => console.log('Button (捕獲)'), { capture: true });  // 當點擊 'myBtn' 時,輸出順序會是: // Outer (捕獲) // Inner (捕獲) // Button (捕獲)  // 如果 myBtn 自身也有捕獲監(jiān)聽器,會在這里觸發(fā) // Button (冒泡) // Inner (冒泡) // Outer (冒泡)

注意:同一個元素上的同一事件類型,捕獲階段的監(jiān)聽器總是比冒泡階段的先觸發(fā)。如果像myBtn這樣既有捕獲又有冒泡監(jiān)聽器,它自身的捕獲監(jiān)聽器會在捕獲階段被觸發(fā),然后事件到達目標,再開始冒泡,觸發(fā)它自身的冒泡監(jiān)聽器。這里為了簡化演示,我把myBtn的捕獲監(jiān)聽器省略了,但實際情況是這樣。

2. once (布爾值)

  • 默認值: false
  • 作用: 如果設置為true,事件監(jiān)聽器在被調(diào)用一次后會自動移除。這對于只需要執(zhí)行一次的初始化操作或提示非常有用,避免了手動調(diào)用removeEventListener。
const welcomeMessageBtn = document.getElementById('welcomeMessageBtn'); // 假設有這個按鈕  welcomeMessageBtn.addEventListener('click', () => {     alert('歡迎來到我們的網(wǎng)站!這條消息只會顯示一次。'); }, { once: true }); // 只觸發(fā)一次

這比前面手動移除匿名函數(shù)的例子要優(yōu)雅和簡潔得多。

3. passive (布爾值)

  • 默認值: false
  • 作用: 這個選項是性能優(yōu)化的利器,尤其是在移動設備上處理滾動和觸摸事件時。如果設置為true,它告訴瀏覽器:這個事件監(jiān)聽器不會調(diào)用preventDefault()來阻止事件的默認行為。 為什么這很重要?對于像wheel、touchstart、touchmove這樣的事件,瀏覽器通常需要等待你的事件監(jiān)聽器執(zhí)行完畢,才能確定你是否調(diào)用了preventDefault()來阻止?jié)L動或縮放等默認行為。如果監(jiān)聽器執(zhí)行時間較長,這就會導致頁面滾動卡頓,用戶體驗下降。 設置passive: true后,瀏覽器會立即執(zhí)行默認行為(比如滾動),而無需等待你的監(jiān)聽器完成,從而顯著提升滾動流暢度。
document.addEventListener('wheel', (event) => {     // 如果 passive 為 true,即便你寫了這行,event.preventDefault() 也不會生效,并且瀏覽器會給出警告。     // event.preventDefault();     console.log('滾輪事件觸發(fā)'); }, { passive: true }); // 告訴瀏覽器:這個監(jiān)聽器不會阻止?jié)L動

重要提示: 只有當你確定你的監(jiān)聽器不會阻止默認行為時才使用passive: true。否則,如果你在passive: true的監(jiān)聽器中調(diào)用了preventDefault(),它會被忽略,并且控制臺會報錯。

4. signal (AbortSignal 對象)

  • 默認值: undefined
  • 作用: signal是AbortController API的一部分,它提供了一種機制來取消一個或多個DOM請求或事件監(jiān)聽器。當你將一個AbortSignal對象傳遞給addEventListener的signal選項時,當該signal被abort()時,對應的事件監(jiān)聽器就會被自動移除。這對于管理復雜組件的生命周期,或者需要批量取消

? 版權聲明
THE END
喜歡就支持一下吧
點贊12 分享