XUL Tutorial - 10.2 - Anonymous Content
Previous Contents Reference Next

XUL Tutorial - Anonymous Content

In this section we'll look at creating content with XBL.

XBL Content

XBL can be used to automatically add a set of elements inside another element. The XUL file only needs to specify the outer element while the inner elements are described in the XBL. This is useful for creating a single widget that is made up of a set of other widgets, but can be refered to as only a single widget. Mechanisms are provided for adding attributes to the inner elements that were specified on the outer element.

The example below shows how a scrollbar might be declared (It has been simplified a bit from the real thing):

<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:scrollbarbutton type="increment"/>
    </content>
  </binding>
</bindings>

This file contains a single binding, declared with the binding element. The id attribute should be set to the identifier of the binding. This way it can be refered to through the CSS -moz-binding property.

The content tag is used to declare anonymous content that will be added to the scroll bar. All of the elements inside the content tag will be added inside the element that the binding is bound to. Presumably this binding would be bound to a scroll bar, altough it doesn't have to be. Any element that has its CSS -moz-binding property set to the URI of the binding will use it.

The result of using the above binding is that the line of XUL below will be expanded as follows:

<scrollbar>

expands to:

<scrollbar>
  <xul:scrollbarbutton type="decrement"/>
  <xul:slider flex="1"/>
  <xul:scrollbarbutton type="increment"/>
</scrollbar>

The content with-in the content tag is added to the scroll bar anonymously. Although anonymous content is displayed on screen, you cannot get to it through a script in the normal way. To the XUL, it's as if there was only one single element, even though it is really made up of a number of elements.

If you look at a scroll bar in a Mozilla window, you will see that it is made up of an arrow button, a slider and a second arrow button, which is the elements that appear in the XBL above. Notice that the content elements need the XUL namespace (they appear preceded with xul:), because they are XUL elements and aren't valid in XBL. This namespace was declared on the bindings tag. If you don't use the namespace on XUL elements, Mozilla will assume that the elements are XBL, not understand them and your elements won't work correctly.

Another example, this time for a field for entering a filename:

<binding id="fileentry">
  <textfield/>
  <button value="Browse..."/>
</binding>

Attaching this binding to an element will cause it to contain a field for entering text, followed by a Browse button. This inner content is created anonymously and cannot be seen using the DOM.

The anonymous content is created automatically whenever attach a binding to an element. However, what would happen if you put elements inside the scrollbar element directly in the XUL file? Should the inner content of the XBL be used or the XUL content? The answer is that this content will override what the XBL file specifies. For example, take this XUL fragment:

<scrollbar align="vertical"/>

<scrollbar align="vertical">
  <button value="Overridden"/>
</scrollbar>

The first scroll bar, because it has no content of its own, will have its content generated from a binding definition decalred in an XBL file. The second scroll bar has its own content so it will use that instead of the XBL content, resulting in something that isn't much of a scroll bar at all. Note that the built-in elements such as scroll bars, get their XBL from the file chrome/xulBindings.xml.

This only applies to the elements defined with-in the content tag. Properties, methods and other aspects of XBL are still available whether the content is from XBL or whether the XUL provides its own content.

There may be times when you want both the XBL content and the content provided by the XUL file to be displayed. You can do this by using the includes attribute to allow certain elements to appear in the content without preventing the XBL from being used. Its value should be set to a single tag name, or to a list of tags separated by bars ( The | symbol ).

The listed tag names are excluded from the check to see whether the tag is empty or not. If you don't use the includes attribute, all tags inside the XUL element cause the XBL content to be ignored. However, you can have the XBL content added and the bound element's content added if the tags in the bound element's content are listed in the includes attribute.

This is handy when creating custom menu widgets. For example, a simplified version of the menulist element, might be created as follows:

XUL:

<menu class="dropbox">
  <menupopup>
    <menuitem value="1000"/>
    <menuitem value="2000"/>
  </menupopup>
</menu>

CSS:

box.dropbox {
    -moz-binding: url('chrome://example/skin/example.xbl#dropbox');
}

XBL:

<binding id="dropbox">
  <content includes="menupopup">
    <xul:textfield flex="1"/>
    <xul:button src="chrome://global/skin/dropbox.gif"/>
  </content>
</binding>

