各種イベントについて

 JavaScriptでは数多くのイベントが用意されています。その中でAjaxで多く使用されるのは以下のイベントでしょう。

【マウス関連】
  1. click(クリックされた)
  2. dblclick(ダブルクリックされた)
  3. mousedown(マウスの左ボタンが押された)
  4. mouseup(マウスの左ボタンが離された)
  5. contextmenu(右ボタンがクリックされた)
  6. mouseover(マウスが重なった)
  7. mouseout(マウスが離れた)
  8. mousemove(マウスが動いた)
【キー関連】
  1. keydown(キーが押された)
  2. keyup(キーが離された)
【入力フォーム関連】
  1. submit(入力フォームデータ送信時)
  2. reset(リセットボタンが押された時)
  3. change(selectメニューで異なる項目が選択された)
  4. focus(フォーカスした時)
  5. blur(フォーカスがなくなった時)
【ページ関連】
  1. load(読み込みが完了した)
  2. unload(他のページに移動した)
  3. resize(大きさが変更された)
  4. move(移動した)

 各イベントに関しては次項で説明するとして、ここではイベントの記述方法について見てみましょう。現在でも最も多く見られるのが以下のようにタグ内にイベント名と処理内容を記述するものです。以下のサンプルではdivタグ内をクリックするとメッセージが表示されます。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベント設定(従来の方法でタグに記述)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
alert("クリックされました");
}
// --></script>
</head>
<body>
<h1>イベント設定(従来の方法でタグに記述)</h1>
<div id="mainContents" onClick="check()">
メインコンテンツが表示される部分です。
</div>
</body>
</html>

 この方法は非常に安定していて多くのブラウザで動作します。どうしてもうまく動かない事態が発生し、他の方法で対応できない場合には、このようにHTMLタグ内に記述することになります。Ajaxが使えるブラウザでは、あまりそのようなことはありません。
 タグ内にイベント処理を記述するとアクセシビリティが低下してしまう、後から他のイベントに変更しにくい、HTMLとJavaScriptが混在しメンテナンス性も低下してしまいます。そこで、HTMLはHTMLとして、JavaScriptで後からイベント処理を追加することができます。上記サンプルのonclickを後付けで設定するには以下のようにオブジェクト名.onclick = 処理内容のように記述します。ちなみにonClickのようにonの後を大文字にはしないようにしてください。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベント設定(従来の方法でタグに記述)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
alert("クリックされました");
}
function addEvent(tagID)
{
document.getElementById(tagID).onclick = check;
}
function removeEvent(tagID)
{
document.getElementById(tagID).onclick = null;
}
// --></script>
</head>
<body>
<h1>イベント設定(従来の方法で後から記述)</h1>
<div id="mainContents">
メインコンテンツが表示される部分です。
</div>
<form>
<input type="button" value="divタグにイベントを設定する" onClick="addEvent('mainContents')"><br>
<input type="button" value="divタグのイベントを削除する" onClick="removeEvent('mainContents')"><br>
</form>
</body>
</html>

 設定したイベントを解除するにはnullを設定します。基本的には、このような方法でイベントの設定と解除を行う事ができます。当面は、これで必要十分でしょう。

 ここから先は、別の方法でイベントを設定します。Internet Explorer以外でのW3Cに準拠したブラウザで利用できる方法です(Flashでも同じ命令が用意されていて利用することができます)。まず、イベントを追加するには、イベントを追加したいオブジェクトにaddEventListener()メソッドで設定を行います。addEventListener()の最初の引数がイベント名になります。このイベント名には上記のようなonは付きません。純粋にイベント名を記述します。次がイベントが発生した場合に実行される関数名です。最後がイベントの伝達方向になります。trueを設定するとOperaで正しく動作しないことがあります。あとで説明しますが、通常はfalseを指定しておきます。
 追加したイベントを削除するにはremoveEventListener()を使います。これは指定したイベントの指定した関数を削除します。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベント設定(addEventListenerで記述)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
