7.4. XBL and the DOM

This section introduces the DOM interfaces in XBL, illustrates how they work, and explains the core concepts involved in XBL interaction with the DOM, such as scope characteristics and insertion points.

7.4.1. The XBL DOM Interfaces

XBL has two core DOM interfaces, DocumentXBL and ElementXBL. These extensions to the Document and Element interfaces are not part of the formal DOM specifications. All methods can be accessed and used from JavaScript. Here is a list of these interface methods.

7.4.1.3. Dynamically adding a binding

The section Section 7.2 covered the attachment of a binding to a bound element using CSS. This technique is the most common method, but you can also attach bindings with the addBinding method.

Using this method as an alternative to CSS attachment is useful when you do not want to attach a binding in all circumstances. In an application based on user input, you may not want to load a binding until certain values are entered. For example, in a membership database, the information that appears on screen may depend on the user's level of membership. The following snippets show how it is used.

<mybinding id="myNewWidget" class="attached" />

To load a binding, add these two lines in your script.

var binding = document.getElementById("myNewWidget");
document.addBinding(binding, "chrome://mypackage/content/myBindings.xml#super");

Notice that the URL used to access the binding takes the same format as in the CSS property -- i.e., the path to the file and the id of the binding qualified by #.

7.4.1.4. Removing bindings

The best way to remove a binding attached via CSS is to change the style rule for that element. You can change the class to one that does not have a different or null binding reference, for example. Then a stylesheet can be set up to provide binding references for both an attached and unattached element.

This example shows how to remove a reference to a binding by resetting it to an empty reference:

mybinding.attached {
  -moz-binding : url("mybindings.xml#my-binding");
}
mybinding.unattached {
  -moz-binding : url("");
}

When you want to detach the binding from an element, you can do this:

var mywidget = document.getElementById("binding1");
mywidget.setAttribute("class","unattached");

An element can have only one binding attached at a time, so this is a programmatic trick for knocking the "real" binding out of its place with an empty one, rather than actually removing it.

The other method used to detach a binding, which is more intuitive from a DOM perspective, uses the removeBinding method:

var binding = document.getElementById("myNewWidget");
document.removeBinding(binding, "chrome://mypackage/content/myBindings.xml#super");

This method ensures that other style information is not lost if you have it attached to a particular class.

When a binding is removed, the anonymous content is destroyed and the methods, properties, and event handlers no longer apply.

In the case of an inheritance chain (see the Section 7.5 section later in this chapter for more details), the bindings are destroyed from the bottom upwards. This means that if there is tear-down code in the form of a destructor, it is executed last on the base binding.

7.4.2. Binding Parents

Although a document cannot access the content of bindings attached to it, a binding can access the document it is attached to (bound document). This gives bindings the ability to provide more than just additional content to the document. It also means that you can find information about the context of bound element in a document and provide information about it from within the binding.

From the perspective of nodes inside the anonymous content, you can use DOM properties to find a higher-level node and then in turn use that to get to other nodes:

Example 7-4 illustrates both properties in use.

Example 7-4 is a binding with two buttons, each of which brings up an alert when activated. The alert simply shows the name of an element that is accessed in the code attached to the button. In Button A, the parent node is the containing box. One level further is the bound element, <mybinding> -- the parent node of the box parent. The alert dialog raised by the alert shows "mybinding." Once a binding is applied, the binding's owner (ownerDocument) is the bound document. Assuming that Button B is a XUL window, the alert, when activated, shows "window." This property can be used to access properties of the document object.

7.4.3. Accessing Anonymous Nodes

Content bound to a document can introduce different levels of scope. Some of the scope is available at the document level, and some is at the binding level. With content in different scopes, there are limits to which standard DOM methods can be used to access other elements and objects in and above a binding. XBL contains some special methods to help work around some of the limitations caused by these barriers, such as not being able to change binding content dynamically or access certain property values.

The two XBL-specific interfaces that exist on the document to get a handle on this content are getAnonymousNodes and getAnonymousElementByAttribute. The advantage of using these interfaces is that they provide a bridge between behavior and content. Use them when you want to dynamically manipulate content or get a value -- for example, when accessing a particular textbox contained in binding, reminiscent of the one used earlier in the chapter when you were introduced to the <inputfield /> binding.

7.4.4. Extra Binding Content and Insertion Points

All examples in the chapter have so far dealt with standard binding content rendering within a bound document. The processes outlined in this section can, in one sense, be seen as abnormal because they allow ordering of the content to change based on insertion points defined in the binding. This process is done with the XBL <children> element

7.4.4.2. Selective inclusion

Sometimes multiple siblings are located within a box in the XUL, but you want to use only some of them in the binding, such as when a user logs into a system and content is displayed depending on its level of membership. In these cases, you can be selective about which children should be included in the binding. Example 7-5 shows how to use the includes attribute on the <children> element.

The children element in Example 7-5 essentially tells, "Of all the content contained in the bound element, insert only the image element at this particular insertion point." Here is the XUL code that goes with this example:

<mybinding id="myNewWidget" flex="1">
  <image src="http://www.mozdev.org/sharedimages/header.gif" />
  <label value="a non includes element" />
</mybinding>

The image is the only child taken from the XUL content and the label is ignored.

If you have children that are not defined in the includes attribute, then the binding is discarded and not used. If the bound element uses another element in addition to an image element, the binding is discarded and only the explicit content is used. If the image element isn't used at all, the binding is discarded.

<mybinding id="myNewWidget" flex="1">
  <image src="http://www.mozdev.org/sharedimages/header.gif" />
  <label value="an element" />
</mybinding>

This example renders the image and the label and discards the binding. The anonymous content does not appear because the binding is discarded and only the explicit content is used.