Modifying a XUL Interface
The DOM provides various functions to modify the document.
Creating New Elements
You can create new elements using the createElement function of the document. It takes one argument, the tag name of the element to create. You can then set attributes of the element using the setAttribute function and append it to the XUL document using the appendChild function. For example, the following will add a button to a XUL window:
Example 7.2.1: Source View<script> function addButton() { var aBox = document.getElementById("aBox"); var button = document.createElement("button"); button.setAttribute("label","A Button"); aBox.appendChild(button); } </script> <box id="aBox" width="200"> <button label="Add" oncommand="addButton();"/> </box>
This script first gets a reference to the box, which is the container to add a new button to. The createElement function creates a new button. We assign a label 'A Button' to the button using the setAttribute function. The appendChild function of the box is called to add the button to it.
The createElement function will create the default type of element for the document. For XUL documents, this generally means that a XUL element will be created. For an HTML document, an HTML element will be created, so it will have the features and functions of an HTML element instead. The createElementNS function may be used to create elements in a different namespace.
The appendChild function is used to add an element as a child of another element. Three related functions are the insertBefore, replaceChild and removeChild functions. The syntax of these functions is as follows:
parent.appendChild(child); parent.insertBefore(child, referenceChild); parent.replaceChild(newChild, oldChild); parent.removeChild(child);
It should be fairly straightforward from the function names what these functions do. The insertBefore function inserts a new child node before an existing one. This is used to insert into the middle of a set of children instead of at the end like appendChild does. The replaceChild function removes an existing child and adds a new one in its place at the same position. Finally the removeChild function removes a node. Note that for all these functions, the reference child or child to remove must already exist or an error occurs.
It is often the case that you want to remove an existing element and add it somewhere else. If so, you can just add the element without removing it first. Since a node may only be in one place at a time, the insertion call will always remove the node from its existing location first. This is a convenient way to move nodes around in the document.
To copy nodes however, you may call the cloneNode function. This function makes a copy of an existing node so that you can add it somewhere else. The original node will stay where it is. It takes one boolean argument which indicates whether to copy all of the node's children or not. If false, only the node is copied, such that the copy won't have any children. If true, all of the children are copied as well. This is done recursively, so for large tree structures make sure that this is desired before passing true to the cloneNode function. Here is an example:
Example 7.2.2: Source View<hbox height="400"> <button label="Copy" oncommand="this.parentNode.appendChild(this.nextSibling.cloneNode(true));"/> <vbox> <button label="First"/> <button label="Second"/> </vbox> </hbox>
When the Copy button is pressed, we retrieve the next sibling of the button, which will be the vbox element. A copy of this element is made using the cloneNode function and the copy is appended.
Note that some elements, such as listbox and menulist provide some additional specialized modification functions which you should use instead when you can. These are described in the next section.
Manipulating Basic Elements
The main XUL elements such as buttons, checkboxes and radio buttons may be manipulated using a number of script properties. The properties available are listed in the element reference as those available are different for each element. Common properties that you will manipulate include the label, value, checked and disabled properties. They set or clear the corresponding attribute as necessary. Here is a simple example which changes the label on a button:
Example 7.2.3: Source View<button label="Hello" oncommand="this.label = 'Goodbye';"/>
When the button is pressed, the label is changed. This technique will work for a variety of different elements that have labels. For a textbox, you can do something similar for the value property.
Example 7.2.4: Source View<button label="Add" oncommand="this.nextSibling.value += '1';"/> <textbox/>
This example adds a '1' to the textbox each time the button is pressed. The nextSibling property navigates from the button (this) to the next element, the textbox. The += operator is used to add to the current value so a 1 will be added onto the end of the existing text. Note that you can still enter text into the textbox. You can also retrieve the current label or value using these properties, as in the following example:
Example 7.2.5: Source View<button label="Hello" oncommand="alert(this.label);"/>
Checkboxes have a checked property which may be used to check or uncheck the checkbox. It should be easy to determine how this is used. In this next example, we reverse the state of the checked property whenever the button is pressed. Note that while the label and value properties are strings, the checked property is a boolean property which will be set either true or false.
Example 7.2.6: Source View<button label="Change" oncommand="this.nextSibling.checked = !this.nextSibling.checked;"/> <checkbox label="Check for messages"/>
Radio buttons may be selected as well using properties, however since only one in a group may be selected at a time, the others must all be unchecked when one is checked. You don't have to do this manually of course. The radiogroup's selectedIndex property may be used to do this. The selectedIndex property may be used to retrieve the index of the selected radio button in the group and well as change it.
It is common to disable particular fields that don't apply in a given situation. For example, in a preferences dialog, one might have the choice of several possibilities, but one choice allows additional customization. Here is an example of how to create this type of interface.
Example 7.2.7: Source View<script> function updateState() { var name = document.getElementById("name"); var sindex = document.getElementById("group").selectedIndex; if (sindex == 0) name.disabled = true; else name.disabled = false; } </script> <radiogroup id="group" onselect="updateState();"> <radio label="Random name" selected="true"/> <hbox> <radio label="Specify a name:"/> <textbox id="name" value="Jim" disabled="true"/> </hbox> </radiogroup>
In this example a function updateState is called whenever a select event is fired on the radio group. This will happen whenever a radio button is selected. This function will retrieve the currently selected radio element using the selectedIndex property. Note that even though one of the radio buttons is inside an hbox, it is still part of the radio group. If the first radio button is selected (index of 0), the textbox is enabled by setting its disabled property to true. If the second radio button is selected, the textbox is enabled.
(Next) The next section will provide more details about manipulating radio groups as well as manipulating lists.