An XForms case study, part 2: triggers for setting values

[20 February 2012]

This is another in an ongoing series of posts describing the design and implementation of the evaluation forms we put together last year for Balisage: The Markup Conference. The first post in the series discussed the overall look and feel of the forms.

The conference has used printed feedback forms in the past, sanitary and the online forms ask essentially the same questions as the paper forms have always asked. Overall, this what’s your impression of the registration process? the materials (the on-site guide to the conference, the proceedings), etc. etc. For each question, the paper forms ask for an overall judgement (Good, Okay, Bad) and provide a space for comments. In recent years, the space for the overall judgement has taken the form of three small graphic images, showing smiling, neutral, and frowning faces:

A first version of the form

The XML document created by the form has a sequence of elements for the various topics: registration, materials, presentations, etc. Each covers one question in the form. For most questions, the element has an attribute named overall to record the user’s overall judgement of how well we did in that area, and its contents include any comments the user types into the comments field. (A few questions have a more complex XML representation, which will be discussed in separate posts.) If you would like to see the XML produced by the form in full detail, you can: go to the overview page, select the form you want to examine, optionally fill it in with some sample data, and select the Show XML button at the bottom of the page.

A straightforward translation into XForms of the question about registration would use a select1 element with the three values Good, Okay, and Bad, and an associated comments field, like this:

<xf:group ref="instance('eval')/registration">
<xf:label>Registration process</xf:label>
<xf:select1 ref="@overall" appearance="full">
<xf:label/>
<xf:item>
<xf:label>Good</xf:label>
<xf:value>good</xf:value>
</xf:item>
<xf:item>
<xf:label>Okay</xf:label>
<xf:value>okay</xf:value>
</xf:item>
<xf:item>
<xf:label>Bad</xf:label>
<xf:value>bad</xf:value>
</xf:item>
</xf:select1>
<xf:textarea ref=".">
<xf:label>Comments</xf:label>
</xf:textarea>
</xf:group>

In a browser, it would look something like this:

You can also load the entire form into your own browser to examine it.

A first enhancement: smiling faces

The first obvious enhancement is to follow the paper form in using images of smiling and frowning faces for the various values. A few hours on the Web turned up a large number of public-domain clip-art sites, many with sets of widgets including smiling and frowning faces. It turns out that there are also smiling-face and frowning-face characters in Unicode (in the Miscellaneous Symbols block U+2600 to U+26FF), but no corresponding neutral face. And nowadays there is an entire block of Unicode emoticons (U+1F600 to U+1F64F). I was tempted by the Unicode characters, but ultimately decided that on my screen, at least, they were too hard to read clearly. So eventually I settled on a set of icons from a clip-art site.

Integrating the smiley images into the labels is simple: just embed an img element in the label, as shown here.

<xf:group ref="instance('eval')/registration">
<xf:label>Registration process</xf:label>
<xf:select1 ref="@overall" appearance="full">
<xf:label/>
<xf:item>
<xf:label>
<img src="lib/smiley_thumbs_up.png"
class="emoticon"
height="20"
alt="Good"/> Good
</xf:label>
<xf:value>good</xf:value>
</xf:item>
<xf:item>
<xf:label>
<img src="lib/smiley_pleased.png"
class="emoticon"
height="20"
alt="OK"/> Okay
</xf:label>
<xf:value>okay</xf:value>
</xf:item>
<xf:item>
<xf:label>
<img src="lib/smiley_thumbs_down.png"
class="emoticon"
height="20"
alt="Bad"/> Bad
</xf:label>
<xf:value>bad</xf:value>
</xf:item>

</xf:select1>
<xf:textarea ref=".">
<xf:label>Comments</xf:label>
</xf:textarea>
</xf:group>

This provides a bit more color in the form:

To make it easier to see what the images are saying, the full form includes a large version of the images at the top, with explanatory labels. (This version of the form actually uses two different sets of clip-art images, so I could look at each of them in context and decide which one I wanted to use.)

A second enhancement: clicking on the images

The overall Good / Okay / Bad ratings still take up a lot of screen real estate, though.

And what I really wanted was not just to use the images as part of the labels for the radio buttons, but to have an even simpler interface: ideally, I wanted just to show the three icons and let the user click on them to specify that they thought things were good, back, or okay. (There is a certain amount of danger in this way of thinking: part of the point of XForms is to provide device-independent forms that might be rendered in different ways on different devices: getting really specific about details like this may interfere with device independence.)

This can be achieved in XForms, too. After some experimentation, I ended up a method of handling this that looks like this in the browser: a large image showing the current rating for the topic, and three small images (but larger than the images in the earlier versions of the form) for changing the rating.

The large display is handled with a sequence of xf:group elements, each of which contains an img element and each of which binds to the overall rating for a given topic if that rating has the particular value associated with the image.

<xf:group ref="instance('eval')/registration">
<xf:label>Registration process</xf:label>
<div class="overall-rating">
<xf:group ref=".[@overall='good']">
<img src="lib/smiley_thumbs_up.png"
height="150px"
alt="good"/>
</xf:group>
<xf:group ref=".[@overall='okay']">
<img src="lib/smiley_pleased.png"
height="150px"
alt="okay"/>
</xf:group>
<xf:group ref=".[@overall='bad']">
<img src="lib/smiley_thumbs_down.png"
height="150px"
alt="bad"/>
</xf:group>
<xf:group ref=".[@overall='dunno']">
<img src="lib/smiley_no_speak.png"
height="150px"
alt="good"/>
</xf:group>
</div>
...
</xf:group>

So the big image for the registration topic will show a smiling face if the overall attribute on the registration element has the value good, and a frowning face if it has the value bad, and so on.

The value of the overall attribute could be set by the select1 elements shown above, but in the final version we used a set of XForms triggers (user-activatable controls — on a laptop screen these are typically buttons) labeled with the images for the values:

<div class="simple">
<xf:group ref="instance('eval')/registration">
<xf:label>Registration process</xf:label>
... <!--* read-only display image, as shown above *-->
<div class="overall-triggers">
<xf:trigger>
<xf:label><img src="lib/smiley_thumbs_up.png"
height="40" alt="good"/></xf:label>
<xf:setvalue ev:event="DOMActivate"
ref="@overall"
value="'good'"/>
</xf:trigger>
<xf:trigger>
<xf:label><img src="lib/smiley_pleased.png"
height="40"
alt="okay"/></xf:label>
<xf:setvalue ev:event="DOMActivate"
ref="@overall"
value="'okay'"/>
</xf:trigger>
<xf:trigger>
<xf:label><img src="lib/smiley_thumbs_down.png"
height="40" alt="bad"/></xf:label>
<xf:setvalue ev:event="DOMActivate"
ref="@overall"
value="'bad'"/>
</xf:trigger>
</div>
<xf:textarea ref=".">
<xf:label>Comments</xf:label>
</xf:textarea>
</xf:group>
</div>

As you can see, each trigger contains a label element which in turn contains the image, and a setvalue element which specifies the action to be run when the trigger is activated. In each case, the action is to set the value of the overall attribute to the appropriate value.

The group as a whole, the large image, and the set of three triggers are each wrapped in an XHTML div element with a different class value, to make it easier to style the display appropriately using CSS.

With XForms, it was easy to get a first working version of the form, much easier than it has ever been for me to get an equivalently complex form running in straight HTML forms, with its model of form content as a flat undifferentiated sequence of attribute-value pairs. And with XForms and CSS, it was (relatively) easy to change the look and to replace the radio buttons of the first version of the form with image-labeled buttons. Working with Javascript libraries has never been anything like this straightforward for me.