JavaScriptプログラムでCSS特性を操作するということは、 マウスやキーボードでの対話的アクションをきっかけにすることが 多くなる。ユーザが起こしたマウスやキーボードの操作によって 計算機に伝わる事象のことをイベントという。ここでは、 イベント処理を行なうプログラム作成の指針を示す。
マウスクリックなど、イベントをきっかけに動くプログラムは、 処理起動対象としたいイベントについて、あらかじめその処理関数を 登録しておく必要がある。これをイベントリスナ(Event Listener)と いう。
例:
マウスクリック | → | イベントリスナ |
最も簡単な方法は、HTML要素の共通属性の onclick などに JavaScript の 関数呼出しを直接記述する方法であるが、これは
イベントリスナを登録したい要素が大量にあるとき、それら全てに 属性値を設定するのは非効率的である。
イベントリスナ関数の名前や呼び方を変えたときにHTML文書も書き換える 必要がある。
スクリプト言語がJavaScriptでなくなったときも同様の問題が発生する。
のような問題点がある。HTML文書は「文書の内容を記述するもの」として とじているのが望ましいという観点からも、イベントリスナの登録は JavaScriptプログラムだけの力で行なうのがよい。
定義した関数をJavaScript自身でイベントリスナとするためには、 リスナ登録関数を利用する。リスナ登録関数を呼ぶ方法を知る前にJavaScript プログラムが呼ばれるタイミングについて考察する必要がある。
記述したJavaScriptが最初に読み込まれるのは、HTML文書内のscript要素で JavaScriptがロードされるときであるが、このときはHTML文書の読み込みが完了 していないので、イベントリスナ登録したい要素がまだ存在していない場合 がある。そのため、イベントリスナを一括登録する関数を定義しておき、 文書を全てロードし終わったときにその関数を呼ぶようにする。
簡単な例を以下に示す。
<ul> <li id="item-1"> うじゃら <li id="item-2"> ほじゃら <li id="item-3"> でじゃら </ul>
function hello(ev) { var me = (ev.srcElement||ev.target); alert(me.id); } function addAll() { var i; // lilistに全ての li 要素を得る lilist = document.getElementsByTagName("li"); for (i=0; i<lilist.length; i++) { // 最初から順にli要素をliに代入して繰り返す li = lilist[i]; if (li.id && li.id.match(/^item-/)) { // そのli要素にid属性がありかつ /^item-/ にマッチするなら li.addEventListener("click", hello, false); } } } window.addEventListener("load", addAll, false);
上記の組み合わせの例では
addAll()
関数が定義され、
window(文書表示全体)のロード完了時の
イベントリスナを addAll 関数に設定。
addAll()
関数が呼ばれ、全てのli要素のうち
id属性の値が /^item-/ という正規表現にマッチするもののみ
addEventListener()
でイベントリスナ登録をする。
という順番で、3つのli要素をクリックしたときのイベントリスナが 登録される。
イベントリスナの登録をするときに、たくさんの要素のうちから 特定の条件を持つ要素の全てを選び出す必要がある。その方法を 2つ紹介する。
「HTML文書に含まれる同一要素全て」は
document.getElementsByTagName()
関数で
全ての要素が詰まった配列を得ることができる。たとえば全ての
p要素を得るなら
var i, item; var allp = document.getElementsByTagName("p"); for (i=0; i<allp.length; i++) { item = allp[i]; if (itemがある条件を満たすか?) { item.addEventListener(イベント, 関数, false); } }
のようにする。
「id属性になんらかの値を持つ要素」は、
document.getElementById()
関数で得られる。
その子要素全ての詰まった配列は childNodes
で得られる。
たとえば、id属性に "foo" を持つ要素の子のうち、div要素全てを
選ぶ場合は以下のようにする。
var i; var foo = document.getElementById("foo"); if (foo && foo.childNodes) { var c = foo.childNodes; for (i=0; i<c.length; i++) { if (c.tagName.match(/div/i)) { // 必要な処理 } } }
のようにする。
addEventListener
関数はIEで使えない。その問題を回避するため
addEventListener
と、IEにおいて等価の働きをする関数を
呼び分ける以下の関数を定義して利用するとよい。
function addEvent(elm, ev, f) { if (elm.addEventListener) { elm.addEventListener(ev, f, false); } else if (elm.attachEvent) { elm.attachEvent("on"+ev, f); } else { elm["on"+ev] = f; } }
これを利用して simpleadd.html
を書き直したものが wrapadd.html
である。
イベントリスナとなる関数は、引数ひとつを取る形で定義する(※)。
function someListener(ev) { : : }
※ 引数なしで宣言してもよい。
この関数が呼ばれるときに与えられる引数はイベントオブジェクト
といい、それを介して発生したイベントの種類や位置(座標)を知ることができる。
上の例のように仮引数が ev
だとした場合、イベントに関する
情報は以下の変数で取得できる。
意味 | 値 | IEの場合 |
---|---|---|
イベントが発生した要素 | ev.target | ev.srcElement |
スクリーン座標X | ev.screenX | ← |
スクリーン座標Y | ev.screenY | ← |
ウィンドウ座標X | ev.clientX | ← |
ウィンドウ座標Y | ev.clientY | ← |
イベントの種類 | ev.type | ← |
押したキーのコード | ev.keyCode | ← |