alert("クリックされました");
}
function addEvent(tagID)
{
document.getElementById(tagID).addEventListener("click",check,false);
}
function removeEvent(tagID)
{
document.getElementById(tagID).removeEventListener("click",check,false);
}
// --></script>
</head>
<body>
<h1>イベント設定(addEventListenerで記述)</h1>
<div id="mainContents">
メインコンテンツが表示される部分です。
</div>
<form>
<input type="button" value="divタグにイベントを設定する" onClick="addEvent('mainContents')"><br>
<input type="button" value="divタグのイベントを削除する" onClick="removeEvent('mainContents')"><br>
</form>
</body>
</html>

 従来の方法と異なるのはaddEventListener()によって同一のイベントに複数のイベントハンドラ(処理する関数、命令)を追加できることです。イベントが発生するとaddEventListener()で追加された順番に指定した関数が実行されます。removeListener()では、addEventListener()で追加された特定のイベントの特定の処理だけ削除することができます。(実際のサンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベント設定2(addEventListenerで記述)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
alert("クリックされました");
}
function check2()
{
alert("[2]クリックされました");
}
function addEvent(tagID,funcName)
{
document.getElementById(tagID).addEventListener("click",funcName,false);
}
function removeEvent(tagID)
{
document.getElementById(tagID).removeEventListener("click",check,false);
}
// --></script>
</head>
<body>
<h1>イベント設定2(addEventListenerで記述)</h1>
<div id="mainContents">
メインコンテンツが表示される部分です。
</div>
<form>
<input type="button" value="divタグにイベントを設定する(check)" onClick="addEvent('mainContents',check)"><br>
<input type="button" value="divタグにイベントを設定する(check2)" onClick="addEvent('mainContents',check2)"><br>
<input type="button" value="divタグのイベントを削除する(checkのみ削除)" onClick="removeEvent('mainContents')"><br>
</form>
</body>
</html>

 addListener()、removeListener()はFirefox, Safari, Operaなどで動作しますが、最もシェアの多いInternet Explorerでは動作しません。ただ、似たような機能を持つメソッドが用意されています。それがattachEvent()とdetachEvent()です。それぞれaddListener()とremoveListener()と同じようにイベント処理の追加と削除ができます。引数(パラメータ)は2つです。最初の引数がイベント名、次が処理する関数名になります。イベント名はaddListener()、removeListener()と異なりonclickのように先頭にonが付きます。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベント設定(attachEventで記述)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