This example creates an input field with a button beside it. The includes attribute has been used so that the menupopup tag is allowed in the XUL while the anonymous content is still created. The result is an editable field with a drop-down box. You will commonly exclude the menupopup tag because that way a menu can still be added by a XUL developer.

The resulting content would be like such:

<menu class="dropbox">
  <menupopup>
    <menuitem value="1000"/>
    <menuitem value="2000"/>
  </menupopup>
  <textfield flex="1"/>
  <button src="chrome://global/skin/dropbox.gif"/>
</menu>

Two more points about the includes attribute. First, the template and observes tags don't have to be specified as they are automatically included. Second, you can allow all tags by setting the value of the includes to a single asterisk (*). For example:

<content includes="*">

Adjusting the Child Insertion Point

Both the generated content and the explicitly specified content are added at the same level. Let's say that we wanted to create a widget that displayed an image with a zoom in and zoom out on each side of it. This would be created with a box to hold the image and two buttons. The image element has to placed outside the XBL as it will differ with each use.

XUL:

<box class="zoombox">
  <image src="happy.gif"/>
  <image src="angry.gif"/>
</box>

XBL:

<binding id="zoombox">
  <content includes="image">
    <xul:box flex="1" align="horizontal"/gt;
      <xul:button value="Zoom In"/>
      <xul:box flex="1" style="border: 1px solid black"/gt;
      <xul:button value="Zoom Out"/>
    </xul:box>
  </content>
</binding>

The problem is that the image elements will be added at the top level of the XBL content. We really want it to be placed inside the inner box (the one with the border style). To do this, we need to use a children tag. The XBL should be changed as follows:

Example 10.2.1
<binding id="zoombox">
  <content>
    <xul:box flex="1" orient="horizontal">
      <xul:button value="Zoom In"/>
      <xul:box flex="1" style="border: 1px solid black">
        <children/>
      </xul:box>
      <xul:button value="Zoom Out"/>
    </xul:box>
  </content>
</binding>

Now, the explicit children in the XUL file will be placed at the location of the children tag, resulting in a display that is equivalent to the following:

<binding id="zoombox">
  <content>
    <xul:box flex="1" align="horizontal">
      <xul:button value="Zoom In"/>
      <xul:box flex="1" style="border: 1px solid black">
        <image src="happy.gif"/>
        <image src="angry.gif"/>
      </xul:box>
      <xul:button value="Zoom Out"/>
    </xul:box>
  </content>
</binding>

Note, however, that from the DOM's perspective, the child elements are still in their original location. That is, the outer XUL box has two children, which are the two images. The inner box with the border has one child, the children tag. This is an important distinction when using the DOM with XBL. This also applies to CSS selector rules. This will be described in more detail in a later section.

The includes attribute is not necessary when using the children element, as the presence of this element means that you expect to have child elements.

Note that content is not added at the exact position of the children element. It is only used to specify which parent element it has. For example, the following creates a widget that has four navigation buttons around it.

Example 10.2.2
<binding id="navbox">
  <content>
    <xul:box orient="vertical">
      <xul:button value="Up"/>
      <xul:box orient="horizontal">
        <xul:button value="Left"/>
        <children/>
        <xul:button value="Right"/>
      </xul:box>
      <xul:button value="Down"/>
    </xul:box>
  </content>
</binding>

However, any children specified are actually added after the 'Right' button. The children tag only says "The parent of this location is where the children should be added." To fix this, put the children tag inside another box.

Multiple Children Elements

You can also use multiple children elements and have certain elements be placed in one location and other elements placed in another. By adding a includes attribute and setting it to a bar-separated list of tags, you can make only elements in that list be placed at that location. For example, the following XBL will cause text labels and buttons to appear in a different location that other elements:

Example 10.2.3
<binding id="navbox">
  <content>
    <xul:box orient="vertical">
      <xul:text value="Labels and Buttons"/>
      <children includes="text|button"/>
    </xul:box>
    <xul:box orient="vertical">
      <xul:text value="Other Elements"/>
      <children/>
    </xul:box>
  </content>
</binding>

The first children element only grabs the text and button elements, as indicated by its includes attribute. The second children element, since it has no includes attribute, grabs all of the remaining elements.


(Next) In the next section, we look at how attributes can be inherited from into the anonymous content.

Examples: 10.2.1 10.2.2 10.2.3

XUL Tutorial - 10.2 - Anonymous Content
Previous Contents Reference Next