JavaScript プログラミング講座

 

アタッチイベントについて

 


■アタッチイベントについて


アタッチイベントは、 Internet Explorer 専用の仕様です。
 
Internet Explorer 11 以降では、廃止済みです。
 
通常の場合、標準化されたイベントの方を優先的に使用します。
 
■アタッチイベントについて
 
イベントの通知を、複数のリスナーが受け取る事ができます。
 
Internet Explorer 8 以前の環境で、複数のリスナーを登録したい場合に必要となります。
 
MSDN のリファレンスです。
 
http://msdn.microsoft.com/en-us/library/ie/jj853341.aspx (Legacy Platform Events)
 
■Opera(Presto 版) の場合
 
Opera(Presto 版) でも一部対応しています。
 
この場合、標準的な仕様で動作します。
 
Opera は、イベントリスナーに対応しているため、アタッチイベントを使う事は無いでしょう。
 

■イベントフェーズについて

 
大抵のイベントは、DOM ツリーと密接に関係しています。
 
■バブルズ(bubbles)について
 
大抵のイベントは、バブルズ(bubbles)に対応しています。
 
対象本人だけでなく、祖先にも通知されます。
 
バブルズに対応していないイベントは、対象本人にのみ通知されます。
 
例えば、onclick イベントは、バブルズに対応しています。
 
例えば、onfocus イベントは、バブルズに対応していません。
 
■イベントフェーズについて
 
イベント通知の伝達順序として、以下の段階があります。
 
1.キャプチャ段階
 
2.ターゲット段階
 
3.バブリング段階
 
アタッチイベントの伝達順序は、イベント属性と同等です。
 
バブルズに対応しているイベントは、ターゲット段階 → バブリング段階 という順序で伝達されます。
 
バブルズに対応していないイベントは、ターゲット段階のみ実行されます。
 
1.キャプチャ段階
 
アタッチイベントには、キャプチャに相当する段階はありません。
 
3.ターゲット段階
 
ターゲット段階は、対象となるエレメント上で実行されます。
 
任意のリスナーにとって、自身にイベントが発生した事がわかります。
 
3.バブリング段階
 
バブリング段階では、子孫から Document に向かって順番に実行されます。
 
「ターゲットの直後」から「Document」まで実行されます。
 
アップしていく流れが、バブル(泡)となります。
 
任意のリスナーにとって、子孫にイベントが発生した事がわかります。
 
下の図であれば、1~4に登録したリスナーに相当します。
 
 
■イベントフェーズの流れを確認する
 
マウスボタンを押した時のイベントフェーズの流れを確認する

<html>
  <body>

    <div style="width:400px; height:300px; background:#fcc;">
	    <span style="font-size:32px;">あいうえお</span>
    </div>

    <script type="text/javascript">
    <!--
	// ------------------------------------------------------------
	// DOM オブジェクトのすべての子孫を検索する関数
	// ------------------------------------------------------------
	function DomNodeFindAllDescendants(node,func){
		function f(n){
			var nodes = n.childNodes;
			var i;
			var num = nodes.length;
			var a = new Array();
			for(i=0;i < num;i++){ a[i] = nodes[i]; }
			for(i=0;i < num;i++){
				node = func(a[i]);
				if(node){ return node; }
				node = f(a[i]);
				if(node){ return node; }
			}
			return null;
		}
		return f(node);
	}


	// アタッチイベントに対応している
	if(window.attachEvent){

		// ------------------------------------------------------------
		// 要素にリスナーを登録する関数
		// ------------------------------------------------------------
		function ElementAttachEventMouseDown(current){

			// アタッチイベントに対応しているか
			if(!(current.attachEvent)) return;

			// 要素にリスナーを登録
			current.attachEvent("onmousedown" , function (e){
				var target = e.srcElement;
				console.log("カレント:" + current + "(" + current.nodeName + ")");
				console.log("ターゲット:" + target + "(" + target.nodeName + ")");
				console.log("---");
			});
		}


		// ------------------------------------------------------------
		// すべてのフェーズでリッスンを開始
		// ------------------------------------------------------------
		// ドキュメントにリスナーを登録
		ElementAttachEventMouseDown(document);

		// コールバック関数を使って、ドキュメントのすべての子孫を検索
		DomNodeFindAllDescendants(document,function (node){

			// ドキュメントの子孫にリスナーを登録
			ElementAttachEventMouseDown(node);

			// 検索を継続
			return null;
		});

	}
    //-->
    </script>

  </body>
</html>
 


 

EventObj クラスについて

 
 


