カテゴリー
オライリーJavaScript

キーボードイベント

keydownイベント

ユーザがキーボード上のキーを押したときに発生する

keyupイベント

ユーザがキーボード上のキーを離したときに発生する

リピートするほどユーザがキーを押し続けた場合、複数のkeydownイベントが発生した後、 keyupイベントが発生する。

これらのイベント用のイベントオブジェクトには、数値を格納するkeyCodeプロパティが定義されている

keyCodeプロパティで押下されたキーを表す

印字可能文字を入力するキーの場合、そのキーを表す主たるUnicodeが格納されるのが普通

英字キーの場合 常に大文字のKeyCodeになる Shiftキーの状態は関係ない

数字キーの場合 常にキー上に印字されている数字 Shiftキーを押して記号を入力している場合でも数字のKeyCodeになる

非印字可能文字キーの場合 何らかのほかの値になる

マウス用のイベントオブジェクトと同様に、キー用のイベントオブジェクトには、 altKey、ctrlKey、metaKey、shiftKeyプロパティが定義されている

イベントが発生したときに、それぞれ対応する修飾キーが謳歌されていればtrueになる

WebkitベースのブラウザのSafariとChromeでは、これらのイベント用のイベントオブジェクトに、 keyIdentifierプロパティを定義している。

keyIdentifierプロパティは、数値ではなく文字列。

カテゴリー
オライリーJavaScript

テキストイベント

ブラウザには、キーボード入力に関して、レガシーなイベントが3種類ある

keydown、keyupイベント 低レベルイベント

keypressイベント 高レベルイベント 印字可能な文字が生成されたことを通知する

DOM Level 3 Eventではもっと一般的な textinputイベントが定義されている

入力方法にかかわらず、ユーザがテキストを入力したときに、このイベントが発生する

Webkitブラウザでは、非常によく似た textInputイベントがサポートされている

textinput、textInputイベントでは、detaプロパティを持つ単純なイベントオブジェクトが渡される。

この、detaプロパティには、入力されたテキストが格納されている。

キーボード入力の場合は、detaプロパティに1文字だけ格納される。 しかし、他の入力方法の場合は、複数の文字が格納されているのが普通。

keypressイベントで渡されるイベントオブジェクトはもっと複雑 keypressイベントは1文字入力を表す。

イベントオブジェクトでは、この文字をUnicodeの符号位置(数値)で表す。

そして、String.fromCharCode()を使えば文字列に変換できる。

多くのブラウザでは、イベントオブジェクトのkeyCodeプロパティに、入力文字の符号位置が格納される。

しかし、Firefoxでは、charCodeを使う。

多くのブラウザでは、印字可能文字列が入力されたときだけ、keypressイベントが発生する。

しかし、Firefoxでは、非印字可能文字に対しても、keypressイベントが発生する。 このような場合を識別して、非印字可能文字を無視できるようにするには、 イベントオブジェクトのcharCodeプロパティの値が0かどうかを調べる

textinput、textInput、keypressイベントをキャンセルすることで、文字入力を抑止できる。

つまり、これらのイベントを使うことで、入力をフィルタできる。

textinput、keypressイベントは、新たに入力されたテキストが、 フォーカスのあるドキュメント要素に挿入される前に発生する。

したがって、これらのイベントのイベントハンドラでイベントをキャンセルすれば、テキストの挿入を抑止できる。

また、ブラウザは、テキストが要素に挿入された後に発生するinputイベントタイプも実装している。

このイベントはキャンセルできない。また、イベントオブジェクト中には新しいテキストは含まれない。

要素のコンテンツテキストが何らかの方法で変更されたことだけを通知する。

カテゴリー
オライリーJavaScript

マウスホイールイベント

ユーザがマウスホイールを回転させると、マウスホイールイベントが発生する。 ブラウザでは、ドキュメントをスクロールしたり、拡大縮小したりするためにマウスホイールを使います。

しかし、マウスホイールイベントをキャンセルすることで、このようなデフォルトの動作を抑止できる

Firefoxを除く全てのブラウザ mousewheelイベントをサポート

wheelDelta

Firefox DOMMouseScrollイベント

detailプロパティ

detailの値に-40を乗算すれば、wheelDeltaの値と同じになる

DOM Level3 Events wheelイベント

wheelイベントハンドラに渡されるイベントオブジェクト

deltaX、deltaY、deltaZプロパティ

これらの値に-120を乗算すれば、mousewheelイベントの値と符号に一致する

mousewheelハンドラに渡されるイベントオブジェクト

wheelDataプロパティが定義されている

ユーザがホイールをどれだけ回転したかを示す

マウスホイールをユーザから離れる方向に1クリック分回すと120という値になる

