Programming
Article
By Kevin Yank

Cross-browser XForms

By Kevin Yank
Last chance to win! You'll get a... FREE 6-Month Subscription to SitePoint Premium Plus you'll go in the draw to WIN a new Macbook SitePoint 2017 Survey Yes, let's Do this It only takes 5 min

XForms is a complex but powerful replacement for HTML forms that relies heavily on cutting-edge XML technology. As a result of its complexity, browsers have been typically slow to implement it. But with working plugins for Internet Explorer and a pre-release extension now available for Firefox, the time is right to give this technology another look.

The last time I mentioned XForms, it seemed like the technology — at least in mainstream Web browsers — would never see the light of day. It required a plugin to work in Internet Explorer, Mozilla couldn’t find people willing to work on it, and Apple and Opera had both labelled it as impractical for various reasons.

Then, in August last year, Novell and IBM announced they would develop XForms support for Mozilla browsers. Now, those efforts are finally starting to pay off. So does that mean we can start writing XForms that work across browsers?

To find out, I installed the latest version of FormsPlayer for Internet Explorer 6 and Mozilla’s XForms extension preview release for Firefox 1.5 Beta 2, then started writing XForms.

XForms can fit right into an XHTML document, but the Firefox extension requires that the document be served with the true XHTML MIME type (application/xhtml+xml). It’s easy enough to use PHP (or another server-side language) to see if the browser supports this and send the appropriate HTTP header:

if (stristr($_SERVER['HTTP_ACCEPT'], "application/xhtml+xml")) {
  header("Content-type: application/xhtml+xml");
}

To identify XForms tags within your XHTML document, you need to declare a namespace prefix for the tags. While we’re at it, we’ll declare a prefix for XML Events as well, as XForms uses that standard to handle user interface events:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xforms="http://www.w3.org/2002/xforms"
    xmlns:ev="http://www.w3.org/2001/xml-events">

Since Internet Explorer doesn’t fully support XHTML, the FormsPlayer plugin needs to be loaded before any XForms tags are used, and told which namespace prefix you’ll use for XForms. For this, you need an <object> tag and an XML processing instruction in the <head> of the document:

<object id="FormsPlayer" width="0" height="0"
    classid="CLSID:4D0ABA11-C5F0-4478-991A-375C4B648F58">
</object>
<?php echo '<?import namespace="xforms" implementation="#FormsPlayer"?>'; ?>

I’ve output the processing instruction with PHP to prevent it from causing a PHP error if PHP’s short tags feature is enabled.

Now we can get down to writing some XForms. The basic premise of the technology is that you define one or more models — XML document skeletons — and then define the form that will be used to fill them in with data using a set of user interface elements.

Here’s an XForms model for a simple login form:

<xforms:model id="login">
  <xforms:instance>
    <login xmlns="">
      <username />
      <password />
    </login>
  </xforms:instance>
  <xforms:bind nodeset="/login/username" required="true()" />
  <xforms:submission action="submit.php" method="post" id="loginsubmit"
      includenamespaceprefixes="#default" />
</xforms:model>

The <xforms:instance> tag contains the XML document skeleton to be filled in. The <xforms:bind> tag indicates that the username tag requires a value before the form can be submitted. The <xforms:submission> tag says where and how the completed document should be submitted (in this case, a POST request to submit.php).

We can also create a second model–the equivalent of a second old-style HTML form–to allow a new visitor to register for an account:

<xforms:model id="newuser" schema="newuser.xsd">
  <xforms:instance>
    <newuser xmlns="">
      <username />
      <email />
    </newuser>
  </xforms:instance>
  <xforms:bind nodeset="/newuser/username" required="true()" />
  <xforms:bind nodeset="/newuser/email" required="true()" />
  <xforms:submission action="submit.php" method="post" id="newusersubmit"
      includenamespaceprefixes="#default" />
</xforms:model>

The main difference here is the schema attribute on the <xforms:model> tag. While we can use <xforms:bind> tags to make particular values required for submission, this attribute lets us control when values are considered valid. The specified file (newuser.xsd in this case) uses the XML Schema standard to describe the data values that must make up the submitted document.

This is where XForms lost a lot of support from browser makers. XML Schema is a complex standard, difficult both to learn and to implement, because it gives you incredible control over the kinds of values that are
acceptable in an XML document. For example, we can use a regular expression to require a valid email address:

<xsd:element name="email" type="emailtype" />
<xsd:simpleType name="emailtype">
  <xsd:restriction base="xsd:string">
    <xsd:pattern value="[-._a-zA-Z0-9]+@([a-zA-Z0-9]+.)+[a-zA-Z]" />
  </xsd:restriction>
</xsd:simpleType>

Unfortunately, because of the amount of work involved in implementing XML Schema, the current Mozilla XForms support does not enforce constraints like these. Just like JavaScript-based form validation logic, however, XForms must be validated server-side after submission anyway, so this is more an inconvenience than a deal-breaker.

