MDN wants to talk to developers like you: https://qsurvey.mozilla.com/s3/8d22564490d8

概要

addEventListener は、1 つのイベントターゲットにイベントリスナーを 1 つ登録します。イベントターゲットは、ドキュメント上の単一のノード、ドキュメント自身、ウィンドウ、あるいは、XMLHttpRequest です。

ターゲットに対して 2 つ以上のイベントリスナーを登録するには、同じターゲットに対して、異なるイベントタイプ、あるいは、キャプチャパラメータを指定して addEventListener() を呼び出します。

構文

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener [, useCapture]);
target.addEventListener(type, listener [, useCapture, aWantsUntrusted  ]); // Gecko/Mozilla のみ

パラメーター

type
対象とするイベントの種類を表す文字列
listener
指定されたタイプのイベントが発生するときに通知を受け取るオブジェクト。これは、EventListener インタフェースを実装するオブジェクト、あるいは、単純に、JavaScript の関数でなければなりません。
options Optional
対象のイベントリスナーの特性を指定する、オプションのオブジェクトです。次のオプションが使用できます。
  • capture: Boolean 値で true を指定すると、指定されたタイプのイベントが、DOM ツリーで下に位置する任意の EventTarget発送される前に、登録したリスナーに発送されます。
  • once: listener が追加後にたかだか1回しか実行されないことを {jsxref("Boolean")}} 値で指定します。true を指定すると、listener は一度実行された時に自動的に削除されます。  
  • passivelistenerpreventDefault() を呼び出さないことを表す Boolean 値です。true を指定すると、ユーザーエージェントはその呼び出しを無視し、コンソールに警告を出力します。
  • mozsystemgroup: コードが XBL か Firefox' chrome で実行されている時のみ利用できます。リスナーがシステムグループに追加されているかどうかを表す Boolean 値です。
useCapture Optional
捕捉フェーズを使用する場合は、 useCapturetrue を指定します。捕捉フェーズの開始後、指定されたタイプのイベントの全てが、まず、登録された listener発送され、その後、DOM ツリーにおいてその下に位置する任意の EventTarget発送 されます。ツリーをたどって上方へ浮上するイベントは、捕捉フェーズを用いるように指定されたリスナーを誘発することはありません。詳細については、DOM Level 3 Events を参照してください。この引数は、全てのブラウザで省略可能ではないことに注意してください。省略した場合、 useCapturefalse となります。
Note: イベントターゲットに登録されたイベントリスナーは、補足フェーズや浮上フェーズではなく、ターゲットフェーズのイベントになります。そのため、ターゲットフェーズのイベントは、useCapture パラメーターにかかわらず、要素上のすべてのリスナーを実行します。
Note: useCapture は主要なブラウザーの最新バージョンではオプションになりました。たとえば、Firefox 6 以前では必須パラメーターでした。最大限の互換性を保証するには、パラメーターを指定するようにしてください。
WantsUntrusted
true の場合、このリスナーはウェブコンテンツによって発火された合成イベント (カスタムイベント) を受け取ります (chrome ではデフォルトで false ですが、一般のウェブページでは true です)。このパラメーターは Gecko でのみ利用可能であり、主にアドオンやブラウザー自身の役に立つものです。特権ページと非特権ページの間のやり取りを参照してください。

シンプルなリスナーの追加

HTML コンテンツ

<table id="outside">
    <tr><td id="t1">one</td></tr>
    <tr><td id="t2">two</td></tr>
</table>

JavaScript コンテンツ

// t2 のコンテンツを変更する関数
function modifyText() {
  var t2 = document.getElementById("t2");
  if (t2.firstChild.nodeValue == "three") {
    t2.firstChild.nodeValue = "two";
  } else {
    t2.firstChild.nodeValue = "three";
  }
}

// イベントリスナーを table に追加
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);

上記の例では、modifyText()addEventListener() を用いて登録された click イベントのリスナーになっています。table 中のどこをクリックしても、そのハンドラまでバブルアップし、modifyText() が実行されます。

リスナー関数に引数を渡す場合は、関数式を使う必要があります。

匿名関数を利用したイベントリスナー

