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 1 : Source View

<script>
function addButton(){
  var aBox = document.getElementById("aBox");
  var button = document.createElement("button");
  button.setAttribute("label","A new Button");
  aBox.appendChild(button);
}
</script>

<box id="aBox" width="200">
  <button label="Add" oncommand="addButton();"/>
</box>
  • This example has two parts
    • a box container element in XUL. Notice that this is NOT the same as a vbox or an hbox. (This is discussed more in the Box Model pages.)
    • a Javascript function named "addButton()"
      • This script first gets a reference to the box with getElementById(), which is the container to add a new button to. The function getElementbyID() does not know that the box it is looking for happens to be containing the tag that has the oncommand attribute that referenced it. getElementById() only knows the box it is looking to find has an id with the value "aBox". This is a subtle dependency between the function and the XUL element to which you should pay attention.
      • addButton() the calls the createElement() function to create a new button. Note this button is not visible, nor is it attached to anything yet.
      • addButton() then assigns the label 'A new Button' to the button using the setAttribute() function.
      • Finally the appendChild() function of the particular box found by getElementbyID() is called to add the button to it. At this point, the button is attached to a visible box, so it becomes visible as well.
    • The button with the label "Add" can be pressed multiple times and it will continue to add new buttons, each of which will have the label "A new Button", and will only be distinguishable by their place as children in the box element with the id "abox".

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 list of children of the parent element 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 in the list of its parent element.
  • Finally the removeChild() function removes a child from the list of its parent element.

Note that for all these functions, the object referred to by the variable referenceChild or the variables newChild and oldChild must already exist or an error occurs. Likewise the object referred to by the variable child which is to be removed must already exist or an error occurs.

Moving Nodes to a different Place

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.

Copying Nodes

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 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..

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.

Label and value properties examples

Here is a simple example which changes the label on a button:

Example 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 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 5 : Source View

<button label="Hello" oncommand="alert(this.label);"/>

Toggling a checkbox

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.

Note: If you're creating the checkbox dynamically and it's not yet added to the DOM, you must use setAttribute("checked", "false") instead, because the XBL isn't initiated yet.)

Example 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.

Changing a element disabled or enabled

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 : Source View

<script>
function updateState(){
  var name = document.getElementById("name");
  var sindex = document.getElementById("group").selectedIndex;
  name.disabled = sindex == 0;
}
</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.

The next section will provide more details about manipulating radio groups as well as manipulating lists.