You are reading O'Reilly XForms Essentials by Micah Dubinko. (What is this?) - Buy XForms Essentials Online
Table of Contents
"While we are free to choose our actions, we are not free to choose the consequences of our actions."
Stephen Covey
"We are not ready for any unforeseen event that may or may not occur."
Dan Quayle
Scripting. It sounds good, at first. With a quick hack, it can cure every problem you throw at it. It's seductive. The problem is, a couple of months later, when you need to fix a bug or add another feature, you can't remember what the script was supposed to do, and you spend a week reverse engineering the whole system. And you'll do it again a few months later. And again, and again. The major problem with script is that even though it's easy to get started with, it's expensive to maintain. If you don't believe it, try debugging someone else's script some time.
There are other problems with scripting as well. Scripts are usually biased towards a visual platform, which means that accessibility aids, or even just regular users with an eyes-free browser, will tend to have trouble with scripting. For the foreseeable future, security-conscious users will have scripting disabled in their browsers.
The answer is to identify areas where scripting is most commonly needed, and create declarative replacements, which is exactly what XForms accomplishes. The core technology that makes this possible is called XML Events.
An event, as far as XForms is concerned, is a data structure that gets passed around to certain interfaces, called event listeners. The Document Object Model (DOM) Level 2 Events specification spells out how events work. The latter part of this chapter describes specific events that are useful in XForms authoring.
Each event has a target element, which represents the point where the main action is happening, from the viewpoint of a particular event. Each event also can have a default action that is effectively triggered by the event. For example, an xforms-focus event will have a form control element as a target and a default action of changing the active focus. DOM Level 2 includes a method to configure an observer, which can take a specific action in response to an event, possibly in lieu of the default action. The most common case is for the observer to attach directly to the target element.
In some cases, however, an observer would like to have broader access to events, for instance to all xforms-focus events for all form controls. In this scenario, the observer is farther downstream from the event target. DOM Level 2 Events define how events "propagate" (or flow) through the DOM, as shown in Figure 7.1, “Event propagation”.
Event propagation is divided into two phases: capture and bubbling. Capture occurs first, as the DOM root node is given the option to observe the event, then additional nodes following the path from the root to the target element. Any listener has the ability to end the propagation, or to prevent the default action of the event. After reaching the target (and giving observers there a change to handle the event), the event reverses course and makes its way back to the root, in a process called bubbling. If the event finishes this journey without being cancelled, the default action happens as long as no listeners have signaled to block the default action.
In the design of HTML forms, script is used whenever some specific action is needed. For example, a form might have a button that copies values from a "ship to" section onto a "bill to" section. In HTML forms plus script, the following code would accomplish this:
<script type="text/javascript"> <!--
function copyAddresses( ) {
var frm = document.forms[0];
frm.shipAddr.value = frm.billAddr.value;
frm.shipCity.value = frm.billCity.value;
frm.shipProv.value = frm.billProv.value;
frm.shipPostCode.value = frm.billPostCode.value;
}
--> </script>This code simply copies form values from one control to another. It would then be activated by a button, with an event-specific attribute, specified like this:
<input type="button" id="cp" value="Copy values" onclick="copyAddresses( )"/>
In terms of DOM Level 2 Events, this represents a registration of an observer on the input element, watching for the DOM click event at the target, and handling the event by calling a short script. As a result, the script in the onclick attribute will get called when the user clicks on the button.
Note that a minor leap of faith is required by the browser to interpret the contents of the onclick attribute as JavaScript—in principle, any scripting language could be used, and a special meta tag would be needed to specify which scripting language is truly in use in the event attributes. (In practice, nobody actually does this, and browsers just muddle through, making essentially an educated guess, almost always JavaScript.) To recap the disadvantages of this approach:
A special hardwired attribute, in this case onclick, is needed. This is inflexible and clutters the language.
Script is difficult to maintain, especially when bits of script are scattered throughout the document.
This won't work in browsers that don't support scripting.
XML Events solves all of these problems by specifying a better way to observe and, ultimately, handle events.
XML Events provides an element-based syntax for DOM Level 2 events. It defines a listener element that, even though not directly used in XForms, is still worth examining. This element has eight attributes defined:
This attribute specifies the name of the attribute to be observed. Event names are defined in DOM Level 2 Events (for general-purpose events) and in XForms, as in Chapter 7, Actions and Events.
This attribute specifies, by a URI reference, the desired handler for the event. XForms defines a number of handlers, covered in the next section. Note that to specify an element through a URI reference, a so-called "fragment identifier" beginning with the # character has to be used; for example, "#handler".
This attribute specifies the DOM Level 2 Events phase in which the observer is to be active. Note that a single observer can't look for both the capture and bubbling phases. The possible values for this attribute are the self-explanatory capture, or default, which covers both the target and the bubbling phase.
This attribute determines whether the default action will be activated for an event. The possible values are cancel and perform (the default). If any observer at any point indicates cancel, then the default processing will not occur. Some events aren't cancellable, and so ignore this attribute.
The specification also calls for an id attribute to be declared by the host language. If the listener element was used in XHTML, it might look like the following.
<head> <listener event="click" observer="btn" handler="#id_of_handler"/> ... </head>
While these attributes map directly to equivalent concepts in DOM Level 2 Events, it can be cumbersome to provide an extra element with extra attributes. To accommodate for this, the XML Events specification contains a few shortcuts, based on namespaced or global attributes. These attributes can be placed directly on the observer element:
<body> ... <input type="button" ev:event="click" ev:handler="#id_of_handler"/> ... </body>
Alternatively, the attributes can be placed on the handler element:
<head> <script type="text/javascript" ev:event="click" ev:observer="btn">...</script> ... </head>
This final technique is used extensively in XForms.
The example in the previous section had a script element that was designated as a "handler." What is the source of such handlers?
Handlers can come from two sources. XForms defines a number of handlers, called XForms Actions, discussed below. Additionally, the host language can define handlers, as is the case with script.
With XForms Actions, the earlier example can be done without any script at all, like this:
<trigger>
<label>Copy values</label>
<action ev:event="DOMActivate">
<setvalue ref="Shipping/Addr" value="../Billing/Addr"/>
<setvalue ref="Shipping/City" value="../Billing/City"/>
<setvalue ref="Shipping/Prov" value="../Billing/Prov"/>
<setvalue ref="Shiping/PostCode" value="../Billing/PostCode"/>
</action>
</trigger>