HTML コンテンツ

<table id="outside">
    <tr><td id="t1">one</td></tr>
    <tr><td id="t2">two</td></tr>
</table>

JavaScript コンテンツ

// t2 のコンテンツを変更する関数
function modifyText(new_text) {
  var t2 = document.getElementById("t2");
  t2.firstChild.nodeValue = new_text;    
}
 
// イベントリスナーを table に追加する関数
var el = document.getElementById("outside");
el.addEventListener("click", function(){modifyText("four")}, false);

注記

なぜ、addEventListener を使うのですか?

addEventListener は、W3C DOM で仕様化された、イベントリスナーを登録するための方法です。その利点は以下の通りです。

  • イベントに 1 つ以上のハンドラを追加することができます。これは、特に、他のライブラリ/拡張で利用しても上手く動作する必要がある DHTML ライブラリや Mozilla の拡張 のために役立ちます。
  • リスナーがアクティブ化されたときに、その動きを細かくコントロールすることを可能にします(キャプチャリング 対 バブリング)。
  • HTML 要素だけでなく、任意の DOM 要素 で動作します。

別の方法である、イベントリスナーを登録するための古い方法 は、後で説明します。

イベント発送中のリスナーの追加

EventListener がイベント処理中に EventTarget に追加された場合、それが現在のアクションによって実行されることはありませんが、浮上フェーズのように、後の段階のイベントフローで実行されるかもしれません。

複数の同一のイベントリスナー

複数の同一の EventListener が、同じ EventTarget に同じ引数で登録された場合、重複するインスタンスは反映されません。EventListener が 2 度呼び出されることはなく、重複するインスタンスは反映されないので、removeEventListener で手動で削除する必要はありません。

ハンドラー内での this の値

一連の類似した要素に対して一般的なハンドラーを使いたい場合のように、イベントハンドラーが実行される要素を参照したいということがたびたびあります。

addEventListener() を使って要素にハンドラー関数を設定したとき、ハンドラーの中の this の値は要素への参照となります。これはハンドラーに渡された event 引数の currentTarget プロパティの値と同じです。

onclick のようなイベント属性が HTML 要素に指定されていた場合、イベント属性の Javascirpt コードの値は、事実上 addEventListener() を使用するような方法で this の値をバインドするハンドラー関数に置き換えられます。つまり、コード内に直接 this が現れた場合には、要素への参照に置き換えられます。けれど、属性の値のコードによって呼ばれた先の関数の内側では、this の値はふつうのルールに従って決まることに注意してください。したがって、以下のような場合:

<table id="t" onclick="modifyText();">
. . .

onclick イベントで呼び出されたときの modifyText() 内の this の値は、グローバル (window) オブジェクトへの参照となります。 (もしくは strict mode の場合、undefined になります。)

注: JavaScript 1.8.5 で Function.prototype.bind() メソッドが導入されました。これは呼び出す関数内で this に相当する値を指定できるものです。これを使えば、関数がどこから呼び出されるかによって this の値が変わってしまうというややこしい問題を簡単に回避できます。ただし、リスナーを後で削除できるように、そのリスナーへの参照を残しておく必要があります。

以下は bind を使った場合と使わない場合の例です。

var Something = function(element) {
  this.name = 'Something Good';
  this.onclick1 = function(event) {
    console.log(this.name); // this は element なので undefined になります
  };
  this.onclick2 = function(event) {
    console.log(this.name); // this はバインドされた Something オブジェクトなので「Something Good」と出力されます
  };
  element.addEventListener('click', this.onclick1, false);
  element.addEventListener('click', this.onclick2.bind(this), false); // これが仕掛けです
}

上の例の問題は、bind の付いたリスナーを削除できないということです。もうひとつの解決策は、あらゆるイベントを捕捉する handleEvent という特別な関数を使用することです。

var Something = function(element) {
  this.name = 'Something Good';
  this.handleEvent = function(event) {
    console.log(this.name); // this は Something オブジェクトなので「Something Good」と出力されます
    switch(event.type) {
      case 'click':
        // 処理
        break;
      case 'dblclick':
        // 処理
        break;
    }
  };

  // この場合のリスナーは this であって this.handleEvent でないことに注意してください
  element.addEventListener('click', this, false);
  element.addEventListener('dblclick', this, false);

  // リスナーは適切に削除できます
  element.removeEventListener('click', this, false);

  element.removeEventListener('dblclick', this, false);
}

