Adding Event Handlers

The find files dialog so far looks quite good. We haven't cleaned it up much but we have created a simple user interface easily. Next, we will show how to add scripts to it.

Using Scripts

To make the find files dialog functional, we need to add some scripts which will execute when the user interacts with the dialog. We would want to add a script to handle the Find button, the Cancel button and to handle each menu command. We write this using JavaScript functions much in the same way as HTML.

You can use the script element to include scripts in XUL files. You can embed the script code directly in the XUL file in between the opening and closing script tags but it is much better to include code in a separate file as the XUL window will load slightly faster. The src attribute is used to link in an external script file.

Our find files example

Let's add a script to the find file dialog. Although it does not matter what the script file is called, usually it would be the same as the XUL file with a .js extension. In this case, findfile.js will be used. Add the line below just after the opening window tag and BEFORE any elements.

<script src="findfile.js"/>

We'll create the script file later when we know what we want to put in it. We'll define some functions in the file and we can call them in event handlers.

You can include multiple scripts in a XUL file by using multiple script tags, each pointing to a different script. You may use relative or absolute URLs. For example, you may use URLs of the following form:

<script src="findfile.js"/>
<script src="chrome://findfiles/content/help.js"/>
<script src="http://www.example.com/js/items.js"/>

This tutorial does not attempt to describe how to use JavaScript (except as related to event handling) as this is a fairly large topic and there are plenty of other resources that are available for this.

By default the JavaScript console only shows errors from web content. To show errors in chrome JavaScript, it is necessary to change the preference javascript.options.showInConsole to true. You can also change the preference javascript.options.strict for debugging. By setting this value to true, non-standard, poorly written, or syntax prone to cause logic errors are logged to the JavaScript console.

Responding to Events

The script will contain code which responds to various events triggered by the user or other situations. There are about thirty or so different events that may be handled in several different ways. A typical event is the user pressing a mouse button or pressing a key. Each XUL element has the ability to trigger certain events in different situations. Some events are triggered only by certain elements.

Each event has a name, for example, 'mousemove' is the name of the event that is triggered when the user moves the mouse over a UI element. XUL uses the same event mechanism as defined by DOM Events. When an action occurs that would trigger an event, such as the user moving the mouse, an event object is created corresponding to that event type. Various properties are set on the event object such as the mouse position, the key that was pressed, and so forth.

The event is then sent to the XUL in phases.

  • In the capturing phase, the event is first sent to the window, then to the document, followed by each ancestor of the XUL element where the event occured downwards until it reaches that element.
  • In the target phase, the event is sent to the target XUL element.
  • In the bubbling phase, the event is sent to each element back upwards until it reaches the window again.

You can respond to an event during either the capturing or bubbling phase. Once the event has finished propagating, any default action will occur, which is the built in behaviour of the element.

For example, when the mouse is moved over a button that is inside a box, a 'mousemove' event is generated, and sent first to the window, followed by the document, and then the box. That completes the capturing phase. Next, the 'mousemove' event is sent to the button. Finally, the bubbling phase causes the event to be sent to the box, document and window. The bubbling phase is essentially the reverse of the capturing phase. Note that some events don't do the bubbling phase.

You can attach listeners to each element to listen to the events during each step of event propagation. Due to the way a single event is passed to all the ancestors, you may attach a listener to a specific element or to an element higher in the hierarchy. Naturally, an event attached to an element higher up will receive notification of all elements inside it, whereas an event attached to a button will only receive events pertaining to that button. This is useful if there are several elements you would like to handle using the same or similar code.

The most common event used is the 'command' event. The command event is fired when a user activates an element, for example by pressing a button, changing a checkbox or selecting an item from a menu. The command event is a useful event since it automatically handles different ways of activating the element. For example, the command event will occur regardless of whether the user uses the mouse to click a button, or presses the Enter key.

There are two ways to attach an event listener to an element. First, by using an attribute with a script as its value. Second, by calling an element's addEventListener method. The former may only handle bubbling events but tends to be simpler to write. The latter can handle events at any phase and may also be used to attach multiple listeners for an event to an element. Using the attribute form is more common for most events.

Attribute Event Listeners

To use the attribute form, place an attribute on the element where you want the event listener to be, the name of which should be the event name preceded by the word 'on'. For example, the corresponding attribute for the 'command' event is 'oncommand'. The value of the attribute should be some script that should be executed when the event occurs. Typically, this code will be short or just call a function defined in a separate script. An example of responding to a button being pressed:

Example 1 : Source View

<button label="OK" oncommand="alert('Button was pressed!');"/>

Since the command event will bubble, it is also possible to place the event listener on an enclosing element. In the example below, the listener has been placed on a box and will receive events for both elements.

Example 2 : Source View

<vbox oncommand="alert(event.target.tagName);">
  <button label="OK"/>
  <checkbox label="Show images"/>
</vbox>

In this example, the command event will bubble up from the button or checkbox to the vbox, where it is handled. If a second listener (the oncommand attribute) were placed on the button, its code will be called first, followed by the handler on the vbox. Event handlers are passed the event object as an implied argument called 'event'. This is used to get specific information about the event. One commonly used property is the 'target' property of the event, which holds the element where the event actually occured. In the example we display an alert containing the target's tag name. The target is useful when using a bubbling event so that you could have a set of buttons which are all handled by a single script.

You might notice that the attribute syntax is similar to that used for events in HTML documents. In fact, both HTML and XUL share the same event mechanism. One important difference is that while the 'click' event (or the onclick attribute) is used in HTML to respond to buttons, in XUL the command event should be used instead. XUL does have a click event, but it only responds to mouse clicks, not to keyboard usage. Thus, the click event should be avoided in XUL, unless you have a reason to have an element that can only be handled with a mouse. In addition, whereas the command event will not be sent if an element is disabled, the click event will be sent regardless of whether the element is disabled or not.

Our find files example

A command handler can be placed on the Find and Cancel buttons in the find files dialog. Pressing the Find button should start the search. Because we aren't going to implement this part yet, we'll leave it out for now. However, pressing the Cancel button should close the window. The code below shows how to do this. While we're at it, let's add the same code to the Close menu item.

<menuitem label="Close" accesskey="c" oncommand="window.close();"/>
...

<button id="cancel-button" label="Cancel"
     oncommand="window.close();"/>

Two handlers have been added here. The oncommand attribute was added to the Close menu item. By using this handler, the user will be able to close the window by clicking the menu item with the mouse or by selecting it with the keyboard. The oncommand handler was also added to the Cancel button.

DOM Event Listeners

The second way to add an event handler is to call an element's addEventListener method. This allows you to attach an event listener dynamically and listen for events during the capturing phase. The syntax is as follows:

Example 3 : Source View

<button id="okbutton" label="OK"/>

<script>
function buttonPressed(event){
  alert('Button was pressed!');
}

var button = document.getElementById("okbutton");
button.addEventListener('command', buttonPressed, true);
</script>

The getElementById() function returns the element with a given id, in this case the button. The addEventListener() function is called to add a new capturing event listener. The first argument is the name of the event to listen to. The second argument is the event listener function which will be called when the event occurs. Finally, the last argument should be true for capturing listeners. You can also listen during the bubbling phase by setting the last argument to false. The event listener function passed as the second argument should take one argument, the event object, as shown in the declaration for the buttonPressed function above.

Find files example so far : Source View

Next, we'll look at some more details about the event object.