這節將介紹一個XBL的完整例子。
幻燈片元素(A Slideshow Element)
讓我們構建一個完整的使用XBL元素的例子,這是一個保存了一組對象的組件,而每頁只會顯示其中一個。置於下方的導航按鈕用於循環顯示這些對象,而在一個文本框中會顯示當前的頁號。你可以向頁面添加任何元素,當然最好是一組圖片,我們給這個組件起名叫做“幻燈片”元素。
首先,我們要決定哪些元素需要通過XBL的匿名內容顯示。因為要實現翻頁效果,所以使用deck元素來作為容器可能再合適不過了。循環顯示的內容要 在XUL文件中定義,而不是在XBL中,但我們要把這些內容插入到XBL的deck中,這個操作會利用到children元素。在下方,我們需要添加兩個 按鈕用來分別顯示上一頁和下一頁,還有一個文本組件(譯者注:其實是一個label)用來顯示頁號。
實例11.8.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>
這個綁定項建立了我們需要的幻燈片結構。很多元素中都被添加了flex特性,這樣他們就能自動進行布局。兩個按鈕上的label特性是從綁定元素上繼承過來的,分別對應於各自需要的特性,previoustext和nexttext,這樣就可以方便的修改按鈕上顯示的文字。XBL綁定項所關聯到的元素的子元素將被顯示在deck元素下面。selectedIndex是從元素上繼承下來的特性,這樣我們就能在XUL中設置初始顯示的頁面。
下面的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”對應於deck中的第一頁,由於沒有給label組件設置值,所以label組件沒有顯示出來,我們當然可以為其設置一個值,但是在後面我們將會對其更多的操作。
接下來,我們將添加一個用於保存頁號的屬性,而這個屬性從deck的selectedIndex特性中獲取值,這個值對應的就是當前的頁號。同樣 的,我們也可以對這個屬性進行修改,它將同步修改deck上的selectedIndex特性。另外,文本組件需要在頁號發生改變的時候實時進行更新。
<property name="page"
onget="return parseInt(document.getAnonymousNodes(this)[0].childNodes[0].getAttribute('selectedIndex'));"
onset="return this.setPage(val);"/>
page屬性的值來自於匿名內容數組的第一個元素。因為第一個元素返回的是vbox元素,所以為了獲取deck元素,我們需要取vbox的第一個子 元素。這時候沒有使用獲取匿名內容的方法,是因為對於vbox來說deck在DOM中已經是可見的啦。總之最後我們獲取的哦了selectedIndex 特性的值。為了修改當前的也號,我們需要增加一個setPage方法,這個方法將在下面定義。
在向前和向後按鈕上需要增加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屬性是在上級元素中定義的,所以我們需要使用parentNode屬性來獲取。第一個parentNode返回的是button的父元 素,也就是hbox,第二個parentNode指向了vbox,最後第三個parentNode指向了最外面的box元素。page屬性被遞增或者遞 減,這個過程會調用onget內的腳本來獲取值,對這個值進行加一或者減一的操作以後,在調用onset內的腳本把值賦回去。
下面我們來定義setPage方法,這個方法只有一個參數,就是要設置的頁號。我們需要需要確定這個頁號沒有超過實際的范圍,然後就可以把這個頁號賦給deck的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的參數。方法的內容被置於CDATA標記中。這是在XML文件中嵌入代碼又不用轉換的一個小技巧,這樣你就不必把所有的小於號和大於號都替換成特殊編碼了。
讓我們一步步解釋這段代碼:
var thedeck=document.getAnonymousNodes(this)[0].childNodes[0];
獲取匿名內容數組的第一個元素(也就是vbox)的第一個子元素,對應於deck元素。
var totalpages=this.childNodes.length;
獲取關聯到的box下面的子元素數量,這個數值也對應於總頁數。
if (newidx<0) return 0;
如果新頁號小於第一頁的頁號,當然不會進行修改並返回0。第一頁前面是不會有任何頁面的。
if (newidx>=totalpages) return totalpages;
如果新頁號大於最後一頁,也不會進行任何修改,這時候直接返回最後一頁的頁號。合理的頁號應該小於等於最後一頁。
thedeck.setAttribute("selectedIndex",newidx);
修改deck元素上的selectedIndex特性,這將導致切換到新頁面。
document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1].setAttribute(
這行代碼將label元素的內容修改為當前頁號。可以通過獲取匿名內容數組的第一個元素(vbox)的第二個子元素(hbox)的第二個子元素的方式,來 獲取label元素。 value特性可以修改為1-3之間的數值。注意這其中有一個對索引加一的操作,因為索引是從0開始計數的。
"value",(newidx+1)+" of "+totalpages);
我們還需要建立一個構造函數,對label元素進行初始化,這樣在幻燈片剛加載的時候就可以正常的顯示。我們直接使用和上一個方法類似的代碼來設置頁號。“this.page”會直接調用page屬性onget內的腳本,反過來會用來初始化首頁的頁號。
<constructor>
var totalpages=this.childNodes.length;
document.getAnonymousNodes(this)[0].childNodes[1].childNodes[1]
.setAttribute("value",(this.page+1)+" of "+totalpages);
</constructor>
我們還可以增加一些擴展功能。比如為向前和向後按鈕增加快捷鍵(回退鍵和回車鍵可能是個好主意),還可以增加兩個按鈕,分別可以導航到第一頁和最後 一頁。label元素可以換成文本框,這樣可以直接輸入要跳轉的頁號,或者換成下拉列表,這樣我們可以直接選擇要跳轉的頁面。我們還可以在deck周圍加 上邊框,並用CSS做些修飾,相信這樣做會更加的漂亮。
下面是這一節介紹的例子的完整代碼:
實例11.8.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="return 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>