古い Internet Explorer と attachEvent

IE9 より前の Internet Explorer では、標準の addEventListener ではなく、 attachEvent を使わなければなりません。IE をサポートするためには、上記の例を以下のように修正しなけれなりません。

if (el.addEventListener){
  el.addEventListener('click', modifyText, false);
} else if (el.attachEvent){
  el.attachEvent('onclick', modifyText);
}

attachEvent の欠点が 1 つあります。this の値がイベントを起こした要素ではなく、window オブジェクトへの参照になってしまうことです。

互換性

次のコードをスクリプトの初めに書いておくと、IE 8 では対応していない addEventListenerremoveEventListenerEvent.preventDefault、および Event.stopPropagation が動作するようになります。このコードは、handleEventと DOMContentLoaded イベントもサポートします。

Note: useCapture がサポートされていないため、IE 8 では代わりの方法はありません。以下のコードは IE 8 のサポートしか追加しないことに注意してください。

また、この IE8 への対応は standards mode でのみ動作することに注意してください。そのためには、 doctype 宣言が必要になります。

(function() {
  if (!Event.prototype.preventDefault) {
    Event.prototype.preventDefault=function() {
      this.returnValue=false;
    };
  }
  if (!Event.prototype.stopPropagation) {
    Event.prototype.stopPropagation=function() {
      this.cancelBubble=true;
    };
  }
  if (!Element.prototype.addEventListener) {
    var eventListeners=[];
    
    var addEventListener=function(type,listener /*, useCapture (will be ignored) */) {
      var self=this;
      var wrapper=function(e) {
        e.target=e.srcElement;
        e.currentTarget=self;
        if (typeof listener.handleEvent != 'undefined') {
          listener.handleEvent(e);
        } else {
          listener.call(self,e);
        }
      };
      if (type=="DOMContentLoaded") {
        var wrapper2=function(e) {
          if (document.readyState=="complete") {
            wrapper(e);
          }
        };
        document.attachEvent("onreadystatechange",wrapper2);
        eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper2});
        
        if (document.readyState=="complete") {
          var e=new Event();
          e.srcElement=window;
          wrapper2(e);
        }
      } else {
        this.attachEvent("on"+type,wrapper);
        eventListeners.push({object:this,type:type,listener:listener,wrapper:wrapper});
      }
    };
    var removeEventListener=function(type,listener /*, useCapture (will be ignored) */) {
      var counter=0;
      while (counter<eventListeners.length) {
        var eventListener=eventListeners[counter];
        if (eventListener.object==this && eventListener.type==type && eventListener.listener==listener) {
          if (type=="DOMContentLoaded") {
            this.detachEvent("onreadystatechange",eventListener.wrapper);
          } else {
            this.detachEvent("on"+type,eventListener.wrapper);
          }
          eventListeners.splice(counter, 1);
          break;
        }
        ++counter;
      }
    };
    Element.prototype.addEventListener=addEventListener;
    Element.prototype.removeEventListener=removeEventListener;
    if (HTMLDocument) {
      HTMLDocument.prototype.addEventListener=addEventListener;
      HTMLDocument.prototype.removeEventListener=removeEventListener;
    }
    if (Window) {
      Window.prototype.addEventListener=addEventListener;
      Window.prototype.removeEventListener=removeEventListener;
    }
  }
})();

イベントリスナーを登録するための古い方法

addEventListener() は、DOM 2 Events 仕様で導入されました。それ以前は、以下のようにイベントリスナーを登録していました。

// 関数へのリファレンスを利用する方法—'()' が無いことに注意してください
el.onclick = modifyText;

// 関数式を利用する方法
element.onclick = function() {
   /* ...文... */
};