逆にユーザ方向に1クリック分回すと-120という値になる

Apple社のマウスは2次元のトラックボールを内蔵している

Apple社のマウスをサポートするために、SafariとChromeでは、 wheelDeltaのほかに、wheelDeltaXとwheelDeltaYがイベントオブジェクトに定義されている

wheelDeltaとwheelDelteYは常に同じになる。

これらのすべてのイベントタイプにおいて、イベントオブジェクトはマウスイベント用のものとよく似ている マウスポインタの位置や、キーボードの修飾キー状態が含まれている

カテゴリー
オライリーJavaScript

マウスイベント

マウスに関連したイベントはたくさんある。

click contextmenu dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave

マウスイベントはすべてバブリングする。

ただし、mouseenter、mouseleaveは例外 リンクに対するclickイベントと、Submitボタンはデフォルトの動作を持つ。 この動作は抑止できる。

contextmenuイベントをキャンセルすれば、コンテキストメニューが表示されないようにもできる。

マウスイベントハンドラに渡されるイベントオブジェクト

clientXプロパティ clientYプロパティ altKey、ctrlKey、metaKey、ShiftKeyプロパティ buttonプロパティ

drag()関数 mousedownイベントハンドラから呼び出され、絶対位置指定したドキュメント要素をユーザがドラッグできるようにする。

DOMイベントモデルでも、IEイベントモデルでも動作する。

ドキュメント要素をドラッグする

/*
*Drag.js:絶対位置指定されたHTML要素をドラッグする
*
*このモジュールはdrag()関数を定義する
*イベントハンドラから呼び出される。
*この後のmousemoveイベントで、指定された要素を移動する
*mouseupイベントでドラッグをやめる
*この実装は、標準イベントモデルでも、IEイベントイベントモデルでも動作する
*例15-8のgetScrollOffsets()関数を必要とする
*
*引数:
* elementToDrag:mousedownイベントが発生した要素か、
*	その要素を包含する要素。
*	この要素は絶対位置指定していなければならない
*	style.leftとstyle.topの値が、ユーザのドラッグに応じて変更される
* event: mousedownイベントのEventオブジェクト
*/
function drag(elementToDrag, event) {
	//マウスの初期位置。ドキュメント座標に変換される
	var scroll = getScrollOffsets();	//例 15-8で定義されているユーティリティ関数
	var startX = event.clientX + scroll.x;
	var startY = event.clientY + scroll.y;

	//ドラッグされる要素の初期位置(ドキュメント座標)
	//elementToDragは絶対位置指定されるので、offsetParentがドキュメントボディと想定している
	var origX = elementToDrag.offsetLeft;
	var origY = elementToDrag.offsetTop;

	//mousedownイベントと要素の左上角の距離を計算する
	//マウスが移動している間、この距離が維持されるようにする
	var deltaX = startX - origX;
	var deltaY = startY - origY;

	//mousemoveイベントとmouseupイベント用のイベントハンドラを登録する
	if (document.addEventListener) {	//標準イベントモデル
		//キャプチャリングイベントハンドラをドキュメントに登録する
		document.addEventListener("mousemove", moveHandler, true);
		document.addEventListener("mouseup", upHandler, true);

	} else if(document.attachEvent) {	//IE5~8用のIEイベントモデル
		//IEイベントモデルでは、イベントをキャプチャしたい要素に対して
		//setCapture()を呼び出すことで、イベントをキャプチャする
		elementToDrag.setCapture();
		elementToDrag.attachEvent("onmousemove", moveHandler);
		elementToDrag.attachEvent("onmouseup", upHandler);
		//マウスキャプチャを失った場合は、mouseupとして処理する
		elementToDrag.attachEvent("onlosecapture", upHandler);
	}

	//このイベントは処理が完了したので、ほかの要素が処理しないようにする
	if (event.stopPropagetion) event.stopPropagetion();	//標準モデル
	else event.cancelBubble = true;	//IE

	//デフォルトの動作を抑止する
	if (event.preventDefault) event.preventDefault();	//標準モデル
	else event.returnValue = false;	//IE

	//要素がドラッグされている間、mosemoveイベントをキャプチャする
	//ハンドラ。要素の移動処理を行う
	function moveHandler(e) {
		if (!e) e = window.event;	//IEイベントモデル

		//要素を現在のマウス位置まで移動する
		//スクロールバーの位置や、
		//最初のマウスクリック時のオフセットを計算に入れて調整する必要がある
		var scroll = getScrollOffsets();
		elementToDrag.style.left = (e.clientX + scroll.x - deltaX) + "px";
		elementToDrag.style.top = (e.clientY + scroll.y - deltaY) + "px";

		//このイベントをほかの場所で処理されないようにする
		if (e.stopPropagetion) e.stopPropagetion();
		else e.cancelBubble = true;
	}

	//ドラッグの最後で発生するmouseupイベントをキャプチャするハンドラ
	function upHandler(e) {
		if (!e) e = window.event;	//IEイベントモデル

		//キャプチャリングイベントハンドラを登録解除する
		if (document.removeEventListener) {	//DOMイベントモデル
			document.removeEventListener("onmouseup", upHandler, true);
			document.removeEventListener("onmousemove", moveHandler, true);
		} else if (document.detachEvent) {	//IE5以降のイベントモデル
			elementToDrag.detachEvent("onlosecapture", upHandler);
			elementToDrag.detachEvent("onmouseup", upHandler);
			elementToDrag.detachEvent("onmousemove", moveHandler);
			elementToDrag.releaseCapture();
		}

		//これ以上、イベントを伝播しないようにする
		if (e.stopPropagetion) e.stopPropagetion();	//標準モデル
		else e.cancelBubble = true;	//IE
	}
}

