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.

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 it 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 as this is a fairly large topic and there are plenty of other resources that available for this.

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 be 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. The first phase is the capturing phase, in which 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. Then, the event is sent to that XUL element. Finally, during 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 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.

Once you handle an event, regardless of where in the propagation the event is, you will likely want to stop the event from being sent to further elements, essentially stopping the capturing or bubbling phases from continuing. Depending on how you attach the event listener to an element, there are different ways of doing this.

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 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 attach multiple listeners for an event to an element. Using the attribute form is more common for most events.

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 and just call a function defined in a separate script. An example of responding to a button being pressed:

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

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


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

Examples: 6.1.1 6.1.2 6.1.3

Find files example so far: Source View

Add a note User Contributed Notes
June 4, 2005, 8:18 am dontemailme at nojunk dot com
My friend has found the solution to the window.close() problem in firefox.
Follow these steps:
Open Firefox.

In the address bar, type this: "about:config"
Without the quotation marks, of course.

Now, scroll down until you see
an item called "dom.allow_scripts_to_close_windows"

Now, look to the right, you will see by default this was set to
"false". If that is the case, double click on this item, and it will
become "true".

Now, restart Firefox, make sure the setting says "true" now.
And try running your code again.

Good Luck.
October 4, 2004, 4:30 am macke at meteli dot net
This took a long time for me to figure out, in all documentation for onclose window event it says that for canceling window closure you should return false from the event handler, but this doesn't work. It seems like the right way to prevent a window from closing is by using: event.preventDefault();
April 2, 2004, 2:43 pm 1 at 234 dot cx
If you prefer to add event listeners with addEventListener, two points must be borne in mind. First of all, when your script runs, much of the DOM tree will not have been constructed. For this reason, it is a good idea to register an OnLoad handler first of all. By definition, OnLoad will not run until the DOM tree is complete. This means that in OnLoad, you can register the rest of the handlers without finding that the corresponding elements do not yet exist.

Secondly, when you use OnLoad in this way, you must register it against the window object. There is no point in trying to register it against any of the XUL elements because, again, these may not exist at the point when your script runs.
March 22, 2004, 3:46 pm steve at zpfe dot com
The oncommand="..." handlers here don't seem to be able to invoke window.close() on Firefox 0.8.

This could be because unprivileged code can't close a window it didn't open. This prevents a web page, for example, from closing your browser. - Neil

Copyright © 1999 - 2005 XULPlanet.com