このセクションでは、XBL 要素について例を用いて説明します。
スライドショー要素
それでは、XBL 要素の完全な例を作ってみることにしましょう。 作成するのは、オブジェクトをデッキで保持して 1 つずつ表示していくウィジェットです。 下辺付近にナビゲーションボタンを置いて、利用者がオブジェクトを巡回できるようにするとともに、 ボタンの間にあるテキストウィジェットで現在のページ番号を表示するようにします。 この要素の中には、ページとしてどんな要素でも置くことが可能ですが、一連の画像を順に表示させるために使用するのがよいと思います。 ここでは、この要素を スライドショー (slideshow) 要素と呼ぶことにします。
スライドショーのコンテント
まず、XBL のコンテントとして必要な要素を決めることにしましょう。 ページの切り替えを行う必要があるので、ページのコンテンツを保持するために、
要素を利用するのが最適だと思います。 また、ページのコンテンツは XBL ではなく XUL ファイルの側に置く必要がありますが、 それらはデッキの中に追加する必要があるため、deck
タグを使う必要がありそうです。 さらに、下辺付近には、前のページに戻るためのボタン、現在のページ番号を表示するテキストウィジェット、次のページに進むボタンを置くことにします。children
var el = env.locale; 例 1 : ソース
<binding id="slideshow"> <content> <xul:vbox flex="1"> <xul:deck xbl:inherits="selectedIndex" selectedIndex="0" flex="1"> <children/> </xul:deck> <xul:hbox> <xul:button xbl:inherits="label=previoustext"/> <xul:label flex="1"/> <xul:button xbl:inherits="label=nexttext"/> </xul:hbox> </xul:vbox> </content> </binding>
このバインディングによって、スライドショーのために必要な構造が作成されます。 いくつかの要素には、適切に伸縮が行われるように
属性が追加されています。 また、2 つのボタンの flex
属性には、 バインドされた要素に設定される 2 つのカスタム属性、label
previoustext
と nexttext
が継承されます。 これによって、ボタンのラベルの変更が簡単になります。 また、XBL がバインドされた要素の子要素は、
の中に配置されることになります。 さらに、deck
が selectedIndex
に継承されているため、 XUL 側で最初のページを設定することが可能です。deck
以下の XUL ファイルの表示結果を、その下の画像で示します。
<box class="slideshow" previoustext="Previous" nexttext="Next" flex="1"> <button label="Button 1"/> <checkbox label="Checkbox 2"/> <textbox/> </box>
使用するスタイルシート:
.slideshow { -moz-binding: url("slideshow.xml#slideshow"); }
最初の「Button 1」ボタンは、デッキの最初のページとして使われているものです。 また、
ウィジェットは label
value
が指定されていないため表示されていません。 この値は、後で自動計算されるようにするため、設定せずに置いてあります。
page プロパティ
次は、現在のページを保持するためのプロパティを追加します。 このカスタムプロパティの値を取得する場合、 デッキから現在表示されているページの番号を保持している
属性の値を取得する必要があります。 同様に、このプロパティを設定する場合は、デッキの selectedIndex
属性を変更する必要があり、 加えて現在のページ番号を表示しているテキストウィジェットについても更新する必要があります。selectedIndex
<property name="page" onget="return parseInt(document.getAnonymousNodes(this)[0].childNodes[0].getAttribute('selectedIndex'));" onset="this.setPage(val);"/>
page
プロパティの値を取得する場合、 まず匿名コンテント配列の最初の要素を参照します。 得られる要素は垂直ボックスなので、デッキを取得するためには、このボックスの最初の子ノードを取得する必要があります。 なお、ボックスから見れば、デッキは匿名ではないため、匿名コンテント配列は使用しません。 最後に、デッキの
属性を取得して返します。 また、selectedIndex
page
を設定するためには、後で定義する setPage()
メソッドを呼び出します。
また、Previous ボタンと Next ボタンには、ボタンが押されたときにページを変更するための、
ハンドラを追加する必要があります。 都合よく、たった今追加したカスタムプロパティの oncommand
page
を使えば、ページを変更することが可能です。
<xul:button xbl:inherits="label=previoustext" oncommand="parentNode.parentNode.parentNode.page--;"/> <xul:description flex="1"/> <xul:button xbl:inherits="label=nexttext" oncommand="parentNode.parentNode.parentNode.page++;"/>
page
プロパティは、外枠の XUL 要素だけに存在するため、取得するためには parentNode
プロパティを使う必要があります。 最初の parentNode
は、ボタンの親である水平ボックスを返し、2 つめはその親である垂直ボックスを返します。 そして、最後の parentNode
が、その親の外枠のボックスを返します。 返された page
プロパティは、インクリメント (++ 演算)、またはデクリメント (-- 演算) されます。 これにより、値を取得するために onget
スクリプトが呼び出され、 その値に 1 の加算または減算を行った後、値を設定するために onset
ハンドラが呼び出されることになります。
setPage メソッド
続いては、setPage
メソッドの定義にかかります。 このメソッドは、パラメータを 1 つ取り、そこに設定するページ番号を渡します。 また、このメソッドでは、ページが範囲外ではないことの確認と、デッキの
属性の設定、及びテキストウィジェットの selectedIndex
属性の変更を行う必要があります。label
<method name="setPage"> <parameter name="newidx"/> <body> <![CDATA[ var thedeck=document.getAnonymousNodes(this)[0].childNodes[0]; var totalpages=this.childNodes.length; if (newidx<0) return 0; if (newidx>=totalpages) return totalpages; thedeck.setAttribute("selectedIndex",newidx); document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1] .setAttribute("value",(newidx+1)+" of "+totalpages); return newidx; ]]> </body> </method>
この関数の名前は、setPage
で、newidx
というパラメータを 1 つ取ります。 メソッドの本体は「<![CDATA[
~ ]]>
」の内側に置かれています。 これは、XML ファイルで一般的に使用可能なメカニズムで、 この中に置かれたテキストは、すべてエスケープされることになります。 これによって、テキストに含まれる小なり記号 ('<')と 大なり記号 ('>') を、その都度エスケープする必要がなくなります。
次に、コードを細分化して詳細に見ていくことにしましょう。
var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
匿名コンテント配列から、垂直ボックスに該当する最初の要素を取得し、次に、デッキ要素に該当する最初の子要素を取得します。var totalpages=this.childNodes.length;
バインドされたボックスが持っている子要素の数を取得します。これは存在するページの総数を示すことになります。if (newidx<0) return 0;
ページが、最初のページより前に変更されないように、新しいインデックスが、最初のページよりも前を指定している場合は、ページの変更は行わずに 0 を返します。if (newidx>=totalpages) return totalpages;
ページが、最後のページより後に変更されないように、新しいインデックスが最後のページよりも後の場合は、ページの変更は行わずに、最後の ページのインデックスを返します。thedeck.setAttribute("selectedIndex",newidx);
デッキのselectedIndex
属性を変更します。これによって、リクエストされたページが表示されます。document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1].setAttribute("value",(newidx+1)+" of "+totalpages);
この行では、現在のページインデックスを表示するラベル要素の変更を行います。ラベル要素を取得するために、匿名コンテントから最初の要素 (垂直ボックス) を取得し、その 2 番目の子要素 (水平ボックス) の、2 番目の子要素を取得しています。そして、取得した要素のvalue
属性を、「1 of 3」のような形式のテキストに変更します。インデックスは 0 から始まるため、ページ番号として表示する場合は 1 を加えていることに注意してください。
コンストラクタ
スライドショーを最初に表示したときから、ページ番号が正しく表示されるようにするためには、 コンストラクタを作成して、ラベル要素を初期化しておく必要があります。 ページ番号を設定するためには、先述のメソッドと類似のコードを使用します。 下記の this.page
による参照は page
プロパティの onget
スクリプトを呼び出し、 メソッドのときとは逆に、
属性を初期ページを取得するために使用します。selectedIndex
<constructor> var totalpages=this.childNodes.length; document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1] .setAttribute("value",(this.page+1)+" of "+totalpages); </constructor>
追加可能な機能
これ以外にもいくつかの機能を加えることが可能です。 Previous ボタンと Next ボタンにキーボードショートカット (例えば、バックスペースと Enter キー) を設定したり、 最初のページと最後のページに行くための First ボタンと Last ボタンの追加することが考えられます。 さらに、ラベル要素を入力欄に変更して、ユーザが行きたいページを入力できるようにしたり、 ポップアップを追加して、メニューからページの選択ができるようにすることも可能でしょう。 また、CSS を使ってデッキの周囲に境界を付ければ、見栄えが若干よくなるはずです。
最終的なコード
最終的なコードは以下のようになります。
var el = env.locale; Example 2 : ソース
<binding id="slideshow"> <content> <xul:vbox flex="1"> <xul:deck xbl:inherits="selectedIndex" selectedIndex="0" flex="1"> <children/> </xul:deck> <xul:hbox> <xul:button xbl:inherits="label=previoustext" oncommand="parentNode.parentNode.parentNode.page--;"/> <xul:description flex="1"/> <xul:button xbl:inherits="label=nexttext" oncommand="parentNode.parentNode.parentNode.page++;"/> </xul:hbox> </xul:vbox> </content> <implementation> <constructor> var totalpages=this.childNodes.length; document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1] .setAttribute("value",(this.page+1)+" of "+totalpages); </constructor> <property name="page" onget="return parseInt(document.getAnonymousNodes(this)[0].childNodes[0].getAttribute('selectedIndex'));" onset="this.setPage(val);"/> <method name="setPage"> <parameter name="newidx"/> <body> <![CDATA[ var thedeck=document.getAnonymousNodes(this)[0].childNodes[0]; var totalpages=this.childNodes.length; if (newidx<0) return 0; if (newidx>=totalpages) return totalpages; thedeck.setAttribute("selectedIndex",newidx); document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1] .setAttribute("value",(newidx+1)+" of "+totalpages); return newidx; ]]> </body> </method> </implementation> </binding>
次のセクションでは、ウィンドウの付加的な機能についていくつか見ていきます。