You now know that XUL is the basic tool set for creating your application interface, but even it has limitations. It is just a finite set of widgets that your programming needs may transcend. If you find that you reimplement many of the same groups of widgets in different applications, or if you want to extend your application's interface in some other way, you will find the eXtensible Binding Language (XBL) an invaluable tool.
This chapter describes what XBL is and how it is used. Basically, XBL provides a way to attach new content or behavior to your application by using XBL bindings. XBL can extend, add to, and reorganize user interfaces. XBL can also help you organize scattered XUL code into a set of self-contained widgets that make building and maintaining your Mozilla application much easier. Appendix C provides a reference for the XBL element set, with which new elements can be created.
XBL is an XML markup language invented specifically for creating widgets. XBL looks similar to XUL, and may even contain XUL or HTML and other markup (see the "Namespaces and XBL" section later in this chapter for more information about how other markup is used in XBL bindings), but its purpose is different. Flexibility and interoperability are the point of XBL.
If the XUL textbox
is inadequate, for example, you can
use XBL to create and attach a new widget called <datafield/>
,
possibly based on textbox
, that provides special
attributes and functionality for validating input data against a
database.
A binding is a single XBL language entity that can contain content as other markup (such as XUL) behavior that is represented as methods and properties, and event-handling capabilities. Bindings can be anything from small widget objects to large, complex blocks of code with extensive functionality. Figure 7-1 shows the different components that make up a binding: fields, properties, functions, event handlers, and content. The section "Anatomy of a Binding," later in this chapter, provides more detail about a binding's structure.
Bindings differ from XUL overlays because they are fully self-contained, reusable, and generally have no dependencies outside of the binding itself. Although XUL is used most often as content in an XBL binding, XBL can also bind to and from HTML and XML. If you have worked with Java or C#, you may recognize some parallels between XBL bindings and Java objects.
The following terms are used to describe XBL and its use in the XPFE:
XBL documents are files saved with an .xml filename extension. Most bindings implement XUL content and behavior with script, so XBL files reside in your XUL application's chrome content area and have full access to XPConnect-wrapped XPCOM objects.
Several bindings often reside inside the same XBL file. Performance benefits from this arrangement, if you have multiple related bindings, because only one XBL document needs to be loaded, rather than multiple documents. Organization is another factor. Mozilla has dozens of bindings that are interrelated by either inheritance or filename identifiers. Individual pieces to a menu widget reside in a file called menu.xml, button bindings are in button.xml, and so forth. Keeping these bindings together is wise.
The XBL document's root container is the
<bindings>
tag. Inside this element is one or more
individual child bindings, defined by the <binding>
tag. A simple XBL document is as follows:
<?xml version="1.0"?>
<bindings id="dataBindings" ...>
<binding />
<binding />
</bindings>
An XBL document is
a valid XML document. The XML preamble you are used to seeing in XUL
files is present. It also contains the single root element (in this
case, <bindings>
) and the child nodes that define
the bindings (empty).
Bindings are the atomic units of an XBL document. An XBL document may define any number of individual bindings, each of which is bound (i.e., associated with other XML/XUL elements by way of CSS class definitions) somewhere in the interface. In other words, an XBL document may be a set of unrelated or barely related bindings that are picked up by the XUL interface.
Because XBL is
a binding language for other markup, remember to distinguish between the
XBL markup (such as <binding>
and <handler>
)
and markup from another language (such as XUL). Namespaces are a
feature of the XML language that was invented to handle this separation
of intermingled markup, and XBL uses namespaces. For more information on
namespaces, refer to the W3C at http://www.w3.org/TR/REC-xml-names/.
Namespaces are declared in the root element of an XML document. The
most common implementation is the declaration of two namespaces: a
default namespace for XBL and a namespace for the other markup. This
code shows the root of a bindings document in which the XUL namespace
declaration (xmlns:xul
) and the XBL default namespace are
declared:
<bindings id="dataBindings"
xmlns=http://www.mozilla.org/xbl xmlns:xul= http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul>;
An NCName is the
part of a namespace declaration that qualifies the markup type for that
particular namespace. It is placed after a colon, and in many XPFE
documents, is the markup language name (xul
, xbl
,
or rdf
). The XBL namespace is the default in this
instance because it does not declare a namespace prefix (NCName
).
You can choose to namespace your document in a different way. For
example, if you have a large mass of XUL code in your binding and do not
wish to use the xul
: prefix repeatedly, you can declare
the XBL namespace as xmlns:xbl
; you won't need to use
prefixes on the XUL content since it is set as the default. Another
option is to namespace a parent element:
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
This code enables all children inside the <box>
to be in the scope of the XUL namespace; therefore the explicit xul
:
tag prefix declaration is not necessary.
Although XUL usually makes up the content of an XBL binding in Mozilla, HTML is another valid and popular binding format. Using the XBL with HTML combination can be advantageous. With it, web pages (rendered in Mozilla) can be more feature-rich and move beyond the limitations of the HTML specification's finite element set. It means a possible mingling of one or many markup languages, including HTML, XUL, and RDF.
The following snippet, in which a simple binding defines the name of
the browser in an HTML div
, gives you a feel for its
potential:
<binding id="browser">
<content>
<html:div>Mozilla 1.0</html:div>
<children />
</content>
</binding>
The bound element in HTML is called browser_name
and is
attached to the anonymous content in the HTML document's inline style.
<head>
<title>Browser Information</title>
<style>
browser_name {
-moz-binding: url("brand.xml#browser");
}
</style>
</head>
<body>
<h1><browser_name /> Guide</h1>
...
Although the <browser_name/>
element is not a
valid HTML element, one of XBL's great capabilities is that Mozilla
finds the binding, reads the content there, and makes the substitution.
The browser name can be included in several places in the HTML
document. Like a poor man's DTD, the binding lets you change the
definition of browser_name
in one place and propagate that
change to every instance of its use. This feature is useful because it
requires the touching of fewer files during code maintenance.
The best way to understand a binding is to watch one evolve from start to finish. This section examines a binding from its inception, into the construction of the binding, and through its use in a XUL document. At the end of the section, you should be able to take the pieces and construct your own binding, so try to follow it like a step-by-step guide.
Design is important in XBL. The implementation can sometimes be
tricky; for example, when you intend to reuse the binding elsewhere or
when others use it in a way you don't foresee. Ideally, a binding should
be as small as possible to facilitate reuse. And it's a good idea to
separate your binding into smaller pieces-perhaps smaller
"subbindings"-so you can recombine when necessary. You could design the <datafield/>
widget mentioned in the introduction-for example, as a combination of
the XUL <textfield/>
widget and your own new binding, <validator/>
,
which you could then use elsewhere.
The widget constructed in this section is a good example of a small,
reusable binding. It is a special text input widget called inputfield-
a
self-contained extension to a XUL textbox that can be used on its own
or as part of another binding. The binding combines a
<label>
and a
<textbox>
, allows child elements, and offers functions
that work with the data and style of the <textbox>
.
Attachment is the process through which the binding is connected to the bound document that uses it. This process is most commonly achieved through CSS, but can also be done by using the DOM. The section "XBL and the DOM," later in this chapter, details the interaction between XBL and the document object model. The CSS connection begins where the bound element is placed in the XUL file:
<inputfield/>
Remember that XML ignores
elements it doesn't recognize, so this new element won't be rendered
until you add information to the stylesheet; a binding is attached with
the special -moz-binding
attribute. The style selector must
be associated with the bound element in the XUL file. In the following
example, the binding is attached to every <inputfield>
tag because the element name itself is used as the style selector.
However, -moz-binding
can also be inside a class selector,
an ID selector, or any other CSS selector you wish to use:
inputfield {
-moz-binding: url("inputfield.xml#inputfield");
}
It also can be from an inline style:
<inputfield
id="ifd"
style="-moz-binding: url("inputfield.xml#inputfield")"/>
The constituent parts of this style rule are the -moz-binding
property, the url
binding locator that takes the bindings
file (and possibly the path to it) as a parameter, and the id
of the binding denoted with the #
notation. For the binding
to take, the XBL file must contain a binding with the same id
.
<binding id="inputfield">
<!-- binding content / behavior / handlers -->
</binding>
The ID of inputfield
matches the value specified in the
URL after the #
symbol. When the UI is drawn in Mozilla,
the binding content, behavior, and handlers are applied to the bound
document at the point where the
<inputfield>
element appears in the document. Figure 7-2 shows a visual representation of the
constituent parts of a binding attachment occurring via CSS.
In this example, we use our own new element name called <inputfield>
,
but you can also extend existing XUL widgets by including:
<box id="inputfield" flex="1"/>
Because they are bound through CSS, bindings cannot be guaranteed to be loaded until the whole document is loaded, which means that any inline scripts accessing bindings should be considered incorrect because you cannot guarantee that the binding is loaded.
XBL content is considered "invisible" in the context of the document object because it is not contained directly in the XUL document. Refer to the later section "XBL and the DOM" for more information on this concept.
Because a document binding can have multiple instances, something must happen to make the content unique in each one. When a binding is attached to a document, its content is automatically cloned in memory. Every instance of a binding shares the same fields, properties, methods, and event handlers because separate copies of those are simply not necessary. These elements cannot be changed dynamically, but the content document model can.
The <binding>
element
requires an id
attribute to make the binding unique within
the entire document. In the general XML specification, there can only
be one element in a document that has a certain ID string. As in XUL,
if you use an ID twice, the last one parsed is the only one seen. This
situation can lead to unexpected behavior. Figure 7-3
shows the appearance of an inputfield
binding.
An <inputfield>
has a <label>
attached
to it, shown here as "Input Field." It also has a regular <textbox>
.
The "Eric's" label is not part of the binding, but is still displayed
inside of it as a child. Child content is discussed later in the section
"Extra Binding Content and Insertion Points." The binding content is
defined as:
<content>
<children/>
<xul:label xbl:inherits="value=label"/>
<xul:textbox anonid="input" flex="1"/>
</content>
<children/>
, the first element in the binding,
lets any elements that existed in the original XUL document pass
through the binding's display if it exists inside the <inputfield>
tag.
<inputfield id="ifd" label="Input Field">
<label value="Eric's"/>
</inputfield>
In this case, the XUL label is inserted into the anonymous content
at the point of the <children/>
element when the
binding is rendered. This ability is useful for changing the ordering
of content and adding extra content within a binding.
You can limit which tags are displayed as child content by using something like:
<children includes="treehead|treechildren"/>
These filtering capabilities open the possibility of multiple <children/>
in your binding.
The next content element is <xul:label/>
. Notice
how the XML namespace of xul
is used in the content. Using
this notation is the most common way to apply namespace XUL elements in
bindings.
The label
element has an XBL-namespaced inherits
attribute. This code translates an attribute used on the original
bounded tag into something usable by a content element:
<inputfield id="ifd" label="Input Field">
The final element in the content is a typical XUL textbox
that has a namespace like the label. The anonid
attribute
on the textbox is fabricated and used here to avoid bugs and scope
issues with the id
attribute in content. The id
attribute should be used only on the <binding>
and <bindings>
tags, but anonid
works well as direct DOM access to this
element and is shown in the next section.
The next part of the
binding, and also the most complex, is the behavior. The
<implementation>
element contains the <constructor>
,<destructor>
,<field>
,<property>
,
and <method>-
all of which handle the binding's
implementation features.
All elements can contain JavaScript, which changes the binding into a dynamic widget that does more than display content on the screen. The binding implementation accepts user input, dynamically changes the UI, interacts with remote sites through web protocols, and surfaces Mozilla library functions in widgets.
In the example binding, some
variables and style rules are set up for access by the rest of the
binding to make the code cleaner. These rules are set up by using the constructor
:
<constructor><![CDATA[
this.input=document.getAnonymousElementByAttribute
(this,"anonid","input");
// Initialize color and backgroundColor to something besides a "" value
this.input.inputField.style.backgroundColor="white";
this.input.inputField.style.color="black";
this.input.inputField.setAttribute("onchange","");
]]></constructor>
The first JavaScript command accesses the <textbox>
with the anonid
label and puts it into the this.input
variable. getAnonymousElementByAttribute
is a custom DOM
method used to access anonymous content. The section "The
XBL DOM Interfaces," later in this chapter, talks more about the
XBL DOM methods.
The use of the this
keyword and the "dot notation"
comes from Java. If you have programmed in Java, these bindings can be
considered similar to Java classes. They are self-contained and can be
extended. Using this
is not necessary but it's common
practice and clarifies that the variable or property is a member of the
binding, especially if you define elements in the binding's
constructor.
In the next two commands, an object called inputField
contains the style
object property. You may be familiar
with this structure in HTML elements, and in fact, this inputField
is a version of the HTML <input>
textbox. The <textbox>
in XUL derives from that HTML element.
The color
and backgroundColor
are set here
manually to return something other than the initial value of a blank
string when they are accessed. The last line in the <constructor>
sets up the onchange
event handler for the textbox. This
event handler is also used in a property for this binding.
This section of a binding executes anything that needs to be done immediately before a binding is unloaded. Here is an example:
<destructor>
this.input=null;
</destructor>
The code you see here is not necessary in this binding, but it shows you the format for executing code in the destructor. Destructors are great to use when you have loaded a component in the binding and want to set the variable representing the component to null to avoid memory leaks. If you have a lot of bindings that load and then unload components while a Mozilla session is still running, a significant amount of memory can be eaten up unless you take this kind of step.
Like Java properties,
an XBL <property>
has getters and setters, so they can behave differently when you read or
write data. Here is a subset of properties used in this binding:
<property name="value" readonly="true">
<getter>
return this.input.value;
</getter>
</property>
<property name="uppercase" readonly="true"
onget="return this.value.toUpperCase( );"/>
<property name="backgroundColor">
<getter>
return this.input.inputField.style.backgroundColor;
</getter>
<setter>
this.input.inputField.style.backgroundColor=val;
return val;
</setter>
</property>
At this point, the characteristics of properties to watch out for
include the readonly
attribute, the getter and setter
elements, and the existence of a val
keyword that is used
internally for accessing a property's current value. For your reference,
this binding's property extracts are used for getting the value of the
input field, returning an uppercase version of the inputted text, and
getting or setting the input field's background color.
Methods in XBL are
self-contained functions represented by the <method>
tag and encapsulated within the <implementation>
element. They usually provide a binding object with a specific function
like copying and saving some data or showing and hiding widget
controls. Like properties, they can be called from within the binding,
from another binding that subclasses that binding, and directly from
the bound element.
<method name="clear">
<body>
this.input.value='';
</body>
</method>
<method name="setValue">
<parameter name="newValue"/>
<body>
this.input.value=newValue;
</body>
</method>
The method code is contained in a <body>
tag, and
each method can have 0 or more parameters, which gather the values
passed into the method when called.
Handlers in XBL mimic
regular document events like onclick
and onmousedown
,
and provide a means for trapping them within your binding and carrying
out tasks associated with them.
<handlers>
<handler event="mouseover">
this.input.focus( );
</handler>
</handlers>
Each handler is contained in a
<handler>
tag and the event name is placed in the event
attribute-minus the "on" prefix. The handler in the code shown above
places the focus in the inputfield
when the mouse goes over
it. See the section "Event Handling," later in this chapter, for more
details.
The sample binding's last piece of the puzzle is style. Any XUL elements used in the content inherit the default styles for those widgets, but if you want to add more, you can include your own stylesheet like this:
<resources>
<stylesheet src="inputfield.css"/>
</resources>
Notice the
<resources>
container element, which is a prerequisite for
this feature. The use of stylesheets in bindings is covered more
thoroughly at the end of the chapter in the section "Resources
for Bindings."
At this point, you should be familiar with the pieces that make up a binding, which, at the top level, are the content, implementation, event handlers, and extra resources. The binding that you have constructed is small, yet it shows all the main concepts involved in structuring a binding. With some personalizing, it could be included in potentially any application.
Like XUL widgets, XBL uses JavaScript to provide functionality to bindings by accessing XPCOM methods via XPConnect. Like binding content, behavior is optional in a binding. Each can exist without the other. At times, you might want only implementations, such as a base binding that contains certain properties that are inherited by other bindings.
The <implementation>
element is the container for
all other elements that make up a binding's behavioral portion. Example 7-1 shows an empty implementation shell that
highlights the element's contained hierarchy.
Example 7-1: XBL implementation element
<implementation>
<constructor />
<destructor />
<method name="">
<parameter name="" />
<body />
</method>
<property>
<getter />
<setter />
</property>
<field />
</implementation>
The code in Example 7-1 shows the <implementation>
element having a constructor, destructor, method, property, and field as
possible children. Each component can exist in quantities of zero or
more, with the exception of the constructor and destructor, of which
there can be only zero or one. The rest of this section describes each
binding implementation component in more detail.
Bindings can exist solely as content generators, acting passively when they are drawn. But you can also create bindings that provide new capabilities and more interactive functions or that execute routines in your application.
In the spirit of self-containment, functions can be added to a
binding that carry out functionality related to that binding. These
functions are the behavior of a binding. The ideal way to add behavior
is to add methods to your binding with the <method>
element. Each parameter for a method defined in the
<method>
element is contained within its own <parameter>
tag.
<method name="dumpString">
<parameter name="aString1"/>
<parameter name="aString2"/>
<body>
<![CDATA[
if (!aString1 && aString2)
return;
return dump(aString1+" "+aString2+"\n");
]]>
</body>
</method>
To use the method to print text to the command shell, call the name
that is specified in the name
attribute. All methods
created in a binding are added to the binding element object and called
as members of that object, as shown here:
<mybinding id="myNewWidget">
<image src="http://www.mozdev.org/sharedimages/header.gif" />
</mybinding>
<button label="test method"
oncommand="document.getElementById('myNewWidget')
.dumpString('hello', 'there!');"/>
Using the <![CDATA XML entity is also important. The purpose of
<![CDATA is to escape JavaScript that may otherwise cause conflicts
with the XML parser. Having characters like quotes and slashes in XML is
problematic when they are not escaped. Using <!CDATA
with large portions of JavaScript in a binding can improve performance
and minimize bugs.
Methods were designed for language neutrality with the type
attribute and getter and setter elements. Currently, bindings support
only JavaScript, which is the default when no type is specified.
However, this may change as other scripting engines are plugged into
Gecko.
Two special methods exist in XBL that
allow you
to manipulate what happens when a binding is added or removed from a
document. These methods use the native <constructor>
and <destructor>
tags. A constructor is valuable, for
example, when you need to initialize a binding object or prefill the UI
based on stored values. Destructors can clean up when a binding is
discarded.
When bindings are attached, their content is inserted into the
document at the point you specify. After insertion-or, more
specifically, after the attachment occurs-the code in the <constructor>
executes. Similarly, you can use the <destructor>
element to execute functions when a binding is destroyed.
<implementation>
<constructor>
<![CDATA[ dump("\n********\nCreate\n********\n");]]>
</constructor>
<destructor>
<![CDATA[ dump("\n********\nDestroy\n********\n");]]>
</destructor>
</implementation>
This example prints some text to output, but you can include code
that carries out variable initialization or anything else you want to
occur at these stages of the binding's life. Bound elements constructed
before the document load
event execute this script before
the document load
event fires. In the case of extended
bindings, base handlers are fired first, followed by derived handlers.
Properties on a binding are
included by using the
<property>
element. Fundamentally, properties are used to
hold and manipulate values that are used elsewhere in the binding, such
as when a text widget has its displayed value changed periodically by
the application. Currently, there are two classifications for
properties, one of which gets a raw value directly on the element.
<property name="someAttribute">
false;
</property>
This example always sets the attribute named someAttribute
to false. Though still supported, this use of the <property>
element was replaced by a new element called <field>
.
Although it is not in the XBL 1.0 specification, the <field>
element is implemented and used in the Mozilla code base. It has the
same syntax but a different name. We recommend using the <field>
element.
<field name="someAttribute">
false;
</field>
The second property usage defines functions that are carried out
when getting and setting a property's value. Take this anonymous content
with a label
child:
<xul:box align="left" flex="1">
<xul:label xbl:inherits="value=title"/>
<xul:spacer flex="1"/>
</xul:box>
This content simply shows some text on the screen. The bound element that uses this content looks like this:
<mybinding id="my-binding" title="For Those Who Love to Use XBL" />
The XUL label
used in the binding inherits the title
attribute value that is set on the bound element. Example
7-2 shows how to set this title and its retrieval. Both access the
bound element, and any changes filter down to the label.
Example 7-2: An XBL property setting and getting a value
<property name="title">
<setter>
<![CDATA[
this.setAttribute('title',val); return val;
]]>
</setter>
<getter>
<![CDATA[
return this.getAttribute('title');
]]>
</getter>
</property>
The script keyword val
is used
internally to represent the latest property value. The request to change
the property or retrieve its value can come from another property in the
same binding (this.<propertyName>
), from a binding
method, or from a method in the bound document that accesses the binding
object directly. This Java-Script sets the value of a property named title
on the binding object:
var titleElement = document.getElementById("my-binding");
titleElement.title = "The Adventures of an XBL hacker";
You can use the onget
and onset
attribute
as an alternative to <getter>
and <setter>
elements. Properties are initialized after the content is generated but
before the binding attached event is set off. This ensures that all
properties are available once that event occurs.
Although it is most commonly used just for getting and setting
values on the property, nothing stops you from putting more code in the <properties>
element that carries out other actions on the binding. One scenario
shown in Example 7-3 is if you have a property that
holds a search value, you can send that text to the Google [*] API, and fill another widget in
the UI with the results every time the value is updated. [*]
Example 7-3: Performing a Google search when setting a property
<property name="searchString">
<setter>
<![CDATA[
var s = new SOAPCall( );
var q = val;
if (!s)
return "Error creating SOAPCall object";
var soapversion = 0;
var method = "doGoogleSearch";
var object = "urn:GoogleSearch";
var headers = [ ];
var params = [
new SOAPParameter(this.googleKey, "key"),
new SOAPParameter(q, "q"),
new SOAPParameter(this.start, "start"),
new SOAPParameter(this.maxResults, "maxResults"),
new SOAPParameter(this.filter, "filter"),
new SOAPParameter(this.restrict, "restrict"),
new SOAPParameter(this.safeSearch, "safeSearch"),
new SOAPParameter(this.lr, "lr"),
new SOAPParameter("utf8", "ie"),
new SOAPParameter("utf8", "oe")
];
s.encode(soapversion, method, object, headers.length, headers,
params.length, params);
s.transportURI = "http://api.google.com/search/beta2"
var response = s.invoke( );
if (response.fault)
return { msg : "SOAP call error", fault : response.fault };
// At this point you would pass the results back to the UI
return response.message;
]]>
</setter>
</property>
The value of the search string is set to the value that has been
given to the property: var q = val
. This value is then
added to the parameter list (SOAPParameter
) for the SOAP
call, along with other parameters that are obtained from other
properties in the binding (e.g., this.maxResults
).
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.
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.
The DocumentXBL interface gains access to and interacts with an XBL document. The methods of this interface are as follows:
The ElementXBL interface adds and removes bindings from a bound element. The methods of this interface are as follows:
The section "Anatomy of a Binding" 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 #
.
Neither addBinding
nor removeBinding
are
implemented at the time of writing. They are covered because they are
part of the XBL 1.0 specification. When implemented, they offer crucial
alternatives for attaching and detaching bindings, making XBL a more
interactive technology.
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.
-moz-binding:url("")
can be used at this time as a hack
around the -moz-binding:none
binding. The later binding
does not currently work in Mozilla.
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 "Inheritance" 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.
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:
parentNode
ownerDocument
While higher-level nodes can be accessed from anonymous content,
parents do not have explicit access to their anonymous children using
the DOM childNodes
property. Using firstChild
or nextSibling
will also not work.
Example 7-4 illustrates both properties in use.
Example 7-4: Accessing a bound document from a binding
<binding id="my-binding">
<content>
<xul:vbox>
<xul:button label="A" id="button1"
oncommand="alert(this.parentNode.parentNode.nodeName)"/>
<xul:button label="B" id="button2"
oncommand="alert(this.ownerDocument.firstChild.nodeName)"/>
</xul:vbox>
</content>
</binding>
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.
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.
The method getAnonymousNodes(element)
takes
a node as a parameter and returns a list of nodes that are in the
anonymous content. The following code uses script in the <getter>
to access the anonymous node and return a value contained in it.
<getter>
<![CDATA[
var list = document.getAnonymousNodes(this)[0];
return list.selectedItem.getAttribute('label');
]]>
</getter>
If we assume that this binding's content is a XUL menu list, then
this code gets the label
attribute of the menu item that is
currently selected in that list (list.selectedItem
). The
list variable contains the value returned by the getAnonymousNodes
function, which is passed the binding node (this
). The
method returns an array, so the item is accessed via the first index of
0.
The method getAnonymousElementByAttribute(element, attr,
value)
returns a single anonymous node rather than a list. This
node is
qualified by an element name, a particular attribute, and the value of
that attribute, and returns this specific node. This retrieval provides
faster access to anonymous content elements when you know an attribute's
value.
<property name="emailID" onget="return
document.getAnonymousElementByAttribute(this, 'id',
'emailAddressNode');" readonly="true"/>
This example uses an id
attribute to retrieve a
specific node. You can use this method when multiple elements of the
same type exist and you need to get access to a particular one-for
example, a particular field on a database entry submission form like an
email address or a telephone number.
Although these two functions (getAnonymousNodes
and getAnonymous-Elements-ByAttribute
)
were probably designed to be used within a binding scope, they can be
used both inside and outside a binding to access anonymous content.
Bindings are meant to be self-contained, and getting anonymous nodes
outside a binding breaks this philosophy. However, these functions can
act as a bridge between scopes if you are careful.
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
Zero or more children can be contained
in anonymous content. These children are marked up with the XBL-specific <children>
tag. They can be either the content of the element using the binding or
anonymous content generated by the base binding. If the
<children>
tag contains its own elements, then it will be
used as the default content. If the element the binding is attached to
contains children, the default content will be ignored.
The location of the <children>
tags determines
the content's insertion point. Insertion points play an important role
in the generation of content within a template because they affect how
the content is displayed and accessed by the DOM.
<binding id="my-binding">
<content>
<xul:vbox>
<children />
</xul:vbox>
</content>
</binding>
This stripped-down binding has only a vertical box as its own
content and looks to the children of the bound element for more content
by using the <children>
element. Here is the XUL
content that uses the binding:
<mybinding id="myNewWidget" flex="1" class="attached">
<label value="this is child 1" />
<label value="this is child 2" />
</mybinding>
When the binding is attached and the content is drawn, the insertion point for the two labels is inside the container vertical box inside the binding. This scenario could be used when a binding is used multiple times. Each time, it needs to be rendered differently with extra content that can be provided this way.
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.
Example 7-5: Selective inclusion of child content in a binding
<binding id="my-binding">
<content>
<xul:vbox class="insideBox">
<xul:description value="Top" />
<xul:box>
<children includes="image" />
</xul:box>
<xul:description value="Bottom" />
</xul:vbox>
</content>
</binding>
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.
In XBL, inheritance is the process in which one object included in another object is allowed to use properties from that parent object. These properties can be many things, depending on the implementation, ranging from methods to attribute property values. Inheritance is a concept familiar in programming languages, most notably object-oriented ones. It's not something alien to markup, however, and it is deployed effectively in XBL. This section examines three forms of XBL inheritance: binding, attribute, and implementation. As you will see, inheritance promotes self-contained (modular) and flexible bindings that permit shared content across and within XBL documents.
Binding inheritance occurs when one binding is linked to another binding or XUL element and uses some or all properties of it, whether they are content or behavior. A binding can inherit from another binding that exists in the same or different file. In one way, this useful feature makes a binding like a class, with content and methods that can be used elsewhere. Bindings become modules, which prevents code duplication, makes maintenance easier, and gets slotted in and out of documents.
Linkage or inheritance is enabled by the extends
attribute on the <binding>
element. This attribute
contains the URL of the binding that you inherit from. This URL is made
up of the location and name of the file that contains the binding (the #
symbol), and the id
of the specific binding being used. In
this way, it is similar to the access method used in CSS attachment.
Although it is in the XBL 1.0 specification, Mozilla hasn't fully
implemented type="inherits"
on the children tag yet, so the
best way to work with binding inheritance is to use the extends
attribute. Example 7-6 shows a few bindings used in
the implementation of the listbox
cell in the Mozilla tree.
It illustrates how extends
is used to inherit from another
binding.
Example 7-6: Binding inheritance
<binding id="listbox-base">
<resources>
<stylesheet src="chrome://global/skin/listbox.css"/>
</resources>
</binding>
<binding id="listcell"
extends="chrome://global/content/bindings/listbox.xml#listbox-base">
<content>
<children>
<xul:label class="listcell-label"
xbl:inherits="value=label,flex=flexlabel,crop,disabled"
flex="1" crop="right"/>
</children>
</content>
</binding>
<binding id="listcell-iconic"
extends="chrome://global/content/bindings/listbox.xml#listcell">
<content>
<children>
<xul:image class="listcell-icon" xbl:inherits="src=image"/>
<xul:label class="listcell-label"
xbl:inherits="value=label,flex=flexlabel,crop,disabled"
flex="1" crop="right"/>
</children>
</content>
</binding>
In Example 7-6, listcell-iconic
inheritslistcell
.
In turn, listcell
inherits list-box-base
,
which holds resources. The listcell
binding is a cell with
text only and the listcell-iconic
binding has text and an
image. Thus, the user has a choice of using a list cell binding with an
icon or no icon. Yet both of these bindings have access to the
stylesheet resource declared in the base binding. If listcell-iconic
is used, the duplicate xul:label
is ignored in the
inherited binding and the stylesheet inherited from the base binding
via the inherited binding is used. We've used this technique to
illustrate how resources in multiple bindings are shared.
With binding extensions that use the extends
attribute,
you can also extend a XUL element as a model, using extensions as a
proxy to mimic that XUL element. The element may not be included
directly in the anonymous content, but its characteristics are still
present on the bound element. If you use the XUL namespace xul
:
in the same way you use it for XUL content in a binding, you can
inherit the XUL element properties as illustrated in Example
7-7.
Example 7-7: Inheriting XUL widget characteristics using extends
<binding id="Widget1" extends="xul:vbox">
<content>
<xul:description value="Top" />
<children includes="image" />
<xul:description value="Bottom" />
</content>
</binding>
In Example 7-7, the binding has all of the
attributes and behavior of a XUL box. Because you extend a box element,
the base widget <mybinding>
is now a vertical box.
The anonymous content is laid out according to the box model, and all
attributes that are recognized on the bound element are applied to the
box.
Also known as "attribute forwarding," attribute inheritance
is a way for anonymous content to link to the attributes from the
bound element. When the bound element attribute is changed, this
modification filters down to the binding attribute list. The code in Example 7-8 shows anonymous content where multiple
attributes are picked up by the xbl:inherits
attribute,
with each one separated by a comma.
Example 7-8: XBL attribute inheritance
<xul:box class="insideBox" xbl:inherits="orient, flex, align">
<xul:description value="Top" />
<xul:box>
<children includes="image" />
</xul:box>
<xul:description value="Bottom" />
</xul:box>
</xul:box>
The element that inherits the attributes can be anywhere in the chain of anonymous content. In this case, it is on the top-level box. It assumes the value given to these attributes in the bound element. Here is the XUL that uses the binding content from Example 7-8:
<mywidget orient="vertical" flex="1" align="center" />
The xul:box
element inherits the attribute values vertical
,1
,
and middle
, respectively, from the bound element (mywidget
).
The box in the anonymous content contains three children: two text
(description) elements and an image contained in another box. The
default orientation for a box is horizontal, but these child elements
are now positioned vertically.
You may notice that the inherits
attribute is preceded
with the xbl
: prefix, unlike other attributes in the XBL
element set. Why is this unique? It guarantees that the effect is on the
binding and not directly on the element that uses it. This ensures that
the element can have an inherits
attribute of its own if
needed. This scenerio is unlikely and you might wonder why this rule
does not apply to other attributes used on XBL elements. To achieve
correct binding, the XBL namespace must be declared on an element at a
higher level than the element using it, most commonly the <bindings>
container, as explained earlier. Here is what the code will look like:
<binding id="my-bindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtsml"
xmlns:xbl="http://www.mozilla.org/xbl">
The third type of inheritance, inheritance
of behavior, is also achieved by using the extends
attribute and is useful when you want to use methods or properties in
another binding. Example 7-9 shows how one binding
inherits implementation from another in the same file.
Example 7-9: Inheritance of behavior between bindings
<binding id="Widget1" extends="test.xml#Widget2">
<content>
<xul:box class="insideBox">
<xul:description value="Top" />
<xul:box>
<children includes="image" />
</xul:box>
<xul:description value="Bottom" />
</xul:box>
</content>
</binding>
<binding id="Widget2">
<implementation>
<constructor>
this.init( );
</constructor>
<method name="init">
<body>
<![CDATA[ dump("This is Widget2");]]>
</body>
</method>
</implementation>
</binding>
The Widget1
binding in Example 7-9
pulls in Widget2
using extends
. Widget2
has implemented a constructor that dumps some text to output. When Widget1
is bound and it does not find any implementation to initiate, it looks
to the inherited binding and, in this case, dumps "This is Widget2" to
output.
In a bindings inheritance tree, more than one implementation could have a method with the same name. In this case, the most derived binding-the one nested deepest in the inheritance chain-is the one used. It is even possible for some common DOM functions used on the bound element outside of the anonymous content to find imitators when implementing the attached bindings.
<method name="getElementById">
<parameter name="id" />
<body>
<!-- implementation here -->
</body>
</method>
If you glance through the source code for the Mozilla chrome, you may notice that many of the standard XUL widgets used were extended by using XBL. The button is a good example.
On its own, the button can display text with the value
attribute and an image with the src
attribute. Usually,
this is sufficient, and you can color the button and change the text
font with CSS. But you may want to take advantage of inherent behaviors
in other elements or inherit from other bindings. Mozilla buttons are a
mix of <box>
,<text>
, and <image>
elements, and they take on the characteristics of each.
Event handlers are attributes that listen for events. They intercept events raised by certain user actions, such as button clicks. When intercepted, control is given to the application to carry out some functionality.
Mouse and keyboard actions are included in these events. XBL uses
all events that are available on an element in XUL and calls them by
their name, minus the on
prefix. Thus, for example, the onmouseclick
event handler becomes mouseclick
in XBL. Refer to Appendix C for a full list of these events,
which also describes the difference between XUL and XBL event handling.
The <handler>
element contains a single event. Sets of individual <handler>
elements need to be included in a <handlers>
element.
The event that sets off the action is contained in the event
attribute.
<handlers>
<handler event="mousedown" action="dumpString('hello', 'there!')" />
</handlers>
This code uses the action
attribute to point to script
that is executed when the event is triggered. The alternative way to
set up the actions is to put the executable script between the handler
tags, like you can with the XUL <script>
element. If
you use this "embedded" syntax, wrap your script in a CDATA
section so it gets interpreted and executed properly:
<handlers>
<handler event="mousedown">
<![CDATA[
var list = document.getElementById('someElement');
list.setAttribute('style', 'display:block;');
]]>
</handler>
</handlers>
You cannot use both inline and external scripts in an XBL event
handler. If this instance does occur, the action
attribute
is used. Like code decisions in other contexts, which one you use
depends on whether you want to reuse the code. In our experience, using
inline scripts is best in most circumstances unless useful code
libraries can be accessed in external scripts.
Event handlers in XBL allow for fine-tuning, using built-in
attributes that control when, where, and how they are executed. Events
are not limited to bindings. They can be registered with other UI
elements that pre-empt behavior when, for example, a create
or load
event occurs. This registration occurs when using
the attachto
attribute.
<handler event="create" attachto="window" action="returnNode( )">
The handler is designed to update the list in the binding when the
application is loaded up and the window shows. Other possible values for attachto
are document
and element
.
The attachto
feature is disabled for Mozilla 1.0, but
it is included here for completeness and to highlight XBL's full
capabilities.
Another nice feature of event handlers in XBL is the existence of
extra modifiers on mouse and key events using the modifiers
and the key
or keycode
attributes. The value
is a list of one or more modifier keys separated by a comma. The most
common combination is the use of the alt
, control
,
or shift
modifiers. The key codes have special identifiers
like VK_INSERT
and VK_UP
.
<handler event="keypress" modifiers="control, alt" keycode="VK_UP" action="goUp( )">
Finally, when talking about event handlers, the phase
attribute allows you to control the point in the event's lifecycle when
the code is to be executed.
<handler event="mouseup" phase="capturing" action="goUp( )">
The possible values are bubbling
(the default), targeting
,
and capturing
.
Here is an example of a handler implementation that fills a tooltip
when the popup
element displaying it is shown:
<handler event="popupshowing">
<![CDATA[
var label = "";
var tipNode = document.tooltipNode;
if (tipNode && tipNode.hasAttribute("tooltiptext"))
this.label = tipNode.getAttribute("tooltiptext");
]]>
</handler>
This event handler first checks that the current popup
is in fact a tooltip via the document's tooltipNode
property and then extracts the text from it. This text is assigned to
the binding's label, which will propagate via inheritance to the text
display content widget used in the binding, which could be a label
or description
element.
This chapter stresses that bindings used in XUL documents are designed to be modular, self-contained widgets that have a certain appearance and carry out a specific set of functionality. This final section extends the notion of XBL as an organization of content, behavior, and event handling by describing extra resources (such as stylesheets and pictures) that are available in the XBL framework for use in your bindings. If you are creating templates, for example, you should consider using these approaches to application development.
You can include stylesheets
in an XBL document by using the XBL-specific element
<stylesheet>
. The example below shows the color-picker
stylesheet as it would be included in a <resources>
-containing
element, allowing styles contained therein to be used by the bindings
that referenced it.
<stylesheet src="chrome://xfly/skin/color-picker.css" />
The <stylesheet>
element is intended for the
styling of bound elements and anonymous content. It can be used on
anonymous content generated by the binding and in explicit children in
documents that use the bindings. Typically, you would include this
element in a binding and inherit it to style other bindings when there
are many bindings that have a similar appearance.
<binding id="popup-base">
<resources>
<stylesheet src="chrome://global/skin/popup.css" />
</resources>
</binding>
Then you can access the stylesheet in your binding by using the extends
attribute:
<binding id="popup" extends="chrome://global/content/bindings/popup.xml#popup-base">
Beyond this static usage of stylesheets, two attributes, applyauthorstyles
and styleexplicitcontent
, can affect the appearance of a
binding element if a stylesheet is applied to it. Although they are part
of the XBL 1.0 specification, these attributes were not implemented at
the time of writing. They are attributes of the
<binding>
element.
Stylesheets take effect from the inside scope and move outwards. This means that styles on a binding can be overridden easily by styles attached to elements contained in anonymous content.
The <image>
XBL element works much like a XUL
element and pulls in the image by using the src
attribute.
<binding id="images">
<resources>
<image src="plane.png"/>
<image src="boat.png"/>
<image src="bicycle.png"/>
</resources>
</binding>
If an element calls this binding, the pictures would lay out side-by-side horizontally in the bound document.
[Back] This example is modified code taken from http://www.segment7.net/mozilla/GoogleAPI/GoogleAPI.html , and is covered by a three-clause BSD license. More on SOAP and the SOAP API in Mozilla can be found at http://lxr.mozilla.org/mozilla/source/extensions/xmlextras/docs/Soap_Scripts_in_Mozilla.html .