最近のWebサイトは非常にインタラクティブになっています。動的なナビゲーション・メニューや画像の転回、さらにドラッグ・アンド・ドロップまでもが利用可能になっています。そうしたサイトの全てに共通する要素として、文書の特定部分に振る舞いを関連付ける何らかの手法が使われていることが挙げられます。残念ながら現在使われている手法は、(特に複数のブラウザでコードを動作させる必要がある場合には)スクリプトを偏重した手法の寄せ集めとなっています。
リスト1は、よくありそうな一例を示しています。ここでは、
load
イベント用と、
unload
イベント用の2つのイベント・ハンドラを定義しています。一つはVBScriptで書かれており、
script
要素に特別な属性を含めるという、あまり一般的ではない手法で配置されています。もう一つの方は、ECMAscript
(
参考文献
参照)で書かれています。
リスト1. 2種類のイベント
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Intrinsic Events</title>
<script event="onload" for="window" language="vbscript">
<!--
alert "hello"
-->
</script>
</head>
<body onunload="alert('goodbye')">
</body>
</html>
|
Microsoft Internet ExplorerやVBScriptをサポートする別のブラウザで見ると、この文書をロードした途端に「hello」と表示された警告ボックスが出現します。またウィンドウを閉じる時、あるいは別のページに移る時には、ほとんど全てのブラウザで「goodbye」が出現します。ここで起きていることを完全に説明するには、現在Document Object Model (DOM) Level 2 Events Specification ( 参考文献 参照)というタイトルのW3Cの文書で規定されている、DOM Eventsを理解する必要があります。
イベント
というのは一体どんなものなのでしょうか・・・。ここでの議論では、従来の言葉の意味から「イベント」を考えるのではなく、変化しつつある何かに関する詳細を含む単純なデータ構造として考えた方が良いでしょう。例えば
mouseover
イベントでは、イベント・オブジェクトの持つ情報としての関心対象は、マウス・ポインタの座標、どのマウス・ボタンが押下されているか、「shiftキー」のような修飾キーが現在押下されているかどうか・・・、などです。個々のイベント・オブジェクトは短命ですが、消滅する前に即時処理されるには、充分な時間存在します。
それぞれのイベントには、
ターゲット
(そのイベントと最も密接に関連したXML要素あるいはHTML要素)があります。イベント・
ハンドラ
は、特定のイベントに応答する、一つの実行可能コードまたはマークアップです。ハイパーリンクのクリックなど一部のイベントは、どのハンドラとも異なり、デフォルト・アクションと呼ばれる動作を引き起こします。
リスト1
では、
unload
イベントのターゲットは
body
要素であり、イベント・ハンドラは
onunload
属性内のちょっとしたスクリプトです。
最も容易で最も一般的な技法は、イベント・ハンドラをターゲットにそのまま付加するもので、かつてブラウザがサポートしたのはこの技法だけでした。ところがこの技法は多くの場合、実用的ではありません。リスト2はその一例です。
リスト2. イベント・オブザーバの必要性を示す例
<p>You want to capture all clicks on this paragraph,
even if the text has <em>special markup</em>.</p>
|
リスト2
において、
p
要素をターゲットにした
click
イベントを処理するイベント・ハンドラがあると仮定してください。もしユーザが偶然「special
markup」という言葉をクリックしたなら、
em
要素をターゲットとしたイベントが生成されるでしょう。段落がターゲットではないので、その段落のクリック・ハンドラは発動しないでしょう。これがうまく動作するようにするためには、
p
要素に対してイベント・
オブザーバ
を配置し、
p
自身またはその子のどれかをターゲットとしたイベントに反応するようにする必要があります。
DOM Level 2 Eventsでは、 リスト2 で示した問題を解決するためにイベント・ハンドラをどのように接続するかを記述しています。ただし昔のブラウザの歴史から、必要以上に複雑になってしまっています(初期のInternet ExplorerやNetscapeはそれぞれ特有の独自性があり、その一部は公式なDOM仕様の新規定から除外されています)。イベントは 伝搬(propagation) として知られる仮想的な移動を行い、文書のツリー構造をたどって運ばれます。実は2種類の経路をとり、 キャプチャ と呼ばれる最初の経路は、文書のルートから始まってターゲット要素まで進みます。通常のターゲット処理の後、 バブリング と呼ばれる2番目の経路はターゲット要素から始まり、文書のルートに戻って終わります。それぞれのフェーズで、移動経路上にある要素はどれもイベントのオブザーバとして登録され、従ってイベント・ハンドラを起動することができます。イベントは、それ以上伝搬されないように停止することもでき、それによって後にあるオブザーバがイベントの発生を検出しないように防止することができます。図1は伝搬を示しています。
図1. イベントの伝搬
イベント・オブザーバを設定する必要がある場合には、キャプチャ・フェーズを使うべきでしょうか、それともバブリング・フェーズを使うべきでしょうか・・・? 幾つかの指針を下記に示します。
- 一つの観察点のみを相手にしているのであれば(通常はそうでしょう)、キャプチャ・フェーズ、バブリング・フェーズのいずれでも使用できます。実際的な差はありません。単一のイベントが文書ツリーの複数の場所から観察されている場合にのみ、2つの違いが重要となるのです。
-
focusなどの一部のイベントは、バブリング・フェーズに関与しないので、キャプチャ・フェーズの間か、あるいはターゲットにおいてのみ直接観察することができます。 -
複数のオブザーバが存在する場合で、よりターゲットに近いオブザーバが最初に起動して欲しいのであれば、バブリング・フェーズを使います。例えば
図1
で、
bodyとpの両方に対してオブザーバが存在すると仮定した場合。キャプチャ・フェーズでは、bodyに対するオブザーバが最初に起動し、次にpに対するオブザーバが起動します。バブリングでは、pに対するオブザーバが最初に起動し、次にbodyに対するオブザーバが起動します。 -
リスト1
の
onunloadに使われているHTML 4.0形式の属性構文では、バブリング・フェーズに対してオブザーバを登録していることを忘れないでください。 - バブリング・フェーズまたは、キャプチャ・フェーズのどちらが使われるにしても、デフォルト・アクションは常に、全てのイベントの伝搬が終了した後に発生します。イベントの伝搬を止めても、それ自体がデフォルト・アクションを止めることはありません。別のAPI機能を使うことで、伝搬中に他のどのようなことが発生しても、デフォルト・アクションをキャンセルすることができます。
DOM Events仕様では、スクリプトからイベント・オブザーバを付加する方法を定義しています。これを下記のリスト3に示します。
リスト3. イベント・オブザーバを付加する
var el = document.getElementById('observer_element_id');
el.addEventListener("mouseover", highlight_func, true);
el.addEventListener("mouseout", normal_func, true);
|
addEventListener
の呼出しの中で、最初のパラメータはイベント名、2番目のパラメータは実行される機能への参照、3番目のパラメータは
true
(キャプチャ・フェーズを示す)あるいは
false
(バブリング・フェーズを示す)のいずれかです。DOM
Events仕様では、幾つかの核となるイベントを定義していますが、イベントの伝搬を停止し、関連したデフォルト・アクションを停止するAPIメソッドも同様に定義しています。
こうしたAPIの実装は、ブラウザによって大きく異なることに注意してください。特にInternet
Explorerのバージョン6までは、
addEventListener
をサポートしておらず、その代わりに
attachEvent
という名前の類似の機能を使っています。
ここまでは、イベントに関してのすべてが手続き的なスクリプトに結びついていました。XML Events仕様は、DOM Level 2 Eventsを基盤として構築されており、イベント・オブザーバを接続する宣言的な方法を追加しています。W3C XForms仕様( 参考文献 参照)は、共通の課題を宣言的に達成できるXML Events準拠の要素ライブラリを定義した最初のものです。リスト4は、このXFormsを使った例です(ここで使われている名前空間宣言の詳細については、 リスト5 にある完全な例を見てください)。
リスト4. 宣言的イベント・ハンドラ
<xf:trigger>
<xf:label>Reset the form</xf:label>
<xf:reset ev:event="DOMActivate" model="mymodel" />
</xf:trigger>
|
XML Eventsは、幾つかの属性として定義されています。
リスト4
では、
ev:event
属性は注目されている特定のイベントを指定し、ハンドラは
xf:reset
要素であり、オブザーバは親要素
xf:trigger
にデフォルトで設定されます。このリストでは、スクリプトが全く必要ないことに注意してください。
XML Events仕様は、主に既存要素の上に置かれる属性の定義と、属性のホストとして機能する
listener
要素から構成されています。それぞれの属性は、DOM Level
2イベントの機能に対応付けされます。表1に、XML Eventsの属性をすべて列挙します。
表1. XML Eventsの属性
| 属性 | 機能 |
event
| リスト4 に示すように、この属性は必須であり、リスナーのトリガとなるイベントを指定します |
observer
| この属性は、オブザーバである要素の固有IDを示します |
handler
| この属性は、あるアクションまたは処理を行う要素(別の文書内にあるかも知れません)のURIを示します |
phase
|
この属性は、
capture
または
default
のいずれかであり、使用されるキャプチャ・フェーズを指定します
|
propagate
|
この属性は
stop
または
continue
(デフォルト)のいずれかで、イベントの伝搬を継続するか否かを指定します
|
defaultAction
|
この属性は、
cancel
または
perform
(デフォルト)のいずれかで、伝搬後にデフォルト・アクションを実行させるか否かを指定します
|
target
| この属性によってリスナーは、特定のターゲットに向けられたイベントに対してのみ応答するようになるので、特殊な状況でのみ使うべきものです |
id
|
この属性を使うと、
listener
要素に対して文書中で一意な識別子を与えることができます
|
リスト4
は、必要最小限の数の属性でXML Eventsを使う便利な方法を示しています。つまり、XML
Eventsの属性が直接ハンドラ要素に付加されています。
ev:handler
属性が省略された場合には常に、XML Eventsの属性を持つ要素がハンドラとみなされます。オブザーバは、
ev:observer
属性、あるいは親要素を通して指定されます。
リスト4
の場合には、オブザーバは
xf:trigger
です。
別のデフォルト動作として、
ev:observer
属性が存在せず
ev:handler
属性が存在する場合には、XML Eventsの属性がオブザーバ要素に付加されることになります。
リスト5は、 リスト1 にあった振る舞いを作り直したもので、両方のデフォルト動作の技法を示しており、またスクリプトと宣言的アクションの両方を使っています。
リスト5. XML Eventsを使う2つの方法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xf="http://www.w3.org/2002/xforms">
<head>
<title>XML Events</title>
<script type="text/javascript" ev:event="load" ev:observer="bod_id">
alert("hello");
</script>
<xf:message level="modal" id="hndl_id">goodbye</xf:message>
</head>
<body id="bod_id" ev:event="unload" ev:handler="#hndl_id">
</body>
</html>
|
ここでの
script
要素はハンドラであり、
bod_id
(
body
要素)に対するオブザーバに付加されています。
xf:message
要素自体は何もしませんが、オブザーバ要素(これも
body
)が後からそれを指し示し、イベント・ハンドラを登録します。
多くの場合、スクリプトは明らかに有用です。とは言っても、ある種の作業をスクリプトで行うのはやはり困難です。XML Eventsは、文書にスクリプトを宣言的に付加するための統一的な構文を提供しており、スクリプト・インタープリタへの依存性無しに動作する振る舞いを定義することさえも可能になります。XML Eventsは、XFormsやXHTML、SVG・・・などを含め、次第に多くのマークアップ技術で使われるようになっています。今後の記事では、XML Eventsの使い方や実装についても、さらに詳しく説明して行きたいと思っています。
-
ECMAScript standard
を読んでください。この標準は、一般にJavaScriptとして知られ、広く実装されているスクリプト言語を公式に定義しています。
-
イベントの流れ(event flow)についてさらに詳しく知るには、W3C勧告の
The DOM Level 2 Events Specification
を見てください。
-
次に、非常に良く書かれている
XML Events
仕様を見てください。これもW3C勧告です。
-
XML Eventsを少し異なった角度から理解するために、W3CサイトにあるSteven
Pembertonの「
XML Events for HTML Authors
」を読んでください。
-
どのイベントを対象にすべきかの判断への助言や、その他の関連したアクセス性の問題について知るために、
Web Content Accessibility Guidelines 2.0
のワーキング・ドラフトと、それにリンクされた文書を見てください。
-
XML Eventsに基づく宣言的アクションの実際例を見るには、
W3C XForms specification
の
XForms Actions の章を調べてみてください。同時にMicah
Dubinkoが最近developerWorksに寄稿した「
Inside the XForms Validator
」(2004年9月)も読んでみてください。
-
O'Reillyから出版されているMicah Dubinkoの著書
XForms Essentials
の全テキスト(イベントに関する章を含めて)をオンラインで読むことができます。この本は
the developerWorks Developer Bookstore
から注文することもできます。
-
XMLの主な標準の相互関係をより完全に理解したいのであれば、Uche Ogbuji
がXML標準に関する4回シリーズの素晴らしい記事をdeveloperWorksに書いていますのでご覧ください。
- 第1回:核となる標準 (2004年1月)
- 第2回:XML処理の標準 (2004年2月)
- 第3回:最も重要なボキャブラリ (2004年2月)
- 第4回:最も重要なXML標準の相互参照表 (US) (2004年3月)
-
developerWorksの
XMLゾーン
には、XML関連の資料が他にも豊富に用意されています。
-
XMLおよび関連技術においてIBM認証開発者になる方法については
こちら
を参照してください。
Micah Dubinkoは、情報過多の克服を専門としたコンサルタントであり、ソフトウェア・ベンダーおよびコンサルタント会社であるBrain Attic, L.L.C.の創立者です。またO'Reilly Media社刊のXForms Essentials の著者でもあり、XForms 1.0 を開発したワーキング・グループにも参加してきました。アリゾナ州フェニックス在住で、連絡先はmicah@brainattic.info です。