読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

JavaScriptの動かないコード (中級編) イベントハンドラに同じ関数を2回以上追加したい

javascript 動かないコード


以下のJavaScriptコードが意図した動作をしないのは,なぜですか。(制限時間1分)


やりたい事:

  • ボタン押下時に, 2 と表示する。
  • そのために,0に1足す操作を2回行なう。
<input type="button" value="「2」と表示" id="my_button">

<!-- ここに表示されます -->
<div id="my_div"></div>

<script language="JavaScript">

// 初期化
var a = 0;

// a に1足して表示
function f(){
	a++;
	my_div.innerHTML = a;
}

// イベント追加用の関数
function addEvent( element, eventName, func ){  
	if ( element.addEventListener )
	{
		// Firefox
		element.addEventListener( eventName, func, false );
	}
	else if ( element.attachEvent )
	{
		// IE
		element.attachEvent( "on" + eventName, func );  
	}
}

// イベントを2個追加
addEvent( my_button, "click", f );
addEvent( my_button, "click", f );

</script>




答え



IEでは「2」と表示される。

しかし,Firefoxでは「1」と表示されてしまう。



IEでは,イベントリスナとして同じ関数を2回以上追加する事が可能だ。

一方FFでは,関数の重複が無視されてしまい,同一の関数を複数回追加しても一度しか実行されないようだ。


WEB ページの onLoad イベントのまとめ
http://winofsql.jp/VA003334/JScript08...


IE は、同じ関数でも追加されますが、Firefox と Opera は、同じ関数は追加されないようです

※ 但し実行順序から考えると、同じ関数の追加は Firefox は無視。Opera は以前のを削除して追加のようです

Mozillaのページにも公式に述べられている。

element.addEventListener
https://developer.mozilla.org/ja/DOM/...

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

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


ライブラリを利用した場合は,多少挙動が変わってくる。


jQueryで動的にイベント追加したいという場合,例えばクリック専用の click() を使って

	$( "#my_button" ).click( f );
	$( "#my_button" ).click( f );

としてみたり,あるいは bind() を使って

	$( "#my_button" ).bind( "click", f );
	$( "#my_button" ).bind( "click", f );

のようにイベントハンドラを2回割り当てたとする。

でも,いずれの場合でも,IE・FFの両者で,f() は一度しか実行されない。(二度にはならない。)


prototype.jsを使ってもだめである。

	Event.observe( my_button, "click", f );
	Event.observe( my_button, "click", f );

のようにしても,IE・FFの両者で,f() は一度しか実行されない。



クロスブラウザで,同じ関数を複数回イベントハンドラに割り当てるには,下記のようにする方法がある。

	addEvent( my_button, "click", function(){f();} );
	addEvent( my_button, "click", function(){f();} );


f という関数を直接割り当てるのではなく,新たに関数オブジェクトを作成して,その関数オブジェクトを毎回,別個に割り当てるようにすればよいのだ。

こうすれば,FFでも f() は無事2回実行され,「2」と表示されるようになる。


(※ただし,これだと追加したリスナに名前がついていないので,イベントを削除したい時には困る。)