alert("クリックされました");
}
function addEvent(tagID)
{
document.getElementById(tagID).attachEvent("onclick",check);
}
function removeEvent(tagID)
{
document.getElementById(tagID).detachEvent("onclick",check);
}
// --></script>
</head>
<body>
<h1>イベント設定(attachEventで記述)</h1>
<div id="mainContents">
メインコンテンツが表示される部分です。
</div>
<form>
<input type="button" value="divタグにイベントを設定する" onClick="addEvent('mainContents')"><br>
<input type="button" value="divタグのイベントを削除する" onClick="removeEvent('mainContents')"><br>
</form>
</body>
</html>

 ここまでは、divタグが1つで、その中にはdivタグや他のタグは記述されていませんでした。Ajaxではdivタグ内に複雑にタグが入ることがあり、それぞれのタグにイベントが設定されることがあります。以下のサンプルのように、それぞれのdivタグにclickイベントを記述します。一番内側のdivタグ内容をクリックしてみてください。(サンプルを実行する。addListener()の場合のサンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベントの伝達</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check(msg)
{
alert(msg);
}
// --></script>
</head>
<body>
<h1>イベントの伝達</h1>
<div id="mainContents" onClick="check('外側のdivタグ')">
外側のdivタグ
<div id="inner1" onClick="check('内側のdivタグ')">
内側のdivタグ
<div id="inner2" onClick="check('一番内側のタグ')">
一番内側のdivタグ
</div>
</div>
</div>
</body>
</html>

 実行すると以下のように順番にメッセージが表示されます。
  1. 一番内側のタグ
  2. 内側のdivタグ
  3. 外側のdivタグ
 これは一番内側のタグ(オブジェクト)から外側のタグに向かってクリックイベントが次々と伝達されていくためです(上位、親オブジェクトに伝達されていきます。イベントバブルアップ)。このようにイベントが伝達されると問題になることがあります。このような場合に備えてイベントの伝達を停止させるための命令が用意されています。イベントの伝達を停止させる命令はInternet Explorerと、それ以外のブラウザで異なります。Firefox、Safari、Operaの場合にはイベントオブジェクトに対してstopPropagation()メソッドを使います。(実際のサンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベントの伝達を停止(Firefox)</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check(evt)
{
alert(evt.target.id);
evt.stopPropagation();
}
window.onload = function()
{
document.getElementById("mainContents").addEventListener("click",check,false);
document.getElementById("inner1").addEventListener("click",check,false);
document.getElementById("inner2").addEventListener("click",check,false);
}
// --></script>
</head>
<body>
<h1>イベントの伝達を停止(Firefox)</h1>
<div id="mainContents">
外側のdivタグ
<div id="inner1">
内側のdivタグ
<div id="inner2">
一番内側のdivタグ
</div>
</div>
</div>
</body>
</html>

 Internet ExplorerではeventオブジェクトのcancelBubbleプロパティにtrueを入れるとイベントの伝達が停止されます。(実際のサンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベントの伝達を停止</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check(msg)
{
alert(msg);
window.event.cancelBubble = true;
}
// --></script>
</head>
<body>
<h1>イベントの伝達を停止</h1>
<div id="mainContents" onClick="check('外側のdivタグ')">
外側のdivタグ
<div id="inner1" onClick="check('内側のdivタグ')">
内側のdivタグ
<div id="inner2" onClick="check('一番内側のタグ')">
一番内側のdivタグ
</div>
</div>
</div>
</body>
</html>

 イベントの追加や削除、イベントの伝達の停止などブラウザによって異なっているため面倒といえば面倒です。このような場合には、とりあえず共通で利用できる関数を用意しておくと便利です。このような場合にはブラウザ別の処理を行うのが普通です。ブラウザの判別はnavigator.userAgentの文字列を参照し処理します。が、userAgentが必ずしもそのブラウザを示しているとは限りません。このような場合は以下のようにwindow.addEventListenerメソッドが存在するかどうかを調べます。(サンプルを実行する

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>addListenerが使えるか判別</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript"><!--
function check()
{
if(window.addEventListener)
{
alert("addEventListenerは使えます");
}else{
alert("addEventListenerは使えせん・・・");
}
}
// --></script>
</head>
<body>
<h1>addListenerが使えるか判別</h1>
<form>
<input type="button" value="チェック" onClick="check()"><br>
</form>
</body>
</html>

 これを利用して以下のように共通の関数を用意します。これを別ファイル(.js)に登録しておけばInternet Explorer、Firefox、Safari、Operaで共通に利用できます。

【xmlhttp.js】
// イベント追加
function addEvent(tagID,eventName,funcName,bFlag)
{
if(window.addEventListener)
{
document.getElementById(tagID).addEventListener(eventName,funcName,bFlag);
}else{
document.getElementById(tagID).attachEvent("on"+eventName,funcName);
}
}
// イベント削除
function removeEvent(tagID,eventName,funcName,bFlag)
{
if(window.removeEventListener)
{
document.getElementById(tagID).removeEventListener(eventName,funcName,bFlag);
}else{
document.getElementById(tagID).detachEvent("on"+eventName,funcName);
}

}
// イベントバブルアップ停止
function cancelEvent(evtObj)
{
if(window.addEventListener)
{
evtObj.stopPropagation();
}else{
window.event.cancelBubble = true;
}
}
// イベントが発生したオブジェクトの情報を取得
function getEventTarget(evt)
{
if(window.addEventListener)
{
return evt.target;
}else{
return event.srcElement;
}
}

【使用サンプル】
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=shift_jis">
<title>イベントテスト</title>
<link rel="stylesheet" href="main.css" type="text/css" media="all">
<script type="text/javascript" src="xmlhttp.js"></script>
<script type="text/javascript"><!--
function check(evt)
{
alert(getEventTarget(evt).id);
cancelEvent(evt);
}
function addDivEvent()
{
addEvent("mainContents","click",check,false);
addEvent("inner1","click",check,false);
addEvent("inner2","click",check,false);
}
function removeDivEvent()
{
removeEvent("mainContents","click",check,false);
removeEvent("inner1","click",check,false);
removeEvent("inner2","click",check,false);
}
// --></script>
</head>
<body>
<h1>イベントテスト</h1>
<form>
<input type="button" value="DIVタグにclickイベントを追加" onClick="addDivEvent()"><br>
<input type="button" value="DIVタグのclickイベントを削除" onClick="removeDivEvent()"><br>
</form>
<div id="mainContents">
外側のdivタグ
<div id="inner1">
内側のdivタグ
<div id="inner2">
一番内側のdivタグ
</div>
</div>
</div>
</body>
</html>

 このようにすれば各種イベントを設定、削除することができます。次項はイベント処理の注意点について説明します。

[第五章 10:イベント処理の注意点へ]
[目次へ]

(2006.1.16)