javascript - addEventListener 與 onclick
addEventListener 和 onclick 之間有什麼區別?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
上述程式碼一起存放在一個獨立的 .js 檔案中,並且它們都能完美運作。
答案#1
兩者都是正確的,但它們本身都不是「最佳」,並且開發者可能有理由選擇同時使用這兩種方法。
事件監聽器 (addEventListener 和 IE 的 attachEvent)
早期版本的 Internet Explorer 實作 JavaScript 的方式與幾乎所有其他瀏覽器不同。在版本低於 9 的情況下,你需要使用 attachEvent[doc] 方法,如下所示:
element.attachEvent('onclick', function() { /* do stuff here*/ });
在大多數其他瀏覽器中(包括 IE 9 及以上版本),你使用 addEventListener[doc],如下所示:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
使用這種方法(DOM Level 2 事件),你可以將理論上無限數量的事件附加到任何單一元素上。唯一的實際限制是客戶端的記憶體和其他效能問題,這些在每個瀏覽器中都不同。
以上範例表示使用匿名函數[文檔]。你也可以使用函數引用[文檔]或閉包[文檔]來添加事件監聽器:
const myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
addEventListener 的另一個重要特性是最後一個參數,它控制監聽器如何對冒泡事件作出反應[doc]。在範例中我一直傳遞 false,這對大約 95% 的使用情況來說是標準做法。attachEvent 或使用內聯事件時沒有相應的參數。
內聯事件 (HTML onclick="" 屬性和 element.onclick)
在所有支援 JavaScript 的瀏覽器中,你可以將事件監聽器內聯放置,也就是直接在 HTML 代碼中。你可能見過這個:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
大多數有經驗的開發者會避免使用這種方法,但它確實可以完成工作;它簡單直接。在這裡你可能無法使用閉包或匿名函數(雖然處理器本身在某種程度上是一個匿名函數),而且你對作用域的控制也有限。
你提到的另一種方法:
element.onclick = function () { /*do stuff here */ };
... 相當於內嵌的 JavaScript,只不過你對作用域有更多控制(因為你是在寫腳本而不是 HTML),並且可以使用匿名函數、函數參考或閉包。
內聯事件的主要缺點是,與上述描述的事件監聽器不同,你可能只能分配一個內聯事件。內聯事件被存儲為元素的屬性/特性,這意味著它可以被覆蓋。
使用上面 HTML 中的範例 <a>:
const element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };
... 當你點擊該元素時,你只會看到「Did stuff #2」——你用第二個值覆蓋了最初設定的 onclick 屬性,也覆蓋了原本內嵌 HTML 的 onclick 屬性。可以在這裡查看:http://jsfiddle.net/jpgah/。
廣義來說,不要使用內聯事件。可能有特定的使用情境需要使用它,但如果你不完全確定自己有這個使用情境,那麼你其實沒有,也不應該使用內聯事件。
現代 JavaScript(如 Angular 等)
自從這個答案最初發布以來,像 Angular 這樣的 JavaScript 框架已經變得更加流行。你會在 Angular 模板中看到像這樣的程式碼:
<button (click)="doSomething()">Do Something</button>
這看起來像是內聯事件,但它不是。這種類型的模板會被轉譯為更複雜的程式碼,並在背後使用事件監聽器。我在這裡寫的關於事件的所有內容仍然適用,但你至少被抽離了一層底層的細節。你應該理解這些細節,但如果你的現代 JavaScript 框架最佳實踐是讓你在模板中撰寫這類程式碼,不要覺得自己在使用內聯事件——你並沒有。
哪一個最好?
這個問題涉及瀏覽器相容性和必要性。你是否需要在一個元素上附加多個事件?你將來會需要嗎?很可能會。attachEvent 和 addEventListener 是必要的。如果不需要,內聯事件似乎也能起作用,但最好還是為未來做準備,雖然看似不太可能,但至少是可預測的。你很可能需要轉向基於 JavaScript 的事件監聽器,所以不妨從那裡開始。不要使用內聯事件。
jQuery 和其他 JavaScript 框架將不同瀏覽器對 DOM 級別 2 事件的實作封裝在通用模型中,這樣你就可以編寫跨瀏覽器兼容的程式碼,而不必擔心 IE 的反叛歷史。使用 jQuery 相同的程式碼,全部跨瀏覽器並且隨時可用:
$(element).on('click', function () { /* do stuff */ });
不過,別為了這一件事就跑去買一個框架。你可以很容易地自己寫一個小工具來處理舊版瀏覽器:
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
// example
addEvent(
document.getElementById('myElement'),
'click',
function () { alert('hi!'); }
);
試試看:http://jsfiddle.net/bmArj/
綜合考量以上所有因素,除非你正在看的腳本以某種其他方式考慮了瀏覽器差異(在你問題中未顯示的程式碼中),使用 addEventListener 的部分在 IE 9 以下的版本將無法運作。
文件與相關閱讀
- W3 HTML 規範,元素事件處理器屬性
- MDN 上的 element.addEventListener
- element.attachEvent 在 MSDN 上
- Jquery.on
- quirksmode 部落格 「事件入門」
- 由 Google 提供的 CDN 托管的 JavaScript 函式庫
答案#2
如果你有另外幾個函數,你就能看到差別:
var h = document.getElementById('a');
h.onclick = doThing_1;
h.onclick = doThing_2;
h.addEventListener('click', doThing_3);
h.addEventListener('click', doThing_4);
函數 2、3 和 4 可以運作,但函數 1 無法。這是因為 addEventListener 不會覆蓋現有的事件處理程序,而 onclick 會覆蓋任何現有的 onclick=fn 事件處理程序。
另一個顯著的差異,當然是 onclick 總是有效,而 addEventListener 在 9 版之前的 Internet Explorer 中無效。你可以在 IE <9 中使用相似的 attachEvent(其語法有 稍微 不同)。
答案#3
在這個回答中,我將描述定義 DOM 事件處理程序的三種方法。
element.addEventListener()
程式範例:
const element = document.querySelector('a');
element.addEventListener('click', event => event.preventDefault(), true);<a href="//google.com">Try clicking this link.</a>element.addEventListener() 有多個優點:
- 允許你註冊 無限 的事件處理器,並可使用
element.removeEventListener()將它們移除。 - 具有
useCapture參數,該參數表示您是否希望在事件的 捕獲階段或冒泡階段 處理事件。參見:無法理解 addEventListener 中的 useCapture 屬性。 - 關心 語義。基本上,它使註冊事件處理程序變得更明確。對於初學者來說,函數呼叫可以清楚地表明 某些事情發生了,而將事件分配給 DOM 元素的某個屬性至少不直觀。
- 允許你將文件結構(HTML)和邏輯(JavaScript)分開。在小型網頁應用中,這似乎可能無關緊要,但在任何大型專案中,它確實很重要。維護一個分離結構和邏輯的專案比維護一個未分離的專案要容易得多。
- 使用正確的事件名稱可以消除混淆。由於使用內聯事件監聽器或將事件監聽器指派給 DOM 元素的
.onevent屬性,很多缺乏經驗的 JavaScript 程式設計師認為事件名稱例如是onclick或onload。on並不是事件名稱的一部分。正確的事件名稱是click和load,這也是事件名稱傳遞給.addEventListener()的方式。 - 幾乎適用於所有瀏覽器。如果你仍然需要支援 IE <= 8,你可以使用來自 MDN 的 polyfill。
element.onevent=function() {}(例如 onclick、onload)
程式範例:
const element = document.querySelector('a');
element.onclick = event => event.preventDefault();<a href="//google.com">Try clicking this link.</a>這是一種在 DOM 0 中註冊事件處理器的方法。現在不鼓勵使用它,因為它:
- 允許你註冊僅一個事件處理器。此外,移除已指派的處理器並不直觀,因為要移除使用此方法指派的事件處理器,你必須將
onevent屬性恢復到其初始狀態(即null)。 - 不會對錯誤作出適當的回應。例如,如果你不小心將一個字串指定給
window.onload,例如:window.onload="test";,它不會拋出任何錯誤。你的程式碼將無法運行,並且很難找出原因。然而,.addEventListener()會拋出錯誤(至少在 Firefox 上):TypeError: Argument 2 of EventTarget.addEventListener is not an object。 - 沒有提供選擇是否在捕獲階段或冒泡階段處理事件的方法。
內嵌事件處理器(onevent HTML 屬性)
程式範例:
<a href="//google.com" onclick="event.preventDefault();">Try clicking this link.</a>與 element.onevent 類似,現在不建議使用它。除了 element.onevent 所有的問題之外,它還有:
- 是一個潛在的安全問題,因為它會使 XSS 更具危害性。現今網站應該傳送正確的
Content-Security-PolicyHTTP 標頭,以阻止內嵌腳本並僅允許來自受信任域的外部腳本。參見 Content Security Policy 如何運作? - 不會分離文件結構和邏輯。
- 如果你使用伺服器端腳本生成你的網頁,例如你生成了一百個連結,每個連結都有相同的內聯事件處理器,你的程式碼將會比事件處理器只定義一次時長得多。這意味著客戶端需要下載更多內容,因此你的網站會變得更慢。
另見
EventTarget.addEventListener()文件說明 (MDN)EventTarget.removeEventListener()文件說明 (MDN)- onclick 與 addEventListener
- dom-events 標籤維基
答案#4
雖然 onclick 在所有瀏覽器中都能使用,但 addEventListener 在舊版本的 Internet Explorer 中無法使用,舊版本使用 attachEvent 取而代之。
onclick 的缺點是只能有一個事件處理器,而其他兩個則會觸發所有已註冊的回調。
答案#5
摘要:
addEventListener可以添加多個事件,而使用onclick則無法做到這一點。onclick可以作為HTML屬性添加,而addEventListener則只能在<script>元素中添加。- `
addEventListener` 可以接受第三個參數,這個參數可以阻止事件傳播。
兩者都可以用來處理事件。然而,應該優先選擇 addEventListener,因為它可以完成 onclick 的所有功能,甚至更多。不要將 onclick 作為 HTML 屬性內聯使用,因為這會將 JavaScript 與 HTML 混合,這是一種不良的做法。它會使代碼的可維護性降低。
答案#6
據我所知,DOM 的「load」事件仍然只能有限地運作。這意味著它只會針對 window 物件、圖片 和 <script> 元素觸發。同樣,直接的 onload 指派也是如此。這兩者之間在技術上沒有差異。可能 .onload = 在跨瀏覽器可用性上更好。
然而,你無法將 load 事件 指派給 <div> 或 <span> 元素等。
