Next, we'll find out how to add custom properties to XBL-defined elements.
The XBL Interface
JavaScript and the DOM provide access to get and set the properties of elements. With XBL, you can define your own properties for the elements you create. You can also add methods that can be called. That way, all you need is to get a reference to the element (using document.getElementById
or a similar function) and then get or set the additional properties and call the methods on it.
There are three types of items you can add.
- Fields are used to hold a simple value.
- Properties can also be used to hold a value but may have code execute when an attempt is made to retrieve or modify the value.
- Methods are functions which may be executed.
All three are defined within an
element, which should be a child of the implementation
element. Within the implementation, you define individual binding
, field
, and property
elements, one for each one that you want. The general syntax is as follows:method
<binding id="element-name"> <content> -- content goes here -- </content> <implementation> <field name="field-name-1"/> <field name="field-name-2"/> <field name="field-name-3"/> <property name="property-name-1"/> <property name="property-name-2"/> <property name="property-name-3"/> . . . <method name="method-name-1"> -- method content goes here -- </method> . . . </implementation> </binding>
Fields
Each field is defined using the
element. Often, fields would correspond to an attribute placed on the element such as field
label
or disabled
, but they do not have to.
The name
attribute on the
element is used to indicate the name of the field. You can use the name from a script to get and set the value. The example below creates a button which generates and stores a random number. You can retrieve this same number multiple times by getting the field
number
property from the button. Most of the work here is done in the oncommand
handlers. Later, we'll find out how to move this to XBL.
XUL: <box id="random-box" class="randomizer"/> <button label="Generate" oncommand="document.getElementById('random-box').number=Math.random();"/> <button label="Show" oncommand="alert(document.getElementById('random-box').number)"/> XBL: <binding id="randomizer"> <implementation> <field name="number"/> </implementation> </binding>
A number
field has been defined in the binding, which stores the random number. The two extra buttons set and get the value of this field. The syntax is very similar to getting and setting the properties of HTML elements. In this example, no content has been placed inside either the XUL box or its definition in XBL, which is perfectly valid.
This example isn't quite correct because the field is not assigned a default value. To do this, add the default value as the content of the
tag. For example:field
<field name="number"> 25 </field>
This will assign the value 25 as the default value of the number field. Actually, you can instead place a script inside the
tag that evaluates to the default value. That might be necessary if the value needs to be computed. For example, the following field is given a default value equal to the current time:field
<field name="currentTime"> new Date().getTime(); </field>
Properties
Sometimes you will want to validate the data that is assigned to a property. Or, you may want the value to be calculated dynamically as it's asked for. For example, if you want a property that holds the current time, you would want to have its value generated as needed. In these cases, you need to use a
tag instead of a property
tag. Its syntax is similar but has additional features.field
Onget Attribute and Onset Attribute
You can use the onget
and onset
attributes to have code execute when the property is retrieved or modified. Add each to the
element and set its value to a script which either gets or sets the value of the property.property
For example, you could assign a script to the value of onget
to calculate the current time. Whenever a script attempts to access the value of the property, the onget
script will be called to retrieve the value. The script should return the value that should be treated as the value of that property.
The onset
handler is similar but is called whenever a script attempts to assign a new value to the property. This script should store the value somewhere, or validate the value. For example, some properties might only be able to store numbers. Attempting to assign alphabetic text to such a property should fail.
<property name="size" onget="return 77;" onset="alert('Changed to:'+val);"/>
This property will always return 77 when retrieved. When set, an alert will be displayed which displays the value to assign to the property. The special variable val
holds the value that the property should be assigned to. Use this to validate it or store it.
The following decribes what happens in a typical case:
There are two elements, one called 'banana' and the other 'orange'. They each have a custom property called 'size'. When the following line of script is executed:
banana.size = orange.size;
- The
onget
script is called for the size property of the orange. The script calculates the value and returns it. - The
onset
handler of the size property of the banana is called. This script uses the value passed in theval
variable and assigns it to the size property of the banana in some manner.
Note that unlike a field, a property does not hold a value. Attempting to set a property that does not have an onset
handler will generate an error. You will often use a separate field to hold the actual value of the property. It is also common to have the properties match an attribute on the XBL-defined element. The following example maps a property to an attribute on an element.
<property name="size" onget="return this.getAttribute('size');" onset="this.setAttribute('size', val);" />
Whenever a script attempts to get the value of the property, it is grabbed instead from the attribute on the element with the same name. Whenever a script attempts to set the value of a property, it is set as an attribute on the element. This is convenient because then you can modify the property or the attribute and both will have the same value.
Getter Element and Setter Element
You can use an alternate syntax for the onget
and onset
attributes that is useful if the scripts are longer. You can replace the onget
attribute with a child element called
. Similarly, you can replace the getter
onset
attribute with a
element. The example below shows this:setter
<property name="number"> <getter> return this.getAttribute('number'); </getter> <setter> var v = parseInt(val,10); if (!isNaN(v)) { this.setAttribute('number', '' + v); } </setter> </property>
The property in this example will only be able to hold integer values. If other characters are entered, they are stripped off. If there are no digits, the value is not changed. This is done in the code inside the
element. The real value of the property is stored in the setter
number
attribute.
You can use either syntax for creating get and set handlers.
Readonly Attribute
You can make a field or property read-only by adding a readonly
attribute to the
tag or field
tag and setting it to property
true
. Attempting to set the value of a read-only property will fail.
Note: The readonly
attribute did not work correctly on fields until Gecko 2.0.
The next section shows how to add methods to XBL-defined elements.