Mozilla's getting a new look. What do you think? https://mzl.la/brandsurvey

XBL で定義した要素へのメソッドの追加

続いては、XBL で定義された要素に、カスタムメソッドを追加する方法について見ていきます。

メソッド

XBL で定義された要素には、スクリプトプロパティの追加に加えて、メソッドを追加することも可能です。 追加したメソッドは、スクリプトから呼び出すことが可能になります。 メソッドとは、「window.open()」のように、特定のオブジェクトに属した関数のことを指しています。 method 要素を使用することで、独自に定義した要素にカスタムメソッドを定義することが可能になります。 メソッドを定義するための一般的な構文を以下に示します。

<implementation>
  <method name="method-name">
    <parameter name="parameter-name1"/>
    <parameter name="parameter-name2"/>
    .
    .
    .
    <body>
      -- method script goes here --
    </body>
  </method>
</implementation>

メソッドの宣言は、フィールドやプロパティの場合と同様に、 implementation 要素の中で行います。 このとき、method 要素には、 メソッドへのパラメータを記述する parameter 要素と、 メソッドの処理を記述したスクリプトを置くための body 要素の 2 種類の子要素を置くことになります。

method 要素の name 属性の値がメソッドの名前になります。 同様に、parameter 要素の name 属性が、個々のパラメータの名前になります。 個々の parameter 要素は、メソッドへのパラメータの 1 つを宣言するために使用します。 このため、例えばメソッドにパラメータが 3 つある場合には、parameter 要素は 3 つ必要になります。 なお、メソッドにパラメータがない場合は、parameter 要素を置く必要はありません。

また、body 要素には、メソッドが呼ばれたときに実行されるスクリプトを置きます。 このスクリプトに対して、パラメータは、通常の関数の場合のパラメータと同じように、 parameter 要素で指定された名前を持ったスクリプト変数として定義されることになります。 このため、例えば、以下の JavaScript 関数は、その下に示すような XBL メソッドとしても記述することが可能です。

function getMaximum(num1,num2)
{
  if (num1<=num2) return num2;
  else return num1;
}

XBL:

<method name="getMaximum">
  <parameter name="num1"/>
  <parameter name="num2"/>
  <body>
    if (num1&lt;=num2) return num2;
    else return num1;
  </body>
</method>

この getMaximum という関数は、メソッドにパラメータとして渡された値のうち、最大のものを返します。 スクリプト中の比較のための小なり記号 ('<') が、タグの始まりとして解釈されないように、エスケープする必要がある点に注意してください。 または、個別の文字をエスケープするのではなく、XML の CDATA セクションを使って、コードブロック全体をエスケープするのでも構いません。 上記の getMaximum メソッドが定義された XBL をバインドした要素の参照をelement とした場合、 このメソッドは、「element.getMaximum(5,10)」のようなコードによって呼び出すことが可能です。

parameter タグによって、メソッドへ渡すパラメータを定義することが可能です。 Mozilla はスクリプト言語として JavaScript を使用し、JavaScript は型のない言語であるため、現時点ではパラメータの型を指定する必要はありません。 しかしながら、将来的には、それ以外の言語も XBL で使用されるようになるかもしれません。

匿名コンテントにアクセスする

メソッドの body に置かれたスクリプトなどから、content 要素の内部で定義された要素について外観などを変更したいことは、しばしばあると思います。 しかしながら、これらの要素は匿名で生成されるため、通常の DOM 関数からアクセスすることはできません。 通常のアプリケーション開発者が利用する範囲では、その要素がどのように実装されているのかまでは知る必要がないため、これらは隠されています。 このために、匿名コンテントを取得するためには専用の方法を用いる必要があります。

XBL で振る舞いを設定された要素には、内部に匿名 (anonymous) の子要素の配列をもつための特殊なプロパティがあります。 この配列の各要素には、XBL で定義された要素の直接の子要素が保持されています。 といっても、この特殊なプロパティに直接アクセスすることはできません。 その代わりに、documentgetAnonymousNodes() メソッドを呼び出す必要があります。

var value=document.getAnonymousNodes(element);

ここで、「element」には、匿名コンテントを取得したい要素への参照を設定します。 この関数は、匿名コンテントの要素を配列として返します。 そこから下の階層の要素については隠されていないため、通常の DOM 関数を利用して取得することが可能です。 ただし、XBL がバインドされた要素を、別の XBL の中の要素として置いた場合は例外で、 この場合には、getAnonymousNodes() 関数を再度使用する必要があるので注意してください。

以下の例は、1 行に並んだボタンを作ります。

<binding id="buttonrow">
  <content>
    <button label="Yes"/>
    <button label="No"/>
    <button label="Sort Of"/>
  </content>
</binding>

個々のボタンを参照するには、getAnonymousNodes() 関数が使用できます。 このとき、パラメータには、このバインディングがバインドされている要素への参照を渡します。 返される配列の最初の配列要素 (getAnonymousNodes(element)[0] で参照できる値) には最初のボタンが格納され、 2 番目の配列要素には 2 つ目のボタンが 、3 番目の配列要素には 3 つ目のボタンが格納されます。 なお、バインディングメソッドの中のコードからは、「this」を getAnonymousNodes() へのパラメータとして渡すことができます。

次の例は、見出しのついたテキストを作成するために使用できるもので、 メソッド showTitle() によって、ラベルの表示と非表示を切り替えることが可能です。 これは、匿名コンテントの配列から、見出しのための要素への参照を取得し、 その可視性 (visibility) を変更することで実現しています。

XUL:

<box id="num" class="labeledbutton" title="Number of Things:" value="52"/>

<button label="Show" oncommand="document.getElementById('num').showTitle(true)"/>
<button label="Hide" oncommand="document.getElementById('num').showTitle(false)"/>