Back in our XHTML document, we can now put together some form components to enable users to fill in and submit our two models. First, we want to let visitors choose if they will log in or register for a new account:

<xforms:group>
  <xforms:trigger>
    <xforms:label>Log In</xforms:label>
    <xforms:toggle case="logincase" ev:event="DOMActivate" />
  </xforms:trigger>
  <xforms:trigger>
    <xforms:label>Register</xforms:label>
    <xforms:toggle case="newusercase" ev:event="DOMActivate" />
  </xforms:trigger>
</xforms:group>

Each of the <xforms:trigger> tags here will create (in a desktop Web browser, at least) a button with the specified label (<xforms:label>). When these buttons are clicked (which
generates a DOMActivate event), the <xforms:toggle> tag will display a case — one of a number of available sub-forms. One of these will let the user log in, while the other will allow a new user to register.

<xforms:switch>
  <xforms:case id="logincase">
    ...login form...
  </xforms:case>
  <xforms:case id="newusercase">
    ...registration form...
  </xforms:case>
</xforms:switch>

Here’s the login form:

<xforms:input ref="/login/username" model="login">
  <xforms:label>Username</xforms:label>
  <xforms:hint>6-32 characters (letters, numbers, or underscores)</xforms:hint>
</xforms:input>
<xforms:secret ref="/login/password" model="login">
  <xforms:label>Password</xforms:label>
  <xforms:hint>The password you enter will not be displayed</xforms:hint>
</xforms:secret>            
<xforms:submit submission="loginsubmit">
  <xforms:label>Log In</xforms:label>
</xforms:submit>

It uses an <xforms:input> tag for the username field, an <xforms:secret> tag for the password field, and an <xforms:submit> tag for the submit button. Each of the fields has ref and model attributes that point to the specific element in the model that we want this field to set. The submit button indicates which <xforms:submission> tag to trigger — which model to submit and how.

So, how do our two XForms-enabled browsers cope with all this?

XForms default rendering in both browsers

About what we’d expect from an unstyled HTML form, really. Labels, fields and buttons are laid out inline with default dimensions and appearance. Just like HTML forms, XForms should be styled using CSS. Unfortunately, this is where the cross-browser niceness starts to fall apart.

The whole point of the XForms standard is that the appearance of the form’s user interface is determined by the browser or device displaying the form. The specification makes no comment on how CSS should apply to XForms fields, buttons, and whatnot. As a result, different XForms implementations will require different CSS.

Say you want to make the label of a form field bold and red when it contains an invalid value. Here’s how to do it for FormsPlayer:

xforms:input.invalid xforms:label,
xforms:secret.invalid xforms:label {
    color: red;
    font-weight: bold;
}

Easy enough, although it doesn’t conform to the standard for representing XML namespaces in CSS selectors. Firefox, however, does, so it requires this code instead:

@namespace xf url("http://www.w3.org/2002/xforms");
xf|input[invalid] xf|label,
xf|secret[invalid] xf|label {
    color: red;
    font-weight: bold;
}

Also note that the Firefox XForms extension currently uses attributes to identify states like invalid fields. In the final version of the extension, it’ll use pseudo-classes instead:

@namespace xf url("http://www.w3.org/2002/xforms");
xf|input:invalid xf|label,
xf|secret:invalid xf|label {
    color: red;
    font-weight: bold;
}

And that’s just the tip of the iceberg. Until better standards are developed in this area, you’ll basically need a separate style sheet for each of the XForms implementations you wish to support. For this case, we can use Internet Explorer’s conditional comments feature to apply the FormsPlayer style sheet in that browser only:

<link rel="stylesheet" type="text/css" href="xforms-mozilla.css" />
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="xforms-formsplayer.css" />
<![endif]-->

With a bit of experimentation, I was able to set up style sheets that got the form looking pretty good in both implementations.

XForms styled rendering in both browsers

As you can see, when the Mozilla XForms implementation becomes publicly available, developers who have the luxury of asking their Internet Explorer users to install a plugin could find real-world uses for this technology fairly quickly.

Browser support is still something of a sticking point, however, with Apple and Opera working on supporting the less ambitious Web Forms 2.0 in their next major browser releases, and native support in Internet Explorer nowhere in sight. FormsPlayer displays a really ugly ad for itself as a browser toolbar when it starts up unless you manually disable it with Tools > Manage Add-ons…, so I’d hate to ask my users to install it.

Still, XForms is compelling technology — the example we’ve looked at here has only just scratched the surface of what XForms is capable of. If you’ve ever had to code a five-page form in three different languages with full client-side validation and fields that should appear and disappear in response to other selections, XForms is for you. It can knock over tasks like that with incredible ease.

Curious? Grab yourself one of the XForms implementations discussed here and try the example for yourself. Download the code and have a tinker, then head over to the XForms website and read a tutorial or two.

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.Is it good?