■ EventObj クラスについて

 
EventObj オブジェクトは、イベント関連の情報を格納し、リスナーに渡すための入れ物です。
 
システム側からイベントが発行される場合、システムが、EventObj オブジェクトを生成します。
 
登録したコールバック関数の引数から、EventObj オブジェクトを受け取る事ができます。
 
EventObj クラスに派生はありません。
 
マウスやキーボード用の、様々なプロパティが混在しています。
 

■EventObj のプロパティについて

 
EventObj オブジェクトには、以下のプロパティがあります。(一部抜粋)
 
プロパティ名説明
typeStringイベント名を取得する。(接頭の on は省略される)
srcElementElementイベントの発生場所を意味する。ただし、エレメントではない場合 null。
returnValueBooleanリスナーから得られた戻り値情報を意味する。
false を指定すると、デフォルトの動作をキャンセルできる。
cancelBubbleBooleanイベントの伝達(バブリング)を終了したい場合 true を指定する。
 
■ srcElement プロパティ
 
イベントの発生場所を取得するには、srcElement プロパティを使用します。
 
ただし、エレメントではない場合、null が得られます。
 
例えば Window オブジェクトにリスナーを登録した場合、null が得られるでしょう。
 
これは、DOM Event の Event.target プロパティに相当します。
 

■カレントターゲットを取得する

 
カレントターゲットを取得する方法はありません。
 
クロージャなどを利用して、カレントターゲット情報を保持しておきます。
 
これは、DOM Event の Event.currentTarget プロパティに相当します。
 
コールバック関数から、カレントターゲットを取得する

// アタッチイベントに対応している
if(element.attachEvent){

	// ------------------------------------------------------------
	// 匿名関数を即時実行する
	// ------------------------------------------------------------
	(function(){

		// ------------------------------------------------------------
		// ローカル変数
		// ------------------------------------------------------------
		// カレントターゲット
		var current_target = document;

		// ------------------------------------------------------------
		// クリックすると実行されるイベント
		// ------------------------------------------------------------
		current_target.attachEvent("onclick" , function (e){

			// 出力テスト
			console.log(current_target);

		});

	})();

}
 

■デフォルトの動作をキャンセルする

 
イベントの種類によっては、キャンセルに対応している場合があります。
 
任意の動作が行われる直前に、イベントが発行されます。
 
リスナー側で、任意の動作をキャンセルするか選択する事ができます。
 
例えば、onclick や onkeypress イベントなどが対応しています。
 
■デフォルトの動作をキャンセルする
 
デフォルトの動作をキャンセルするには、コールバック関数内で、false 値を返します。
 
returnValue プロパティに、false をセットしても同等の効果があります。
 
これは、DOM Event の Event.preventDefault() メソッドに相当します。
 
テキストエリアへの入力をキャンセルする

// エレメントを作成する
var element = document.createElement("textarea");

// BODY のノードリストに登録する
document.body.appendChild(element);

// スタイルを設定
element.style.cssText = "width:400px; height:300px;";

// アタッチイベントに対応している
if(element.attachEvent){

	// キーボードを押した時に実行されるイベント
	element.attachEvent("onkeypress",function (e){

		// デフォルトの動作をキャンセル
		return false;
	});
}
 

■イベント通知の伝達を終了する


イベントの伝達(バブリング)を終了するには、cancelBubble プロパティを使用します。
 
true 値をセットします。
 
同じイベントに他のリスナーが登録されている場合、それらの処理が終わるまでは中断しません。
 
これは、DOM Event の Event.stopPropagation() メソッドに相当します。
 
イベントの伝達を終了する

