ブロードキャスタとオブザーバ

by 3 contributors:

いくつもの要素に対して、同じようにイベントに反応させるようにしたり、一括して状態の変化を反映させたいことは、しばしばあると思います。 このために、ブロードキャスタを利用することができます。

コマンドと属性の自動転送

ここまでに説明したように、ボタンなどの要素は、コマンドと結びつけることが可能です。 このとき、command 要素に disabled 属性を付加すれば、そのコマンドに結びつけられた要素も自動的に無効化されます。 これを利用して、作成する必要があるコードを単純化することができます。 また、この技法は、それ以外の属性に対しても適用することが可能です。 例えば、label 属性を、command 要素に付加した場合、コマンドに結びつけられたすべてのボタンは、同じラベルを共有することになります。

var el = env.locale; 例 1 : ソース 表示

<command id="my_command" label="Open"/>

<button command="my_command"/>
<checkbox label="Open in a new window" command="my_command"/>

この例では、ボタンは label 属性を持っていませんが、結びつけられたコマンドの方が持っています。 このため、ボタンは、コマンドに設定されたラベルを共有することになります。 また、チェックボックスは、最初からラベルを持っていますが、これもコマンドのラベルによって上書きされます。 その結果、ボタンとチェックボックスは、両方とも同じラベル「Open」を持つことになります。

コマンドの label 属性を変更した場合、ボタンとチェックボックスのラベルも、それに追随して変更されます。 これは、以前のセクションで見た、disabled 属性の変更が、他の要素へ伝播する挙動と類似しています。

属性の自動転送は、いろいろな目的のために利用できます。 例えば、ブラウザの「戻る」動作を無効化したいと考えているとします。 このとき、メニューの「戻る」コマンド、ツールバーの「戻る」ボタン、キーボードショートカット (例えば Alt + ←)、ポップアップメニューの「戻る」コマンドのすべてを無効化する必要があります。 このために、スクリプトを作成することもできますが、退屈なコードを大量に記述することになります。 加えて、「戻る」動作が導入されている箇所を、すべて押さえておく必要があるという欠点もあります。 また、この方法では、ブラウザの拡張機能で、新たに「戻る」操作が追加されても、それについては無効化することができません。 しかしながら、単純に「戻る」の動作自体を無効化すれば、「戻る」動作を実行するすべての要素が、自ら無効になるようになっていれば、この機能は簡単に実現できます。 これは、コマンドが持つ属性の自動転送機能を利用することで実現することが可能です。

ブロードキャスタ

コマンドが持っている属性の自動転送と類似の機能を持つ要素として、ブロードキャスタ (broadcaster) があります。 ブロードキャスタは、コマンドと同様の方法で属性の自動転送をサポートします。 ブロードキャスタは、コマンドと同じように機能しますが、 コマンドが (複数の操作から起動される) 同一の動作をまとめるために利用されるのに対し、ブロードキャスタは (複数の要素に影響する) 状態に関する情報を保持するために利用される点が異なります。 例えば、 command は、「戻る」「切り取り」「削除」といった動作のために利用しますが、 broadcaster は、フラグの保持、具体的には「利用者がオンラインかどうか」といった情報を示すために利用します。 前者の場合は、メニュー項目やツールバーボタンなどを、戻るべきページが無いときや、切り取りや削除の対象となるテキストが無いときなどに、一括して無効化する必要があり、 後者の場合は、利用者がオフラインからオンラインにモードを切り替えたときに、種々の UI 要素の状態を更新する必要があることを想定しています。

ブロードキャスタの最も単純な記述例を以下に示します。 他の要素から参照可能にするために、id 属性は常に設定する必要があります。

<broadcasterset>
  <broadcaster id="isOffline" label="Offline"/>
</broadcasterset>

このブロードキャスタを「観察」している任意の要素は、ブロードキャスタの label 属性が変更されるのに応じて、自動的に変更されていきます。 他の表示されない要素と同様に、broadcasterset 要素が、ブロードキャスタの置き場所として提供されています。 すべてのブロードキャスタは、一箇所にまとめておくために、broadcasterset 要素内に宣言しておくべきでしょう。

要素をオブザーバとして設定する

ブロードキャスタを「観察」している要素のことを、オブザーバ (observer) と呼びます。 要素をオブザーバにするためには、observes 属性を追加します。 これは、要素を command 要素に結びつけるときに、command 属性を利用するのと類似しています。 例えば、ボタンを上記のブロードキャスタのオブザーバにする場合は、以下のようになります。

<button id="offline_button" observes="isOffline"/>

このボタンには、observes 属性が置かれており、値には観察対象となるブロードキャスタの id が設定されています。 この例では、ボタンは、前の例で定義した isOffline を id とするブロードキャスタを観察することになります。 このため、ブロードキャスタの label 属性の値が変更されると、オブザーバの label 属性の値も更新されます。

さらに、他の要素をオブザーバとして追加していくことが可能です。 1 つのブロードキャスタを観察する要素はいくつ設定してもかまいません。 もちろん、観察する要素は 1 つだけでもよいのですが、それではあまり得られるものがありません。 ブロードキャスタを使用する主な目的は、属性を複数の箇所へ転送することにあるからです。 ブロードキャスタは、複数の要素が属性を観察する必要があるときにのみ利用するべきだと思います。 以下に、いくつかのオブザーバを追加で定義した例を示します。

<broadcaster id="offline_command" label="Offline" accesskey="f"/>

<keyset>
  <key id="goonline_key" observes="offline_command" modifiers="accel" key="O"/>
