addEventListener
とonclick
の違いは何ですか?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
上記のコードは別々の.jsファイルにまとめられており、どちらも完全に機能します。
どちらも正しいですが、どれもそれ自体が「最善」ではないため、開発者が両方のアプローチを使用することを選択した理由があるかもしれません。
イベントリスナー(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 events )を使用すると、理論上無制限の数のイベントを任意の単一の要素に添付できます。唯一の実際的な制限は、クライアントサイドのメモリとその他のパフォーマンスの問題です。これらはブラウザごとに異なります。
上記の例は無名関数[ doc ]の使用を表しています。関数参照[ doc ]またはクロージャー[ doc ]を使用してイベントリスナーを追加することもできます。
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
addEventListener
のもう1つの重要な機能はfinalパラメータです。これはリスナーがバブリングイベントにどう反応するかを制御します[ 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 */ };
...は、(HTMLではなくスクリプトを書いているので)スコープをより細かく制御でき、無名関数、関数参照、および/またはクロージャを使用できることを除いて、インラインJavaScriptと同じです。
インラインイベントの大きな欠点は、上記のイベントリスナーとは異なり、インラインイベントを1つだけ割り当てることができるということです。インラインイベントは、要素[ doc ]の属性/プロパティとして格納されます。つまり、上書きすることができます。
上記のHTMLの<a>
の例を使用してください。
var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };
...要素をクリックすると、 only "Did stuff#2"を参照してください - 最初の代入onclick
プロパティを2番目の値で上書きし、元のインラインHTMLのonclick
プロパティを上書きしましたも。ここでそれをチェックしてください: http://jsfiddle.net/jpgah/ 。
大まかに言って、 インラインイベントを使用しない 。特定のユースケースがあるかもしれませんが、そのユースケースが100%確信できない場合は、インラインイベントを使用しないでください。
現代のJavascript(Angularなど)
この答えが最初に投稿されて以来、AngularのようなJavaScriptフレームワークははるかに普及しています。 Angularテンプレートの中に次のようなコードがあります。
<button (click)="doSomething()">Do Something</button>
これはインラインイベントのように見えますが、違います。この種のテンプレートは、舞台裏でイベントリスナーを使用する、より複雑なコードに変換されます。ここで私がここでイベントについて書いたことはすべて当てはまりますが、あなたは少なくとも1つの層によって重要性から取り除かれます。基本を理解する必要がありますが、現代のJSフレームワークのベストプラクティスがこの種のコードをテンプレートに記述することを含む場合は、インラインイベントを使用しているとは思わないでください。
どれが一番いいの?
問題はブラウザの互換性と必要性の問題です。現在、要素に複数のイベントを添付する必要がありますか?あなたは将来的になりますか?オッズは、そうでしょう。 attachEventとaddEventListenerが必要です。そうでなければ、インラインイベントはトリックをするように思えるかもしれません、しかしそれはありそうにないかもしれませんが少なくとも予測可能である未来の準備のためにはるかに役立っています。 JSベースのイベントリスナーに移行しなければならない可能性があるので、そこから始めてください。インラインイベントを使用しないでください。
jQueryやその他のJavaScriptフレームワークは、DOMレベル2イベントのさまざまなブラウザ実装を一般的なモデルにカプセル化しているので、IEの歴史を反逆者として心配することなく、ブラウザに準拠したコードを書くことができます。 jQueryと同じコード、すべてクロスブラウザで、すぐに使える:
$(element).on('click', function () { /* do stuff */ });
ただし、この1つだけのために、実行してフレームワークを入手しないでください。あなたは簡単に古いブラウザの世話をするためにあなた自身の小さなユーティリティを転がすことができます:
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未満.
ドキュメンテーションと関連読書
この回答では、DOMイベントハンドラーを定義する3つの方法について説明します。
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属性を理解できない 。.onevent
プロパティにイベントリスナーを割り当てるため、多くの経験のないJavaScriptプログラマーは、イベント名が例えばonclick
またはonload
であると考えています。 on
はnotイベント名の一部です。正しいイベント名はclick
およびload
です。これが、イベント名が.addEventListener()
に渡される方法です。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
の問題に加えて、次のことを行います。
Content-Security-Policy
HTTPヘッダーを送信して、インラインスクリプトをブロックし、信頼できるドメインからの外部スクリプトのみを許可する必要があります。 コンテンツセキュリティポリシーの仕組み を参照してくださいonclick
はすべてのブラウザで機能しますが、addEventListener
は古いバージョンのInternet Explorerでは機能せず、代わりにattachEvent
を使用します。
onclick
のマイナス面は、イベントハンドラは1つしか存在できず、他の2つは登録されたすべてのコールバックを起動するということです。
私の知る限りでは、DOMの "load"イベントはまだ非常に限定的にしか機能しません。つまり、window object
、images
、<script>
の各要素に対してのみ起動されます。直接onload
代入についても同じことが言えます。両者に技術的な違いはありません。おそらく.onload =
はより良いクロスブラウザの可用性を持っています。
ただし、load event
を<div>
または<span>
要素に割り当てることはできません。
addEventListener
では複数のイベントを追加できますが、onclick
ではこれはできません。onclick
はHTML
属性として追加できますが、addEventListener
は<script>
要素内にのみ追加できます。addEventListener
は、イベント伝播を停止させることができる3番目の引数を取ることができます。どちらもイベントの処理に使用できます。ただし、addEventListener
は、onclick
が実行できることすべてを実行できることから、推奨される選択肢です。 JavaScriptとHTMLが混同されるため、HTML属性としてインラインonclick
を使用しないでください。コードが保守しにくくなります。
_ mdn _ によると、違いは以下のとおりです。
addEventListener:
EventTarget.addEventListener()メソッドは、呼び出されたEventTarget上の指定されたイベントタイプのイベントリスナーのリストに、指定されたEventListener互換オブジェクトを追加します。イベントターゲットは、ドキュメント内の要素、ドキュメント自体、ウィンドウ、またはイベントをサポートするその他のオブジェクト(XMLHttpRequestなど)です。
onclick:
Onclickプロパティは、現在の要素のクリックイベントハンドラコードを返します。 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!");
}
ブラウザのサポートについてあまり心配していないのであれば、イベントによって呼び出された関数内で 'this'参照を再バインドする方法があります。通常は、関数が実行されたときにイベントを生成した要素を指しますが、これは必ずしも必要なものではありません。注意が必要なのは、この例に示すように、まったく同じイベントリスナーを同時に削除できるようにすることです。 http://jsfiddle.net/roenbaeck/vBYu3/
/*
Testing that the function returned from bind is rereferenceable,
such that it can be added and removed as an event listener.
*/
function MyImportantCalloutToYou(message, otherMessage) {
// the following is necessary as calling bind again does
// not return the same function, so instead we replace the
// original function with the one bound to this instance
this.swap = this.swap.bind(this);
this.element = document.createElement('div');
this.element.addEventListener('click', this.swap, false);
document.body.appendChild(this.element);
}
MyImportantCalloutToYou.prototype = {
element: null,
swap: function() {
// now this function can be properly removed
this.element.removeEventListener('click', this.swap, false);
}
}
上記のコードはChromeでもうまく機能します。おそらく他のブラウザと「バインド」互換性を持たせるためのシムがいくつかあります。
詳細についてはまだ触れていません。最近のデスクトップブラウザでは、デフォルトでAddEventListener('click'
とonclick
のボタンクリックが異なると見なされています。
onclick
とAddEventListener
の両方のクリックが左クリックと中クリックで起動します。onclick
は左クリックで only を起動しますが、AddEventListener
クリックは左、中 、 右クリックで起動します。また、スクロールカーソルが関係している場合、中クリックの振る舞いはブラウザ間で 非常に 矛盾します。
また、input
のようなキーボードで選択可能なHTML要素の「クリック」イベントもスペースで発生するか、または要素が選択されたときに入力されることにも注目に値します。
それをプロトタイプ化することによってリスナーを拡張することも可能です(私たちがそれを参照していて、それが無名関数ではない場合) - または 'onclick'が関数ライブラリへの呼び出しを呼び出すようにする(他の関数を呼び出す関数)
好き
Elm.onclick = myFunctionList
function myFunctionList(){
myFunc1();
myFunc2();
}
これは、onclick呼び出しを変更して関数myFunctionList()を変更して、必要な処理を実行する必要がないことを意味しますが、これはバブリング/キャッチ段階の制御なしに行われるため、新しいブラウザでは避けてください。
念のために、誰かが将来このスレッドを見つけたら...
インラインハンドラの使用は コンテンツセキュリティポリシーとの互換性がありません そのためaddEventListener
アプローチはその観点からより安全です。もちろん、インラインハンドラをunsafe-inline
で有効にすることもできますが、その名前が示すように、CSPが防止するJavaScriptの悪用の大部分を取り戻すので安全ではありません。
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(),
onclickイベントがトリガーされますが、button.onclickイベントハンドラーとonclickイベントハンドラーの間には明示的なバインドがないため、ボタンは無効になりません。 'this'
オブジェクトをデバッグすると、'window'
オブジェクトを参照していることがわかります。
第二に、btnSubmit.onclick = disable();
をコメントして//btnSubmit.addEventListener('click', disable, false);
のコメントを解除すると、button.onclickイベントとonclickイベントハンドラーの間に明示的なバインディングがあるため、ボタンが無効になっていることがわかります。無効化機能にデバッグすると、'this'
がwindow
ではなくbutton control
を参照していることがわかります。
これは、一貫性のないJavaScriptについて私が嫌いなものです。ところで、jQuery($('#btnSubmit').on('click', disable);
)を使用している場合、明示的なバインディングを使用します。
Javascriptはすべてをオブジェクトに融合する傾向があり、混乱を招く可能性があります。 1つにすべてJavaScriptの方法です。
基本的にonclickはHTMLの属性です。逆にaddEventListenerは、HTML要素を表すDOMオブジェクト上のメソッドです。
JavaScriptオブジェクトでは、メソッドは値として関数を持ち、それが関連付けられているオブジェクトに対して機能する単なるプロパティです(これを使用してなど)。
DOMでは、HTML要素としてのJavaScriptでは、その属性がそのプロパティにマッピングされます。
JavaScriptが間接層なしで単一のコンテナまたは名前空間にすべてを融合するので、これは人々が混乱するところです。
通常のOOレイアウト(これは少なくともプロパティ/メソッドの名前空間をマージします)では、次のようになるでしょう。
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
Onloadにはgetter/setterを、属性にはHashMapを使うことができるようなバリエーションがありますが、最終的にはそのようになります。 JavaScriptは、何が他のものの中にあるのかを知ることを期待して、その間接層を排除しました。それはdomElementと属性をまとめました。
互換性を排除するために、ベストプラクティスとしてaddEventListenerを使用してください。他の回答として、基本的なプログラム上の違いではなく、その点での違いについて説明します。基本的に、理想的な世界ではHTMLからon *を使用することだけを意図していますが、さらに理想的な世界ではHTMLからそのようなことをしてはいけません。
今日はなぜそれが支配的なのですか?それは書くのが速く、学ぶのがより簡単で、ちょうど働く傾向があります。
HTMLでのonloadの全体的なポイントは、まず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属性に固定できることは、おそらく互換性のためのハックです。
addEventListener
を使用すると、複数のハンドラを設定できますが、IE 8以下ではサポートされません。
IEにはattachEvent
がありますが、まったく同じではありません。