ウィンドウのスクロールバーの位置を取得する

// 現在のスクロールバーの位置を、オブジェクトのx,yプロパティに格納して返す
function getScrollOffsets(w) {
	//指定したウィンドウ(引数なしの場合は現在のウィンドウ)を使う
	w = w || window;

	//以下は、すべてのブラウザ(IE8以前を除く)で動作する
	if (w.pageXOffset != null) return {x: w.pageXOffset, y:w.pageYOffset};

	//標準モードのIE用(任意のブラウザ用)
	var d = w.document;
	if (document.compatMode == "CSS1Compat")
		return {x:d.documentElement.scrollLeft, y:d.documentElement.scrollTop};

	//クァークスモードのブラウザ用
	return {x: d.body.scrollLeft, y: d.body.scrollTop };
}
カテゴリー
オライリーJavaScript

ドキュメントのloadイベント

多くのWebアプリケーションでは、ドキュメmンとがロードされ、操作できるようになったことを、 ブラウザから通知してもらわないとならない。 Windowオブジェクトのloadイベントがこの目的にために用意されている ドキュメントや画像がすべて読み込まれると、loadイベントが発生する。

onload:ドキュメントが読み込まれたときに関数を呼び出す

// ドキュメントの読み込みが完了したときに実行する関数fを登録する
// ドキュメントが読み込み済みの場合は、できるだけ早く非同期で呼び出す
function onLoad(f) {
	if(onLoad.loaded) 		//ドキュメントが読み込み済みであれば、
		window.setTimeout(f, 0); 	//できるだけ早く f が実行されるようにする
	else if (window.addEventListener) 	//イベント登録用の標準メソッド
		window.addEventListener("load", f, false);
	else if (window.attachEvent) 			//IE8以前ではこのメソッドを使う
		window.attachEvent("onload", f);
}

// ドキュメントの読み込みが終わっていないことを示すフラグを設定する。
onLoad.loaded = false;
// そして、ドキュメントが読み込まれたときにフラグを設定する関数を登録する。
onLoad(function(){ onLoad.loaded = true; });


ドキュメントが読み込まれ、解釈され、遅延実行指定されたスクリプトの実行が完了したら、 DOMContentLoadedイベントが発生する。 ドキュメントのロードにつれて、document.readyStateプロパティが変化する

以下の例で、whenReady()関数を定義する。

/*
*whenReady()に関数を引数で渡す
*ドキュメントが解釈され操作可能になったときに、
*この関数がドキュメントのメソッドとして呼び出される
*登録した関数は、
*DOMContentLoaded、readyStatechange、loadイベントが発生したときに呼び出される
*ドキュメントが準備でき、すべての関数が呼び出されていたら、
*whenReady()に渡された関数がすぐに呼び出される
*/
var whenReady = (function() {	//この関数はwhenReady()関数を返す
	var funcs = [];	//イベントが発生したときに実行する関数
	var ready = false;	//イベントハンドラが呼び出されたときにtrueに変更する

	//ドキュメントの準備ができたときに呼び出されるイベントハンドラ
	function handler(e) {
		//一度実行していれば、すぐに戻る
		if(ready) return;

		//readystatechangeイベントの場合で、状態が「complete」以外の状態に
		//変わった場合は、まだ準備ができていない
		if(e.type === "readystatechange" && document.readyState !== "complete")
			return;

		//登録された関数をすべて実行する
		//毎回funcs.lengthを調べていることに注意
		//ある関数を呼び出したときに、されに別の関数が登録されている場合を考慮している
		for (var i = 0; i < funcs.length; i++) {
			funcs[i].call(document);
		}

		//ここで、readyフラグをtrueに設定し、関数を忘れる
		ready = true;
		funcs = null;
	}

	//受信したいイベント用にハンドラを登録する
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", handler, false);
		document.addEventListener("readystatechange", handler, false);
		window.addEventListener("load", handler, false);
	} else if(document.attachEvent) {
		document.attachEvent("onreadystatechange", handler);
		window.attachEvent("onload", handler);
	}

	//whenReady関数を返す
	return function whenReady(f) {
		if (ready) f.call(document);	//すでに準備ができていたら、実行するだけ
		else funcs.push(f);	//まだ準備できていない場合は、キューに入れる
	}
}())