このメソッドは、要素上に click イベントリスナーが既に存在する場合、置き換えてしまいます。
【訳注: これはつまり、既に要素に対し定義されたハンドラに対し更にイベントを追加しようとする場合に、以前のイベントを含める形で上書きしなければならない事を意味します。】
他のイベント、blur (onblur)、keypress (onkeypress)、などのような関連するイベントハンドラも同様です。

これは本質的には DOM 0 の一部分なので、この方法は、非常に広くサポートされており、特別なクロスブラウザ用のコードも必要ありません。それ故、addEventListener() の独自の機能が必要でない場合に、動的にイベントリスナーを登録する方法として普通に使われています。

メモリに関する問題

var i;
var els = document.getElementsByTagName('*');

// ケース 1
for(i = 0; i < els.length ; i++){
  els[i].addEventListener("click", function handler(e){/*関数の処理*/}, false);
}

// ケース 2
function processEvent(e){
  /*関数の処理*/
}

for(i = 0; i < els.length ; i++){
  els[i].addEventListener("click", processEvent, false);
}

最初の例では、新しい匿名関数がループごとに作られています。これに対して 2 番目の例では、前に宣言された同じ関数がイベントハンドラとして使われています。そのため、メモリの消費が抑えられます。その上、最初の例では匿名関数への参照が保存されておらず、ハンドラーへの参照がないため、 element.removeEventListener を実行することが不可能になってしまっています。一方、2番目の例では、myElement.removeEventListener("click", processEvent, false) とすることができます。

passive なリスナー を用いたスクロールのパフォーマンスの改善

var elem = document.getElementById('elem');
elem.addEventListener('touchmove', function listener() {
  /* ここで何かする */
}, { passive: true });

touchmove リスナーはユーザーのスクロール中もブロックされません (同じことは wheel イベントにも適用されます)。デモが ここ (Google Developers Page) で見られます。

注意: イベントリスナーのオプションをサポートしないブラウザーは、3番目の引数を useCapture とみなしてしまうため、true と評価されます。

 

仕様書

Specification Status Comment
DOM
EventTarget.addEventListener() の定義
現行の標準  
DOM4
EventTarget.addEventListener() の定義
勧告  
Document Object Model (DOM) Level 2 Events Specification
EventTarget.addEventListener() の定義
勧告 Initial definition

ブラウザー互換性テーブル

機能 Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
基本サポート 1.0[1][2] 1.0 (1.7 or earlier)[3] 9.0[4] 7 1.0[1]
useCapture がオプション 1.0 6 (6) 9.0 11.60 (有)
options パラメーター (capturepassive の値に対応)[5]

49.0 (capture) 51.0 (passive)

49 (49) 未サポート 未サポート Landed in Nightly WebKit bug 158601
options パラメータが once の値に対応 55 50 (50) 未サポート 未サポート Landed in Nightly WebKit bug 149466
機能 Android Android Webview Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile Chrome for Android
基本サポート 1.0

(有)[2]

1.0 (1.0)[3] 9.0 6.0 1.0[1]

(有)[2]

useCapture がオプション ?

(有)

6.0 (6) ? ? ?

(有)

options パラメータ (capturepassive の値に対応)[5] 未サポート 49.0 (capture) 51.0 (passive) 49.0 (49) ? ? ? 49.0 (capture) 51.0 (passive)

[1] WebKit で useCapture 引数が明示的に省略可能となったのは、 2011 年の 6 月 ですが、変更以前でも動作していました。この変更は、 Safari 5.1 と Chrome 13 から実装されます。

[2] Chrome 49 以前は、タイプとリスナーのパラメーターはオプションでした。

[3] Firefox 6 より前では、 useCapture の引数を省略した場合、例外が発生します。Gecko 9.0 (Firefox 9.0 / Thunderbird 9.0 / SeaMonkey 2.6) より前では、 addEventListener() のリスナー引数が null の場合、例外が発生します。現在では例外は発生せず、何も処理をしません。

[4] 古いバージョンの Internet Explorer は、代わりにプロプライエタリな EventTarget.attachEvent メソッドをサポートしています。

[5] 後方互換性のため、options をサポートするブラウザは、3番目の引数に options または Boolean のどちらも指定できます。

関連項目

ドキュメントのタグと貢献者

 最終更新者: shuuji3,