在這一小節,我們將探討如何使用XBL建立內容。
XBL Content
XBL可以被用來向一個元素中添加一組元素。在XUL中只需要定義最外層的元素,而裡面的元素都可以在XBL中定義。對於開發那些由一系列元素組成 的組件(widget)來說,這是再好不過的功能,只不過每個元素只能指向一個這樣的組件。這種機制同樣可以為外部元素定義屬性,不同的是,這可以在內部 元素,也就是在XBL中完成。
下面的例子展示了如何定義一個滾動條(scrollbar),當然為了說明問題,這是一個簡化的例子。
<bindings XMLns="http://www.mozilla.org/xbl"
XMLns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="scrollbarBinding">
<content>
<xul:scrollbarbutton type="decrement"/>
<xul:slider flex="1">
<xul:thumb/>
<xul:/slider>
<xul:scrollbarbutton type="increment"/>
</content>
</binding>
</bindings>
這個文件只包含一個用binding元素定義的綁定項。id屬性用作這個綁定項的標識符,這樣就可以通過CSS的-moz-binding屬性指向任何元素。
content標記用來定義將要添加到滾動條裡的匿名內容。所有在content裡的元素最後都會被添加到綁定項指向的元素裡。這個綁定項可能會綁定到一個滾動條,也可能是其他的什麼元素,這不是固定的,只要這些元素通過CSS的-moz-binding屬性指向這個綁定項的地址就可以。
假如把上面的綁定項關聯到滾動條以後,那麼下面這一行XUL代碼就會被擴展為接下來的內容:
<scrollbar>
擴展為:
<scrollbar>
<xul:scrollbarbutton type="decrement"/>
<xul:slider flex="1"/>
<xul:thumb/>
</xul:slider>
<xul:scrollbarbutton type="increment"/>
</scrollbar>
content標記裡的所有元素都被匿名的添加到滾動條裡。雖然這些內容確實在屏幕上顯示出來,但是你卻不能使用常規的方式,通過腳本操作他們(譯者注:匿名的嘛,都找不到怎麼操作啊)。對於XUL來說,這裡只有一條單獨的元素,XUL看不到內部組成的那一系列元素的。
如果你在Mozilla窗口中查看這個滾動條,你會看到它由一個向上的箭頭按鈕、一個滑動條、一個翻頁區和一個向下的箭頭按鈕組成,這些元素是在 XBL中定義的。這些元素會被依次綁定到其他綁定項上。請注意,content裡面的元素都使用了xul的命名空間(很明顯嘛,因為元素前面有xul字樣 啊),這是因為這些元素是XUL的元素,而不是XBL的,他們在XBL中是無效的。這個命名空間也是在bindings標記上定義的。如果你不在這些XUL元素前面使用命名空間標識,Mozilla將會判定這些元素是XBL元素,但使用XBL的規則又不能解釋這些元素,導致出錯。
再演示一個例子,這些我們定義一個文件選擇框:
<binding id="fileentry">
<content>
<textbox/>
<button label="Browse..."/>
</content>
</binding>
把這個綁定項關聯到任何一個元素,這個元素就會包含一個文本框和一個浏覽按鈕,這些嵌入的內容是匿名的,而且在DOM中無法看到,也無法操作。
當把綁定項關聯到元素時,會自動建立匿名內容。如果這個元素原來包含子元素,那麼這些子元素會覆蓋綁定項嵌入的元素。舉例說明,下面這個XUL代碼片段,假設他被綁定到上面提到的那個滾動條綁定項。
<scrollbar/>
<scrollbar>
<button label="Overridden"/>
</scrollbar>
第一個滾動條,因為它本身就沒有內容,所以他將只包含由綁定項定義的內容。第二個滾動條,因為本身有內容,所以會用本身的內容替換XBL帶過來的內 容,結果就肯定不是一個滾動條了。需要注意的是,浏覽器內置的元素,就像滾動條,他們都是采用XBL定義內容的,而XBL的定義文件在toolkit包 中。(譯者注:scrollbar的XBL定義在Chrome://global/content/bindings/scrollbar.XML這個文 件中)
上面的規則只適用於通過content定義的元素。對於屬性、方法或者XBL其他部分,都會始終綁定到元素上,無論元素的內容是自身就有的還是由XBL帶來的。
你可能會想同時顯示自身定義的內容和由XBL帶來的內容。這時你就需要使用children元素。children元素用在XUL中,最後會被XBL帶來的content內容替換。對於自定義菜單組件,這是非常方便的俄功能。比如說,一個可以編輯的menulist元素,如果簡單點可能就會像下面這個樣子:
XUL:
<menu class="dropbox">
<menupopup>
<menuitem label="1000"/>
<menuitem label="2000"/>
</menupopup>
</menu>
CSS:
menu.dropbox {
-moz-binding: url('Chrome://example/skin/example.XML#dropbox');
}
XBL:
<binding id="dropbox">
<content>
<children/>
<xul:textbox flex="1"/>
<xul:button src="Chrome://global/skin/images/dropbox.jpg"/>
</content>
</binding>
這個例子建立了一個輸入框和一個按鈕。menupopup元素就會插入到children元素定義的位置。注意,對於DOM函數來說,menupopup因為定義在XUL中,所以是可見的,而且還是menu的子元素。XBL帶來的內容卻是不可見的,XUL開發者甚至無法得知這裡有其他元素。
上面的例子最後顯示的結果應該如下所示:
<menu class="dropbox">
<menupopup>
<menuitem label="1000"/>
<menuitem label="2000"/>
</menupopup>
<textbox flex="1"/>
<button src="Chrome://global/skin/images/dropbox.jpg"/>
</menu>
某些情況,你可以只想包含特定類型的內容,而不是所有的。或者說,你要在不同的位置包含不同類型的內容。這時候你就需要為children元素增加 includes屬性,這個屬性將只會包含特定類型的內容。includes屬性的值必須是一個標記名,或者一個使用豎線(“|”)分隔的標記名列表。
<children includes="button">
這行代碼將用綁定元素的子元素中所有的button按鈕來代替children標記的位置。而不是button的元素將不會被包含。你可以使用多個children元素,這樣就可以把不同類型的內容放到不同的位置。如果XUL中的某個(或者多個)元素不匹配任何children元素的規則,那麼這個元素就會替換掉綁定帶來的內容。
這裡還有一個例子。比如我們要建立一個可以顯示圖片的組件,圖片兩邊各有一個放大按鈕和縮小按鈕。這需要建立一個box來做圖片和兩個按鈕的容器。圖片元素是不能在XBL中定義的,因為每次顯示的圖片不相同嘛。
XUL:
<box class="zoombox">
<image src="images/happy.jpg"/>
<image src="images/angry.jpg"/>
</box>
XBL:
<binding id="zoombox">
<content>
<xul:box flex="1">
<xul:button label="Zoom In"/>
<xul:box flex="1" style="border: 1px solid black">
<children includes="image"/>
</xul:box>
<xul:button label="Zoom Out"/>
</xul:box>
</content>
</binding>
很明確,XUL中的兩個image元素會被置於children標記的位置。顯示結果和下面代碼的執行結果是相同的:
<binding id="zoombox">
<content>
<xul:box flex="1">
<xul:button label="Zoom In"/>
<xul:box flex="1" style="border: 1px solid black">
<image src="images/happy.jpg"/>
<image src="images/angry.jpg"/>
</xul:box>
<xul:button label="Zoom Out"/>
</xul:box>
</content>
</binding>
DOM的角度來看,子元素還是原來的那些子元素。具體講,外面的box包含兩個元素,對應於兩個image元素。而裡面的box有一個border樣式,而且只有一個子元素,就是children元素。在使用XBL的情況下操作DOM,這是一個非常重要的區別,同樣的道理也適用於CSS選擇符規則。
定義多個Children元素
你當然可以設置多個children元素,並且把不同的元素根據需要放在不同的位置。添加includes屬性,並且為他設置由豎線分隔的標記列表就可以達到目的。你可以在特定位置只顯示特定元素,比如下面這個例子,文本標簽和按鈕將會被顯示在和其他元素不同的地方。
Example 11.2.1: 源代碼
<binding id="navbox">
<content>
<xul:vbox>
<xul:label value="Labels and Buttons"/>
<children includes="label|button"/>
</xul:vbox>
<xul:vbox>
<xul:label value="Other Elements"/>
<children/>
</xul:vbox>
</content>
</binding>
第一個children元素按照includes屬性中定義的規則,只會顯示文本標簽和按鈕。而第二個children元素,因為沒有定義includes屬性,會顯示剩下的所有元素。
在下一節,我們來研究研究屬性是怎樣繼承到匿名內容中的。
譯者筆記
內容之所以匿名,因為這些內容在XUL中,對於DOM和CSS都是不可見、不可操作的。XBL定義的內容,最後只能是在XBL中來操作,這是下面幾 個小節要講的內容。曾經迷惑過,為什麼要把內容多次一舉分離出來,後來漸漸明白一些。同一個元素是可以綁定不同內容的,而需要做的只是修改class屬性 而已,比如擴展管理器選中和不選中,列表中顯示的內容是不同的,就是通過這個手段來實現的。而且單獨提煉出來就可以實現重用,這也是Mozilla界面的 基礎之一,每一個XUL元素都對應於一個XBL說明文件,這也就提供一種可能性,我們只有修改這個文件,就修改了所有的相關樣式。對於我們自定義的元素, 也是同一個道理。。
還有一點,個人感覺上文闡述的不夠明確。強調一下,就是如果為children設置了條件,必須保證所有xul的子元素都被包含了,只要有一個沒有 包含,就達不到預期顯示的效果。所以最穩妥的辦法,還是為沒有includes屬性的children元素找一個地方,這樣就萬無一失了。