</keyset>
<menuitem id="offline_menuitem" observes="offline_command"/>
<toolbarbutton id="offline_toolbarbutton" observes="offline_command"/>

この例では、labelaccesskey が、ブロードキャスタから、キー要素、メニュー項目、ツールバーボタンへと転送されます。 このとき、キー要素は、受け取った属性をどれも利用しませんが、ブロードキャスタが無効化されるとそれに応じて無効化されることになります。

ブロードキャスタは、任意の属性を観察するために利用できます。 オブザーバは、ブロードキャスタから、任意に設定された属性の値を全部、値が変更されるたびに受け取ります。 ブロードキャスタの、任意の属性のいずれかの値が変更されたときは、全てオブザーバに通知され、オブザーバは自分の属性値で該当するものを更新します。 ブロードキャスタに設定されていない属性については、オブザーバの属性は変更されません。 ただし、idpersist の 2 つの属性については、共有させることはありえないため、オブザーバ側が更新されることはありません。 また、開発者が独自に定義した属性についても、必要なら利用することが可能です。

ほとんど場面でコマンドが使えるため、ブロードキャスタはそれほど頻繁には用いられません。 属性の自動転送という点から見たとき、command 要素と broadcaster 要素に実質的な違いはありません。 この 2 つは、挙動としては同じですが、使用する場合の意味づけの部分が異なっています。 コマンドは動作に、ブロードキャスタは状態のために使用するようにしてください。 なお、意味づけを無視すれば、実際には、observes 属性の値として、ブロードキャスタ以外の要素を設定してもかまわないため、 任意の要素をブロードキャスタのように振舞わせることも可能です。

observes 要素

ブロードキャスタを観察するとき、特定の属性だけを観察するように限定する方法もあります。 そのためには、observes 要素を利用します。 この要素を、観察したい属性と対になるように置くことで、要素をオブザーバとして定義することが可能です。 observes 要素は、オブザーバとなる要素の子要素にする必要があります。 以下に例を示します。

var el = env.locale; 例 2 : ソース 表示

<broadcasterset>
  <broadcaster id="isOffline" label="Offline" accesskey="f"/>
</broadcasterset>

<button id="offline_button">
  <observes element="isOffline" attribute="label"/>
</button>

observes 要素には、2 つの属性が設定されています。 最初の element 属性には、観察するブロードキャスタの id を指定します。 2 番目の attribute 属性には、観察する属性を指定します。 この例では、指定の結果、ボタンのラベルがブロードキャスタから転送されるようになるため、 ブロードキャスタのラベルが変更されると、ボタンのラベルも更新されることになります。 このとき、observes 要素自身は変更されませんが、代わりに、この要素を中に含んでいる、ボタン (button) 要素が変更されることになります。 なお、accesskey 属性は、観察されていないので、ボタンには転送されないことに注意してください。 もし accesskey 属性も観察させたいのなら、もう 1 つ observes 要素を追加する必要があります。 または、observes 要素を利用する代わりに、 observes 属性をボタンに直接設定することで、すべての属性を観察させることもできます。

broadcast イベント

observes 要素には、付加的なイベントハンドラ onbroadcast を設定することができます。 このイベントは、ブロードキャスタの属性の変化が、観察しているオブザーバに通知されるときに発生します。 以下に例を示します。

var el = env.locale; 例 3 : ソース 表示

<broadcasterset>
  <broadcaster id="colorChanger" style="color: black"/>
</broadcasterset>

<button label="Test">
  <observes element="colorChanger" attribute="style" onbroadcast="alert('Color changed');"/>
</button>

<button label="Observer"
  oncommand="document.getElementById('colorChanger').setAttribute('style','color: red');"
/>

この例では、2 つのボタンがあります。 1 つは「Test」、もう 1 つは 「Observer」というラベルがつけられています。 Test ボタンがクリックされても何も起きませんが、 Observer ボタンがクリックされると 2 つのことが発生します。 まず、Test ボタンのテキストが赤色になり、次に、「Color changed」というメッセージのアラートが表示されます。

このとき発生した事象を順に見ていくと、 まず、Observer ボタンがクリックされて、その oncommand ハンドラが呼び出されます。 そのスクリプトでは、ブロードキャスタへの参照を取得し、そのスタイルの color を、赤(red)に変更しています。 このとき、ブロードキャスタ自体は画面に表示されないので、何の影響も与えませんが、Test ボタンにはスタイルの変化の通知を受けるオブザーバが存在します。 observes 要素の elementattribute 属性によって、スタイルの変化が検知されます。 スタイルの変更は、自動的に Test ボタンに適用されます。

次に、ブロードキャストによる転送が発生したため、イベントハンドラ onbroadcast が呼び出されます。 この結果、アラートメッセージが表示されます。 この broadcast イベントは、broadcaster 要素の属性が変化した場合についてのみ発生することに注意して下さい。 ボタンのスタイルを直接変更したとしても、broadcast イベントは発生しないため、アラートは表示されません。

最初の button (Test ボタン) のコードをいくつか複製した場合、複製したボタンに対して 1 つずつ、複製した個数分のアラートが表示されることになります。 これは、それぞれのボタンがオブザーバであり、スタイルが変更されると、全てに通知が行われるためです。

次のセクションでは、 XUL 要素による文書オブジェクトモデルの利用について見ていきます。


ドキュメントのタグと貢献者

Contributors to this page: ethertank, Morishoji, Mgjbot
最終更新者: ethertank,