はじめに
XULイベントは、以前の XULNote でかなり一般的なやり方が紹介されました。 しかし、XUL でイベントを効果的に使用するには、イベントが発生し、聞きとられ、処理される、実際の過程を意識しなければなりません。 併せて、この過程は XULイベントモデルとして参照されます。 このイベントモデルは、複数のXULファイルを合わせて単一のインターフェースに、さらに振る舞いを XULウィジェットにつなげることを可能にするものです。 というのも、ユーザーのアクションをつかまえて、インターフェースで対応するアクションに翻訳するのはイベンドだからです。
次のイメージは、様々なイベントモデルの役者たちがどのようにお互いでやりとりするのかについての、とても基本的な方法を示しています。
ユーザーはインターフェース上のボタンをクリックします。このボタンはユーザーのアクションに反応してイベントを起こします。このメッセージは、インターフェース上の一方のエレメントからもう一方へと伝います。 この場合では発生したイベントは "click"イベントですが、ボタンエレメントにより発生するイベントには様々なものがあります。たとえば、マウスがボタン上を移動した時には "hover"イベントが発生します。また、エレメントは折々クリックと解放の始めに異ったイベントを起こします。特定のエレメントで発生されるイベントはあらかじめ決められています。
clickイベントはまさしくメッセージです。受信者を探さないという点でそれは受動的です。それは発散します。または何らか方法で処理されるか、処理しないインターフェースから離れるまで、インターフェースのノード階層をシャボンのように昇っていきます。そのイベントに関心のあるエレメント ―― インターフェースのどの部分でも、言い換えるとユーザーがボタンをクリックしたという動作を知り反応する必要のあるエレメント ―― はイベントハンドラか、またはイベントを聞いたときに実行されるコードの集りでそのイベントを扱います。 イベントリスナの利用できるところは、同じように幾らかは決められています。ですが、XULは、ウィジェット階層の多くのエレメントに一般的なイベントリスナ(oncommandイベントリスナなど)を提供します。イベントハンドラはあなた、インターフェースの開発者が書くものです。
とてもよくあることですが、エレメントの起こしたイベントはそのエレメント自身によって聞きとられ、上階層へ浮上する前にそこで取り扱われます。図の例はその例です。ボタンエレメントの onclickイベントリスナは clickイベント を処理して、簡単な警告ダイアログを表示します。
<button value="Click Me" onclick="alert('Thank you')" />
event bubbling の反対は、event capturing です。イベントバブルはイベントをそのターゲットからノード階層の上方へと発散します。イベントキャプチャはイベントを他のエレメントが受信する前に、イベントターゲットが受信する前でさえも、横取りします。
このふたつの基本的なイベントフロー機構、event bubbling と event capturing の組み合わせは インターフェース上で発生したイベントは、イベントの発生したエレメントの上方のどこででもつかまえることができます。XULイベントモデルのこの柔軟性がこの文章の焦点です。
ウィジェット階層
次のような XUL ファイルを考えてください:
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <window id="events" xmlns:html="https://www.w3.org/1999/xhtml" xmlns="https://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" oncommand="alert('Window handler')"> <vbox> <vbox style="background-color: lightgrey;" oncommand="alert('Box handler')"> <menu class="menu" label="File" oncommand="alert('Menu handler')"> <menupopup> <menuitem oncommand="alert('New item alert')" label="New" /> <menuitem label="Open" /> <menuitem oncommand="alert('Close handler')" label="Close" /> </menupopup> </menu> <menu class="menu" label="Edit"> <menupopup> <menuitem oncommand="alert('Edit Source handler')" label="Edit Source" /> <menuitem label="Reload" /> <menuitem label="View Source" /> </menupopup> </menu> </vbox> <spring flex="1" /> </vbox> </window>
このファイルでは、最下層の、もしくは「葉」エレメントは menuitem です。このインターフェース階層は次のように表現できます:
これらの menuitem のどれかがイベントを発生させたときは、そのエレメントの階層上方のどのエレメントもそのイベントを扱うことができます。上の例では、menuitem が一般的な "command"イベントを起こしたとき、それはこの menuitem が選択されたことを示すものですがその時には、menupopup, menuそれ自身, その親の box, またはルートのwindowエレメント自身がそれを利用することができます。
Event Bubbling
イベントをその発生した以外のエレメントで利用できる仕組みのことを "event propagation" または event bubbling と言います。 event bubbling は、イベントの発生したエレメントの上位でそれを処理できることを意味します。 上の例のソースコードを別のファイルへカット&ペーストすると、イベントの上昇にしたがってイベントハンドラが 4つの警告ダイアログを表示するのを見るでしょう。 たとえば、Fileメニューの Newメニューアイテムをクリックすると、イベントハンドラは "New item alert", "Menu handler", "Box handler", "Window handler" という警告を表示します。 これらのイベントハンドラの位置はインターフェースの開発に有用です。(このようになります。)
あるイベントは上位で使うのが便利かもしれませんが、ハンドラの下のどのエレメントがイベントを発生したか決定することが重要な場合がよくあります。 たとえば、メニューのイベントハンドラがメニューアイテムで発生したイベントを処理する場合、メニューはイベントの発生したエレメントを特定して、それに応じた動作をするようなことができるべきです。 以下の例では、JavaScriptの関数がどのメニューアイテムが選択されたかを決定し、適切に反応しています。
<script language="javascript"> function doCMD(el) { v = el.getAttribute("value") if (v = "New") alert('New clicked') elsif (v = "Open") alert('Open clicked') else alert('Close clicked') } </script> ... <menu class="menu" value="File" oncommand="doCMD(event.target)"> <menupopup> <menuitem oncommand="alert('New item alert')" value="New" /> <menuitem value="Open" /> <menuitem oncommand="alert('Close handler')" value="Close" /> </menupopup> </menu>
メニューのイベントハンドラはどのメニューアイテムが実際にクリックされたのかを発見し、しかるべき異なったアクションを起こしています。
Event Capturing
event capturing は、event bubbling と相補的なものです。イベントターゲットの祖先(イベントの発生するエレメントの上位階層のエレメント)にイベントリスナを登録すれば、ターゲット自身やその祖先エレメントとの間にあるノードで処理される前に、event capturingでイベントを処理できます。
上の図では、メニューアイテム自身で呼び出された警告ダイアログは表示されません。というのは、ルートウィンドウ要素がイベントとらえてを処理するためです。 もう一つの例では、XULウィンドウの onloadイベントハンドラが box要素を登録して、子要素で発生する全ての clickイベントをつかまえます。
var bbox = document.getElementById("bigbox"); if (bbox) { bbox.addEventListener("click", "alert('captured')", true); } ... <box id="bigbox"> <menu value="File"> <menupopup> <menuitem value="New" onclick="alert('not captured')" /> ... <menupopup> </menu> </box>
イベントリスナを追加する
event capturing(または既存のイベントリスナが無いエレメントでの event bubbling) を活用するために、下層で起こるイベントを捕えたいエレメントに、イベントリスナを追加しなければなりません。 どの XULエレメントでも、イベント捕獲用に自身を登録するのに DOM の addEventListener メソッドを使うでしょう。XUL でのこの文法は次のとおりです:
XULelement = document.getElementById("''id of XULelement''"); XULelement.addEventListener(''event name'', ''event handler code'', ''useCapture bool''); ''event handler code''引数はインラインコードか関数名です。 ''useCapture'' はイベントリスナが event capturing を使用する か、または普通に階層を浮上するイベントに耳を傾けるものとして登 録するかどうかを指定します。
注意: DOM はノードでイベントリスナを作るために addEventListener メソッドを提供していて、そのノード以外では提供されません。 しかしXULでは、イベントを得るために、ほとんど全てのエレメントに "oncommand" イベントリスナ属性を含みます。 addEventListener は 一般化された "command" イベント以外のイベントのリスナを登録するための方法であり、正規のイベントの流れに先んじてイベントを捕まえる特定のエレメントを登録するための方法です。