カテゴリー
オライリーJavaScript

イベントのキャンセル

addEventListener()メソッドで登録したイベント
preventDefault()メソッドでデフォルトの動作をキャンセルできる
イベントオブジェクトのreturnValueプロパティをfalseに設定する

function cancelHandler(event) {
	var event = event || window.event;

	イベント処理

	if (event.preventDefault) event.preventDefault();
	if (event.returnValue) event.returnValue = false;
	return false;
}

defaultPreventedプロパティ

イベントオブジェクトのstopPropagation()メソッド
イベント伝播を抑止できる

cancelBubbleプロパティ
stopImmediatPropagation()メソッド

カテゴリー
オライリーJavaScript

イベント伝播

イベントターゲットがWindowオブジェクトやXMLHttpRequestなどの単独で存在するオブジェクトの場合
ブラウザは、イベントが発生したときに、このオブジェクト自体の適切なイベントハンドラを呼び出すだけです。

しかし、イベントターゲットがDocumentやドキュメント要素の場合はもっと複雑

イベントターゲット要素上に登録されたイベントハンドラが呼び出された後、イベントはDOMツリー構造をバブリングする。
イベントターゲットの親要素 -> ・・・ -> Documentオブジェクト
にたどり着くまで繰り返され、さらにWindowオブジェクトまで進む

イベントバブリングにより、個々のドキュメント要素にたくさんのイベントハンドラを登録しなくても済むようになる。
共通の祖先要素に1つだけイベントハンドラを登録しておき、イベントをこのイベントハンドラで処理できる。

focus、blur、scrollイベントは例外。バブリングしない。
loadイベントは、Documentオブジェクトまでバブリングする。Windowオブジェクトまではバブリングしない。

バブリング
フェーズ1 キャプチャリング
フェーズ2 イベントターゲットオブジェクト自身のイベントハンドラ呼び出し
フェーズ3 バブリング

addEventListener()メソッドの3番目の引数
trueにすると、イベントハンドラはキャプチャリングイベントハンドラとして登録される。
バブリングの最初のフェーズで呼び出される。

キャプチャリングフェーズはバブリングフェーズと反対の動きをする。
Windowsオブジェクト -> Documentオブジェクト -> ボディオブジェクト ->イベントターゲットの親要素のキャプチャリングイベントハンドラが呼び出される
DOMのツリー構造を下っていく。
イベントターゲット自身のキャプチャリングイベントハンドラは呼び出されない。

イベントキャプチャリングにより、イベントがイベントターゲットに伝わる前に、イベントを覗くことができる。
イベントキャプチャリングがよく使わる場合は、マウスドラッグを処理するとき

カテゴリー
オライリーJavaScript

イベントハンドラの呼び出し順序

イベントハンドラプロパティの設定

イベントハンドラ属性の設定

の方法で設定したイベントハンドラが最初に呼び出される。

addEventListener()

で登録した場合は、登録順で呼び出される

attachEvent()

で登録した場合は、順不同で呼び出される。
シーケンシャルに呼び出されることを前提にコードを書いてはいけない。

カテゴリー
オライリーJavaScript

イベントハンドラの戻り値

一般的に、戻り値でfalseを返した場合、ブラウザに対して、そのイベントのデフォルトの動作をしないように指示したことになる。

Windowオブジェクトのonbeforeunloadハンドラの戻り値も重要
このイベントは、ブラウザが新しいページに移動する直前に発生する。

イベントハンドラプロパティの設定

イベントハンドラ属性の設定

の方法でイベントハンドラを登録した場合、上記の戻り値を考慮する。

addEventListener()

attachEvent()

の方法でイベントハンドラを登録した場合、
preventDefault()メソッドを呼び出すか、
イベントreturnValueプロパティのを設定する
上記、戻り値の方法は考慮しない。

カテゴリー
オライリーJavaScript

イベントハンドラのスコープ

すべてのJavaScript関数と同じように、イベントハンドラは構文スコープを持つ。
つまり、イベントハンドラは定義されたときのスコープで実行される。
呼び出されたときのスコープではない。

イベントハンドラ属性の設定

の方法で登録されたイベントハンドラは特別