XBL:

<binding id="labeledbutton">
  <content>
    <xul:label xbl:inherits="value=title"/>
    <xul:label xbl:inherits="value"/>
  </content>
  <implementation>
    <method name="showTitle">
      <parameter name="state"/>
      <body>
        if (state) document.getAnonymousNodes(this)[0].
          setAttribute("style","visibility: visible");
        else document.getAnonymousNodes(this)[0].
          setAttribute("style","visibility: collapse");
      </body>
    </method>
  </implementation>
</binding>

XUL の側に追加された 2 つのボタンには、ラベルの可視性を変更するために使用する oncommand ハンドラがあり、 どちらの場合も showTitle() メソッドを呼び出します。 このメソッドは、渡された state パラメータをチェックして、見出し用の要素を表示するか隠すかを判定します。 いずれの場合も、匿名コンテントの配列の最初の要素を処理の対象にします。 つまり content 要素の最初の子要素である 最初のラベルウィジェットを参照し、 この要素のスタイルを更新することで可視性の変更を行います。

匿名コンテントからアクセスする

別の方法として、匿名コンテント中から、その XBL がバインドされた要素を DOM の親要素を取得するための Node.parentNode プロパティを使用して取得するやり方もあります。 例えば、Show ボタンと Hide ボタンを XBL ファイルの側に移すには、以下のようにします。

例 1 : ソース

<binding id="labeledbutton">
  <content>
    <xul:label xbl:inherits="value=title"/>
    <xul:label xbl:inherits="value"/>
    <xul:button label="Show" oncommand="parentNode.showTitle(true);"/>
    <xul:button label="Hide" oncommand="parentNode.showTitle(false);"/>
  </content>
  <implementation>
    <method name="showTitle">
      <parameter name="state"/>
      <body>
        if (state) document.getAnonymousNodes(this)[0].setAttribute("style","visibility: visible");
        else document.getAnonymousNodes(this)[0].setAttribute("style","visibility: collapse");
      </body>
    </method>
  </implementation>
</binding>

ここでは、oncommand ハンドラは、まず、ハンドラがある要素の親要素の参照を取得します。 これは content 要素ではなく、XBL がバインドされている XUL 要素になります。 (この例では、labeledbutton クラスをもつボックスが該当します)。 続いて、前述の showTitle() メソッドが呼び出されます。

カスタムプロパティとメソッドは、XBL がバインドされた外枠の XUL 要素だけに追加され、 content タグ内で宣言された要素は、これらのプロパティやメソッドを持つことはできません。 このため、まず最初に親要素の取得から行う必要があります。

なお、XUL ファイルの側に置かれた子要素は、通常の方法で取得することが可能です。 これは、children タグを使った場合でも変わることはありません。 以下に例を示します。

XUL:

<box id="outer" class="container">
  <button label="One"/>
  <button label="Two"/>
  <button label="Three"/>
  <button label="Four"/>
</box>

XBL:

<binding id="labeledbutton">
  <content>
    <description value="A stack:"/>
    <stack>
      <children/>
    </stack>
  </content>
</binding>

Node.childNodes のような DOM 関数を使用して子要素の取得を行った場合、 id として outer が設定された XUL ボックスからは、4 つの子要素が取得でき、これらは 4 つのボタンに対応しています。 つまり、DOM から取得する場合、これらのボタンが実際にはスタックの中に描画されていることに影響を受けることはありません。 一方、スタックの子要素は 1 つだけで、children 要素そのものが対応することになります。 また、このときの外枠のボックスに対応する匿名コンテントの配列の長さは 2 で、 最初の要素としては description 要素、 2 番目の要素には stack 要素が格納されることになります。

コンストラクタとデストラクタ

XBL は、専用のタグ constructordestructor を使用して作成する 2 つの特殊なメソッドをサポートしています。 コンストラクタ (constructor) は、バインディングが要素に結び付けられるたびごとに呼び出されるため、 設定値の読み込みや、フィールドへのデフォルト値の設定といった、コンテントの初期化処理のために使用できます。 また、デストラクタ (destructor) は、バインディングが要素から取り除かれるときに呼び出されるため、 設定された情報を保存する必要がある場合などに使用できます。

バインディングが要素へ結び付けられる契機は、2 つあります。 まず、1 つ目の契機は、ウィンドウが表示されるときです。 このときには、ウィンドウ上の XBL のコンテントがバインドされた要素が持っているコンストラクタは、全て実行されます。 このため、複数のファイルがロードされても構わないように、 呼び出される順番に依存しないようにしておく必要があります。 なお、windowonload ハンドラは、全てのバインディングが結びつけられてコンストラクタの実行が終了するまでは呼び出されません。 次に、2 つ目の契機は、要素の -moz-binding スタイルプロパティを変更したときです。 この場合、まずそれまで設定されていたバインディングに対してデストラクタが実行され、その後そのバインディングが要素から取り除かれます。 次に、その要素に新しいバインディングが追加され、そのコンストラクタが実行されることになります。

コンストラクタやデストラクタ用のスクリプトは、それらのタグの中に直接置く必要があります。 また、コンストラクタとデストラクタには引数を設定することはできず、 バインディングに複数のコンストラクタやデストラクタを置くこともできません。 以下に例を示します。

<constructor>
  if (this.childNodes[0].getAttribute("open") == "true"){
    this.loadChildren();
  }
</constructor>

<destructor action="saveMyself(this);"/>

次のセクションでは、XBL で定義された要素にイベントハンドラを追加する方法を示します。

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

 このページの貢献者: fscholz, ethertank, Mgjbot, Morishoji
 最終更新者: fscholz,