基本的なバインディングの作成
バインディングxmlファイル自体は、[xml]<xbl>[/xml]親要素と、その中に含まれるオプションの<script>要素、およびそれぞれ固有のIDを持つ1つ以上の[xml]<binding>[/xml]要素からなります。XBLバインディングは、さらに4つのセクションに分かれます。
<xbl>
<script>...</script>
<binding id="twitter">
<template>...</template>
<implementation>...</implementation>
<resources>...</resources>
<handlers>...</handlers>
</binding>
</xbl>
<script>要素は、どのバインディングのインスタンス化よりも前に実行されます(実行結果はすべてのバインディングから利用できます)。このため、大抵はjQueryなどのライブラリへの参照が格納されます。この要素でサポートされるJavaScriptは、インライン配置するか、[xml]@src[/xml]属性で参照される別のファイルとして指定します。twitter.xmlの場合、スクリプトは必要ありません(専用のライブラリが必要な場合を除いて、通常は必要ありません)。
<template>要素には、既存の境界タグとその子孫の代わりに視覚的に使われる(より正確にはそれらに含まれる)HTMLマークアップコードが格納されます。要するに、<template>はコンポーネントのマークアップの「骨格」を表現します。ハードコーディングされた個別のテンプレートの実装がすべてのXBLバインディングに必要なわけではありませんが、大抵は必要です。Twitterコンポーネントの場合、構造でさまざまなコアの部分を表現していますが、Twitterデータの行はありません(これはimplementationセクションで生成されます。リスト1を参照ください)。
<template>
<table>
<tr>
<td>
<table id="controller">
<tr>
<th>User Name:</th>
<th colspan="2">Password:</th>
</tr>
<tr>
<td>
<input type="text" id="username"
style="width:150px;" value="kurt_cagle"/>
</td>
<td>
<input type="password" id="password"
style="width:150px;" value="mermaid"/>
</td>
<td>
<input type="button" id="feedbutton"
value="Retrieve"/>
</td>
</tr>
</table>
<div id="container">
<table id="internal"/>
</div>
</td>
</tr>
</table>
</template>
IDはドキュメント全体ではなく、個々のバインディング内のみが適用範囲となります。従って、<table id="interval">のようなものをdocument.getElementById('interval')を使って取得することはできません。#intervalは事実上、それを含むドキュメントの範囲内には存在しないからです。この手法にはいくつもの利点があります。とりわけ、名前空間の衝突を起こさずに、同じ内部IDを含む複数のバインディングを同じページに記述できることが挙げられます。Mozilla XBL 1.0では、これが大きな問題でした。
テンプレートが骨格を形成するとすれば、実装は筋肉と脳を形成します。<implementation>要素にはJavaScriptが格納されます。ただし、これは通常、JSONオブジェクトとして指定され、特定のターゲット要素または境界要素に関連付けられます。これは実質的に、オブジェクトのコードインターフェースです。twitterバインディングの場合は、かなり細かい処理になります(リスト2を参照)。
<implementation><![CDATA[
({
xblBindingAttached: function() {
boundElement = this;
this.shadowTree.getElementById('feedbutton').addEventListener('click', this.getFeed, false);
this.boundElement = this;
frame = this.shadowTree.getAttribute('frame');
interval = this.shadowTree.getAttribute('interval');
},
boundElement:null,
frame:"",
interval:30,
getFeed: function(){
var shadowTree = boundElement.shadowTree;
var internal = shadowTree.getElementById("internal");
var username = shadowTree.getElementById("username").value;
var password = shadowTree.getElementById("password").value;
var http = new XMLHttpRequest();
if (interval > 0) {setTimeout(boundElement.getFeed,interval * 1000)};
// Note - you're passing password via cleartext here, something stronger recommended
var url = "twitter.xq?username="+username+"&password="+password;
http.open("GET",url,true);
http.onreadystatechange = function() {
if (http.readyState == 4){
internal.innerHTML = '';
var tweets = eval(http.responseText);
for (tweetCt in tweets) {
var buf = [];
var tweet = tweets[tweetCt];
var text = tweet.text;
text = text.replace('&'," and ");
text = text.replace(/(http:\/\/\S+).*/,'<a href="$1" target="__frame__">$1</a>');
text = text.replace("__frame__",frame);
if (shadowTree.getElementById("id")==null){
buf.push("<tr class='tweet_row' id='"+ tweet.id+"'>");
buf.push("<td class='tweet_cell'><img src='"+tweet.user.profile_image_url+"' width='48' height='48'/></td>");
buf.push("<td class='tweet_cell'><b>"+ tweet.user.name+":</b> "+text+"</td>");
buf.push("</tr>");
var output = buf.join("");
internal.innerHTML = output + internal.innerHTML;
}
}
}
};
http.send(null);
}
})
]]></implementation>
この特定の例では、バインディングが当該要素に「アタッチ」されるたびにxblBindingAttachedメソッドが呼び出され、大抵は初期化に使われます。同様に、バインディング接続が(例えば何らかのCSSクラスの操作によって)解除される直前にxblBindingDetachedメソッドが呼び出され、通常は関連付けられた変更をクリーンアップするために使われます。