<html>
  <body>

    <div style="width:400px; height:300px; background:#fcc;">
	    <span style="font-size:32px;">あいうえお</span>
    </div>

    <script type="text/javascript">
    <!--
	// ------------------------------------------------------------
	// DOM オブジェクトのすべての子孫を検索する関数
	// ------------------------------------------------------------
	function DomNodeFindAllDescendants(node,func){
		function f(n){
			var nodes = n.childNodes;
			var i;
			var num = nodes.length;
			var a = new Array();
			for(i=0;i < num;i++){ a[i] = nodes[i]; }
			for(i=0;i < num;i++){
				node = func(a[i]);
				if(node){ return node; }
				node = f(a[i]);
				if(node){ return node; }
			}
			return null;
		}
		return f(node);
	}


	// アタッチイベントに対応している
	if(window.attachEvent){

		// ------------------------------------------------------------
		// 要素にリスナーを登録する関数
		// ------------------------------------------------------------
		// リスナーA
		function ElementAttachEventMouseClickA(current){

			// アタッチイベントに対応しているか
			if(!(current.attachEvent)) return;

			// 要素にリスナーを登録
			current.attachEvent("onclick" , function (e){
				console.log("リスナーA");
				console.log("カレント:" + current + "(" + current.nodeName + ")");
				console.log("---");

				// BODY 要素である場合、イベントの伝達を終了する
				if (current.tagName == "BODY") {
					// イベント通知の伝達を終了する
					e.cancelBubble = true;
				}
			});
		}

		// リスナーB
		function ElementAttachEventMouseClickB(current){

			// アタッチイベントに対応しているか
			if(!(current.attachEvent)) return;

			// 要素にリスナーを登録
			current.attachEvent("onclick" , function (e){
				console.log("リスナーB");
				console.log("カレント:" + current + "(" + current.nodeName + ")");
				console.log("---");

				// BODY 要素である場合、イベントの伝達を終了する
				if (current.tagName == "BODY") {
					// イベント通知の伝達を終了する
					e.cancelBubble = true;
				}
			});
		}


		// ------------------------------------------------------------
		// すべてのフェーズでリッスンを開始
		// ------------------------------------------------------------
		// ドキュメントにリスナーAを登録
		ElementAttachEventMouseClickA(document);

		// ドキュメントにリスナーBを登録
		ElementAttachEventMouseClickB(document);

		// コールバック関数を使って、ドキュメントのすべての子孫を検索
		DomNodeFindAllDescendants(document,function (node){

			// ドキュメントの子孫にリスナーAを登録
			ElementAttachEventMouseClickA(node);

			// ドキュメントの子孫にリスナーBを登録
			ElementAttachEventMouseClickB(node);

			// 検索を継続
			return null;
		});
	}
    //-->
    </script>

  </body>
</html>
 

■ EventObj オブジェクトを生成する

 
EventObj オブジェクトを作成するには、document.createEventObject() メソッドを使用します。
 
イベントを発行する方法については、こちらで解説しています。
 
document.createEventObject( EventObj ) :EventObj
第01引数(略可)EventObj別の EventObj オブジェクトを複製したい場合に指定する
戻り値 EventObj新しい EventObj オブジェクト
 
イベントオブジェクトを作成

// イベントオブジェクトを作成
var event_obj = document.createEventObject();

// 出力テスト
console.log(event_obj);
 


 

イベント関連のメソッドについて

 
 


■イベント関連のメソッドについて

 
メソッド名 説明
attachEvent() リスナーを登録して、イベントの通知を受け取る。
detachEvent() リスナーを除外して、イベントの通知を止める。
fireEvent() イベントを発行する。
 


 

イベントの通知を受け取る

 
 


■コールバック関数を登録して、イベントの通知を受け取る


イベントの通知を受け取るには、 attachEvent メソッドを使用します。
 
Internet Explorer で使用すると、必ず専用の仕様で動作します。
 
このメソッドは、必ずしも、DOM オブジェクトで利用できるとは限りません。
 
例えば、テキストノードアトリビュートでは、対応していません。
 
Object.attachEvent ("イベント名" , コールバック関数 ) :Boolean
第01引数 Stringイベント名を指定。接頭の on は省略しない。
第02引数 Functionコールバック関数を指定。
戻り値 Booleanアタッチに成功した場合 true、失敗した場合 false が得られる。
 
■第01引数 イベント名

監視したいイベントハンドラ名を指定します。
 
例えば、"onload" や、"onmousemove" などを指定します。
 
■第02引数 コールバック関数

システムがイベントを発行したときに、実行して欲しいコールバック関数を指定します。
 
IE の場合、コールバック関数の、第01引数から、EventObj オブジェクトが得られます。
 
EventObj クラスについては、こちらで解説しています。
 
マウスボタンをクリックしたときに実行されるイベント

// ------------------------------------------------------------
// マウスボタンをクリックしたときに実行される関数
// ------------------------------------------------------------
function MouseClickFunc(e){
	// 出力テスト
	console.log(e);
}

// ------------------------------------------------------------
// アタッチイベント
// ------------------------------------------------------------
// アタッチイベントに対応している
if(document.attachEvent){

	// イベントのリッスンを開始する
	document.attachEvent("onclick" , MouseClickFunc);

}
 
■リスナーの多重登録に注意

第01~02引数の指定がすべて同一である場合、リスナーの登録を試みても2重に登録されることはありません。
 
第01~02引数の指定に1つでも相違がある場合、リスナーの登録を試みるたびに多重に登録されていきます
 
第02引数に、直接無名関数を渡している場合は、新規に関数を生成しているので注意します。
 

