javascript - true - passive addeventlistener
addEventListener vs onclick (9)
addEventListener
和onclick
什麼區別?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
上面的代碼一起存放在一個單獨的.js文件中,並且它們都可以很好地工作。
JavaScript傾向於將所有東西都混合到對像中,這可能會讓它變得令人困惑。 一切都是JavaScript的方式。
本質上onclick是一個HTML屬性。 相反,addEventListener是表示HTML元素的DOM對象的一種方法。
在JavaScript對像中,一個方法僅僅是一個屬性,它具有一個作為值的函數,並且對它所附著的對象(例如使用這個對象)起作用。
在JavaScript中,由DOM表示的HTML元素將其屬性映射到其屬性。
這是人們感到困惑的地方,因為JavaScript把所有東西都融合到一個沒有間接層的容器或名稱空間中。
在一個正常的OO佈局中(至少合併了屬性/方法的命名空間),你可能會有這樣的東西:
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
有各種各樣的變體,例如它可以使用onload的getter / setter或屬性的HashMap,但最終這是它的樣子。 JavaScript期望知道除了其他事情之外,JavaScript消除了這種間接性。 它將domElement和屬性合併在一起。
除了兼容性,您應該使用addEventListener作為最佳實踐。 正如其他答案所討論的那樣,在這方面的差異而不是基本的方案差異我會放棄它。 從本質上講,在一個理想的世界中,你實際上只是想從HTML中使用*,但在更理想的世界中,你不應該像HTML那樣做任何事情。
為什麼今天它佔統治地位? 寫起來更快,更容易學習,並且往往只是工作。
在HTML中加載的重點是首先允許訪問addEventListener方法或功能。 通過在JS中使用它,當你可以直接應用它時,你會通過HTML。
假設你可以創造你自己的屬性:
$('[myclick]').each(function(i, v) {
v.addEventListener('click', function() {
eval(v.myclick); // eval($(v).attr('myclick'));
});
});
JS所做的與此有點不同。
您可以將它等同於(對於創建的每個元素):
element.addEventListener('click', function() {
switch(typeof element.onclick) {
case 'string':eval(element.onclick);break;
case 'function':element.onclick();break;
}
});
實際的實施細節可能會因一系列微妙的變化而有所不同,這使得兩者在某些情況下略有不同,但這是它的要點。
這可以說是一種兼容性破解,你可以將一個函數固定為on屬性,因為默認情況下屬性都是字符串。
JavasSript中的'this'
引用的上下文是不同的。
看下面的代碼:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<input id="btnSubmit" type="button" value="Submit" />
<script>
function disable() {
this.disabled = true;
}
var btnSubmit = document.getElementById('btnSubmit');
btnSubmit.onclick = disable();
//btnSubmit.addEventListener('click', disable, false);
</script>
</body>
</html>
它的功能非常簡單。 當你點擊該按鈕時,該按鈕將被自動禁用。
首先,當您嘗試以這種方式button.onclick = function(),
事件button.onclick = function(),
onclick事件將通過單擊按鈕觸發,但是,該按鈕不會被禁用,因為button.onclick和onclick事件之間沒有明確的綁定處理程序。 如果你調試看到'this'
對象,你可以看到它指的是'window'
對象。
其次,如果你評論btnSubmit.onclick = disable();
並取消註釋//btnSubmit.addEventListener('click', disable, false);
您可以看到該按鈕被禁用,因為通過這種方式,button.onclick事件和onclick事件處理程序之間存在顯式綁定。 如果您調試到禁用功能,您可以看到'this'
是指button control
而不是window
。
這是我不喜歡JavaScript不一致的地方。 順便說一句,如果你使用jQuery( $('#btnSubmit').on('click', disable);
),它使用顯式綁定。
一個細節還沒有被注意到:現代桌面瀏覽器認為不同的按鈕是AddEventListener('click'
“點擊” AddEventListener('click'
默認為AddEventListener('click'
和onclick
。
- 在Chrome 42和IE11上,
onclick
和AddEventListener
點擊左側和中間點擊。 - 在Firefox 38上,
onclick
只在左鍵單擊時觸發,但AddEventListener
單擊左鍵,中鍵和右鍵點擊。
此外,涉及滾動游標時,跨瀏覽器的中點擊行為非常不一致:
- 在Firefox上,中點擊事件總是會觸發。
- 在Chrome上,如果middleclick打開或關閉滾動光標,它們將不會觸發。
- 在IE中,它們在滾動游標關閉時觸發,但在打開時不會觸發。
還值得注意的是,對於任何鍵盤可選HTML元素(如input
“單擊”事件也會在空間觸發,或者在選擇元素時進行輸入。
你可以看到,如果你有另外幾個功能的區別:
var h = document.getElementById('a');
h.onclick = donothing1;
h.onclick = donothing2;
h.addEventListener('click', donothing3);
h.addEventListener('click', donothing4);
功能2,3和4工作,但1不工作。 這是因為addEventListener
不覆蓋現有的事件處理程序,而onclick
覆蓋任何現有的onclick = fn
事件處理程序。
當然,另一個顯著差異是onclick
將始終工作,而addEventListener
在版本9之前的Internet Explorer中不起作用。您可以在IE <9中使用類似的attachEvent
(語法略有不同)。
兩者都是正確的,但它們本身都不是“最好的”,開發人員可能會選擇使用這兩種方法。
事件監聽器(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事件 ),您可以將理論上無限數量的事件附加到任何單個元素。 唯一的實際限制是客戶端內存和其他性能問題,這對每個瀏覽器都是不同的。
上面的例子表示使用匿名函數[ doc ]。 您還可以使用函數reference [ doc ]或閉包[ doc ]添加事件偵聽器:
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
addEventListener
另一個重要特性是最終參數,它控制偵聽器如何對冒泡事件做出反應[ doc ]。 我在例子中傳遞了錯誤,這可能是95%的用例的標準。 attachEvent
沒有等價的參數,或者使用內聯事件時。
內聯事件(HTML onclick =“”property and element.onclick)
在支持javascript的所有瀏覽器中,您可以將事件偵聽器內聯,即在HTML代碼中正確表示。 你可能已經看到了這個:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
大多數有經驗的開發人員避開這種方法,但它確實完成了工作; 它簡單直接。 你不能在這裡使用閉包或匿名函數(雖然處理程序本身是一種匿名函數),而你對范圍的控制是有限的。
你提到的另一種方法:
element.onclick = function () { /*do stuff here */ };
...相當於內聯javascript,不同之處在於您可以更好地控制範圍(因為您在編寫腳本而不是HTML),並且可以使用匿名函數,函數引用和/或閉包。
內聯事件的一個明顯缺點是,與上述事件監聽器不同,您可能只分配了一個內聯事件。 內聯事件存儲為元素[ doc ]的屬性/屬性,這意味著它可以被覆蓋。
使用上面HTML中的示例<a>
:
var 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/ : http://jsfiddle.net/jpgah/ 。
哪個最好?
問題在於瀏覽器的兼容性和必要性。 您目前是否需要為一個元素附加多個事件? 你會在未來嗎? 賠率是,你會的。 attachEvent和addEventListener是必需的。 如果不是的話,一個內聯事件將會訣竅。
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/ : http://jsfiddle.net/bmArj/
考慮到所有這些因素,除非您正在查看的腳本以某種其他方式考慮了瀏覽器差異(代碼中未顯示您的問題),那麼使用addEventListener
的部分在IE版本少於9的情況下將不起作用。
文件和相關閱讀
在這個答案中,我將描述定義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) 。 在小型Web應用程序中,它似乎並不重要,但它對於任何更大的項目都很重要。 維護一個分離結構和邏輯的項目比一個沒有的項目要容易得多。
- 用正確的事件名稱消除混淆。 由於使用內聯事件偵聽器或將事件偵聽器分配給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:EventTarget.addEventListener的參數2不是對象 。 - 如果您想在捕獲或冒泡階段處理事件,不提供選擇方法。
內聯事件處理程序( onevent
HTML屬性)
代碼示例:
<a href="//google.com" onclick="event.preventDefault();">Try clicking this link.</a>
類似於element.onevent
,現在不鼓勵。 除了element.onevent
的問題之外,它還包括:
- 是一個潛在的安全問題 ,因為它使XSS更加有害。 現在,網站應發送適當的
Content-Security-Policy
HTTP標頭來阻止內聯腳本,並允許僅來自受信任域的外部腳本。 請參閱內容安全策略如何工作? - 不分離文檔結構和邏輯 。
- 如果您使用服務器端腳本生成頁面,並且例如您生成了100個鏈接,每個鏈接都具有相同的內聯事件處理程序,那麼您的代碼將比僅定義一次事件處理程序的時間長得多。 這意味著客戶將不得不下載更多的內容,結果你的網站會變慢。
也可以看看
-
EventTarget.addEventListener()
文檔 (MDN) -
EventTarget.removeEventListener()
文檔 (MDN) - onclick vs addEventListener
- dom-events 標籤wiki
據我所知,DOM“加載”事件的作用仍然非常有限。 這意味著它只會觸發window object
, images
和<script>
元素。 直接onload
分配也是如此。 這兩者之間沒有技術上的區別。 可能是.onload =
有更好的跨瀏覽器可用性。
但是,您無法將load event
分配給<div>
或<span>
元素或其他元素。
根據MDN ,差異如下:
的addEventListener:
EventTarget.addEventListener()方法將指定的與EventListener兼容的對象添加到其調用的EventTarget上指定事件類型的事件偵聽器列表。 事件目標可以是文檔中的元素,文檔本身,窗口或任何其他支持事件的對象(如XMLHttpRequest)。
的onclick:
onclick屬性返回當前元素上的click事件處理程序代碼。 當使用click事件觸發某個動作時,還可以考慮將相同的動作添加到keydown事件中,以允許不使用鼠標或觸摸屏的人使用同一動作。 語法element.onclick = functionRef; 其中functionRef是一個函數 - 通常是在別處聲明的函數或函數表達式的名稱。 有關詳細信息,請參閱“JavaScript指南:函數”。
如您在下面的代碼中看到的,還有一種使用中的語法差異:
的addEventListener:
// Function to change the content of t2
function modifyText() {
var t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue == "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
}
}
// add event listener to table
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
的onclick:
function initElement() {
var p = document.getElementById("foo");
// NOTE: showAlert(); or showAlert(param); will NOT work here.
// Must be a reference to a function name, not a function call.
p.onclick = showAlert;
};
function showAlert(event) {
alert("onclick Event detected!");
}
雖然onclick
適用於所有瀏覽器,但addEventListener
不適用於Internet Explorer的舊版本,該版本使用attachEvent
代替。
onclick
的缺點是只能有一個事件處理程序,而另外兩個會觸發所有已註冊的回調。