■イベントの通知の受け取りを止める


イベントの通知の受け取りを止めるには、detachEvent を使用します。
 
attachEvent メソッドで使用した、第01~02引数と同じパラメータを指定します。
 
Object.detachEvent ("イベント名" , コールバック関数 ) :void
第01引数 String登録時に指定した、イベント名。
第02引数 Function登録時に指定した、コールバック関数。
戻り値 voidなし
 
イベントの通知の受け取りを止める

// ------------------------------------------------------------
// マウスボタンをクリックしたときに実行される関数
// ------------------------------------------------------------
function MouseClickFunc(e){
	console.log("マウスボタンをクリックした");
}


// ------------------------------------------------------------
// アタッチイベント
// ------------------------------------------------------------
// アタッチイベントに対応している
if(document.attachEvent){

	// イベントのリッスンを開始する
	document.attachEvent("onclick" , MouseClickFunc );

	// イベントのリッスンを終了する
	document.detachEvent("onclick" , MouseClickFunc );
}
 

■無名関数を使ったリスナーの登録を解除するには?

 
無名関数内でリムーブ処理を行う必要があります。
 
互換性を考慮する場合、そもそも匿名関数を直接渡さない方がいいでしょう。
 
■ arguments.callee プロパティを使用する
 
arguments.callee プロパティから、自身の関数を取得する事ができます。
 
ただし、strict モードでは、利用できません。
 
無名関数を使ったイベントのリッスンを終了する(strict モードでは不可能)

// アタッチイベントに対応している
if(document.attachEvent){

	// ------------------------------------------------------------
	// イベントのリッスンを開始する
	// ------------------------------------------------------------
	document.attachEvent("onclick" , function (e){

			console.log("マウスボタンをクリックした");

			// イベントのリッスンを終了する
			document.detachEvent("onclick" , arguments.callee);

	});
}
 
■ 関数リテラルに関数名を記述する
 
関数リテラルに関数名を記述すると、自身の関数を取得する事ができます。
 
この方法は、Internet Explorer 8 以前では機能しません。
 
無名関数を使ったイベントのリッスンを終了する(IE8 以前では不可能)

// アタッチイベントに対応している
if(document.attachEvent){

	// ------------------------------------------------------------
	// イベントのリッスンを開始する
	// ------------------------------------------------------------
	document.attachEvent("onclick" , function callee(e){

			console.log("マウスボタンをクリックした");

			// イベントのリッスンを終了する
			document.detachEvent("onclick" , callee);

	});
}
 

 
 

イベントを発行する

 
 


■イベントを発行する

 
イベントを発行するには、fireEvent() メソッドを使用します。
 
カスタムイベントを発行する事はできません。
 
発行可能なイベントは、既存のイベントのみです。
 
このメソッドは、必ずしも、DOM オブジェクトで利用できるとは限りません。
 
例えば、テキストノードアトリビュートでは、対応していません。
 
また、Window オブジェクトも対応していません。
 
Object.fireEvent ( "イベント名" , EventObj ) :Boolean
第01引数 Eventイベント名を指定。未対応イベントを指定するとエラーが発生する。
第02引数(略可)EventObjリスナーに渡す EventObj を指定。省略した場合、空の EventObj が自動生成される。
戻り値 Booleantrue の場合送出に成功。イベントの伝達を中止した場合も true。
false の場合送出に失敗。リスナーがキャンセルを要求した場合も false。
 
■既存イベントの発行について
 
既存イベントを発行して、ブラウザを制御する事はできません。
 
例えば、onmousemove イベントを発行して、OS のマウスカーソルを移動できるわけではありません。
 
イベントを発行すると、任意のエレメントに登録したコールバック関数が実行されるだけです。
 
■使用例
 
onclick イベントを手動的に発行する

// アタッチイベントに対応している
if(document.attachEvent){

	// ------------------------------------------------------------
	// マウスボタンをクリックしたときに実行されるイベント
	// ------------------------------------------------------------
	document.attachEvent("onclick" , function (e) {
		console.log("イベントの通知を受け取った");
	});


	// ------------------------------------------------------------
	// イベントオブジェクト
	// ------------------------------------------------------------
	// イベントオブジェクトを作成
	var event_obj = document.createEventObject();

	// マウス用のプロパティを変更
	event_obj.clientX = 111;
	event_obj.clientY = 222;
	event_obj.screenX = 333;
	event_obj.screenY = 444;

	// ------------------------------------------------------------
	// Document からイベントを送出
	// ------------------------------------------------------------
	document.fireEvent("onclick" , event_obj);
}