Easy ARIA tip #3: aria-invalid and role “alert”

I know, I know, it’s been a while since I posted my last Easy ARIA tip. But I’m hoping that this one will find you all excited and willing to play with it some more!

The problem: You have a form, a contact form, for example, that you want to put some accessible error checking into. Common problems are e-mail addresses that are not valid, or a name that does not contain at least a first and a surname.

The form

Let’s start out with a simple form.

<html>
<head>
<title>Contact form</title>
</head>
<body>
<form method="post" action="post.php">
<fieldset><legend>Please enter your contact details</legend>
<label for="name">Your name (required):</label>
<input name="name" id="name" aria-required="true"/><br />
<label for="email">E-Mail address (required):</label>
<input name="email" id="email" aria-required="true"/><br />
<label for="website">Website (optional):</label>
<input name="website" id="website"/>
</fieldset>
<label for="message">Please enter your message (required):</label><br />
<textarea name="message" id="message" rows="5" cols="80" aria-required="true"></textarea><br />
<input type="submit" name="submit" value="Send message"/>
<input type="reset" name="reset" value="Reset form"/>
</form>
</body>
</html>

Straight and simple, but we’re not here to win beauty prices anyway. :-)

Checking for validity and notifying the user

Checking the validity and notifying the user consists of several steps:

  1. Checking if the e-mail address or entered name are valid. To keep it simple, we’ll check whether the e-mail address contains the “@” symbol, and if the name entry contains at least 1 space characters ” “.
  2. Setting the field’s aria-invalid attribute and giving it a value of “true”.
  3. Notifying the user via an alert that the value entered was incorrect. Instead of using an intrusive dialog box created by the JavaScript ‘alert’ function, we’ll use a simple WAI-ARIA widget to do it. This notifies the user, but lets them continue interacting with the form without any interruptions.

All of this happens when the input loses focus, meaning in the “onblur” handler.

The JavaScript code I wrote looks like this, inserted above the closing “head” tag:

<script type="application/javascript">

  function removeOldAlert()
  {
    var oldAlert = document.getElementById("alert");
    if (oldAlert)
      document.body.removeChild(oldAlert);
  }

  function addAlert(aMsg)
  {
    removeOldAlert();
    var newAlert = document.createElement("div");
    newAlert.setAttribute("role", "alert");
    newAlert.setAttribute("id", "alert");
    var msg = document.createTextNode(aMsg);
    newAlert.appendChild(msg);
    document.body.appendChild(newAlert);
  }

  function checkValidity(aID, aSearchTerm, aMsg)
  {
    var elem = document.getElementById(aID);
    var invalid = (elem.value.indexOf(aSearchTerm) < 0);
    if (invalid) {
      elem.setAttribute("aria-invalid", "true");
      addAlert(aMsg);
    } else {
      elem.setAttribute("aria-invalid", "false");
      removeOldAlert();
    }
  }

</script>

The checkValidity function

The core is the checkValidity function. It takes three parameters: The ID of the input that is to be validated, the term to search for to ensure validity, and the error message to be inserted into the alert.

To see if it is valid, the function checks whether the indexOf the input’s value is anything greater than -1. A value of -1 or less is returned if the index of the search term could not be found within the value.

If invalid, the function does two things:

  1. It sets the element’s aria-invalid attribute to “true”, which will indicate to screen readers that there is invalid content in here.
  2. It will call the addAlert function to add the alert with the provided error message.

If the search term is found, the aria-invalid attribute is reset to “true”. In addition, any alert that still might be around is removed.

The addAlert function

This function first removes any old alerts. The function is simple: It looks for an element with id “alert”, and if found, removes that from the document object model.

Next, the function creates a div element to hold the alert text. It gets an ID of “alert”. And it gets a role set of “alert”. This is actually ARIA-inspired, even though it doesn’t say “aria” in the attribute name. The reason is that role is based on the XHTML role attribute module that was simply ported to HTML for simplicity.

The text is added to the div element, and the div element is added to the document.

The moment this happens, Firefox will fire an “alert” event to assistive technologies when this div appears. Most screen readers will pick this one up automatically and speak it. This is similar to the Notification Bar in Firefox that prompts you whether you want to save a password. Our one does not have any buttons to press, it just tells us what’s wrong.

Adding the magic to the “onblur” event

All that’s left now is add the event handler. We need to change the two inputs for e-mail and name for this:

<input name="name" id="name" aria-required="true" onblur="checkValidity('name', ' ', 'Invalid name entered!');"/><br />
<input name="email" id="email" aria-required="true" onblur="checkValidity('email', '@', 'Invalid e-mail address');"/><br />

Testing the example

I’ve put up the above as an static example page for you to try it out. If you use Firefox 3 and a current supported screen reader, try the following:

  1. Enter only your first name as the name. When tabbing, you’ll hear an alert that tells you you’ve entered an invalid name. You can then shift-tab back and correct the error.
  2. Enter an e-mail address that has no “@” symbol. When tabbing out of this field, you should hear a warning that says you didn’t enter a valid e-mail address.

In both cases, when returning focus to the field in question, your screen reader should tell you that this field is invalid. JAWS 9 supports this, but JAWS 8 does not, so this may not work in all versions of the screen readers supported.

A few questions that you might have

Why did you put both “(required)” in the label text and the aria-required attribute on some of the inputs?
Because if this were a real live form, and the site was being visited by a browser that does not yet support ARIA, we’d still want to give an indication that this is a required field.
Why don’t you set focus back to the invalid field automatically?
Because this is not allowed by at least the Windows API specs and possibly others. Also, letting the focus jump around without real user interaction too often is not a nice thing to do in general.

In conclusion

Personally, it is my hope that websites would include such techniques more often in the future when filling out forms. There’s nothing more frustrating than filling out a form with 20 or so fields, submitting it, only to find that field 3 was invalid, and having to go through all fields again to make sure the values were retained, or supplying some information redundantly.

This is one of those examples where, in my opinion, more direct accessibility and user-friendliness can be achieved by explicitly using some JavaScript in combination with ARIA.

I hope you found this little tutorial of some use! I’d welcome your feedback as always!

And of course, you’re welcome to enhance this little example as a “homework” to also check whether something valid was entered for the “message” textarea.

Previous Easy ARIA tips
  1. aria-required
  2. aria-labelledby and aria-describedby

28 comments:

  1. Another great post Marco thanks.

    You know it touches on another issue I’ve been thinking about recently; namely should you have fallback server-side validation in case the UA doesn’t have javascript. I know you’re really demonstrating ARIA not advocating any validation method but I wonder if these days we can almost assume javascript in most cases and make the server side primative (but still putting on those aria attributes). it’s always a pain do both side and in some case server side only is an easier answer but needs doc refresh or AJAX infrastructure.

  2. The “message” textarea has a label that indicates it’s required, and it has the aria-required attribute, but no validity check is performed on it.

  3. Steve, I think you’re touching on a great subject here. Of course the less duplication of code we have the better. And the validation mechanisms could definitely be done in Ajax as well. The onblur event could easily make Ajax calls and get the responses back from the server and set the attributes and fire the alert. That way the validation would be on the server still, and be run through when the form is submitted.

    But with this approach I’m showing here, the number of falsely filled-out forms being submitted should be drastically smaller.

  4. Hi Jesse,

    well, read my last paragraph again. You’re welcome to add something yourself as an exercise. I left it out on purpose.

  5. @ Marco interesting, However onBlur and Ajax still needs javascript and I think my underling question of can we assume JS on all UAs is important. Progressive enhancement says you should be able to work without and add bells if it is so that means pure server side validation and then client side if possible which gets us back to duplication. On the other hand all these Ajax apps assume JS so is that the way of the web these days and a safe assumption?

  6. @Steve and Marco: Initially I wasn’t fond of the graceful degradation part of progressive enhancement (see http://mindforks.blogspot.com/2007/11/progressive-enhancement.html) but later changed my mind (see http://mindforks.blogspot.com/2007/11/graceful-degradation-is-good.html).

    I think as a abstract concept “graceful degradation” is good, but I don’t think we need to assume non javascript platforms as we move on… Others would disagree of course.

  7. @DavidB: so even the great DB can change his mind ;-) Thanks for the comments, I guess we’re in a interim period and we’ll have to see what falls out. One thing is certain Javascript based web apps are here and very popular.

    Could we say forms are interactive so are really simple web applications so can assume Javascript? I’m beginning to thinks so. For documents and basic page/site navigation I see a very good case for Progressive Enhancement.

  8. Hmm. It seems like implementing Web Forms 2 would make this kind of think easier for authors and provide all the accessibility functionality for free. For example to mark up a control that is required and must contain something that looks like an email address, you would simply do:

    <input type=”email” required>

    See this article about Opera’s support for more examples.

  9. Thanks Marco but standards are standards. Supporting open web standards does not, in my view, include picking and choosing when and how to apply them.

    I’ll wait to see if ARIA makes it into HTML5 and if browsers support it. Neither is guaranteed in the glacial world of web standards.

  10. Very interesting article!

    On the question of server-side/client-side error checking duplication question:

    Even if you assume that everyone has JavaScript, most people also have the ability to turn it off whenever they want. Therefore, if your error checking needs to happen, you have to have server-side error checking as a fall back.

    It doesn’t have to be js, of course. For example, if your web site is a typical PHP/MySQL set up, you could use js to make quick & convenient error checking for your user and PHP server-side error checking as the fall back.

    If you allow user input, you need error checking that the user can’t disable or you set your site up for easy hacking.

  11. Great article and interesting example.
    Testing the form in NVDA/Firefox I found that the alert is announced but the label on the next input tabbed to is not, or not consistently. I don’t know whether or not this is just my set-up. The effect is a bit disorientating, especially when tabbing and shift-tabbing through several ‘required’ fields.

  12. @William: The problem is that alerts are usually meant to — umm — alert you, meaning interrupt anything that’s currently being spoken. So what’s happening is that NVDA tries to speak the next label, but gets interrupted by the alert message. The user can always press NVDA+Tab to hear the newly focused item. But the alert is indeed to alert as soon as possible to grab the user’s attention.

  13. The alert works well. My concern was that the user doesn’t know which input they are in after they have tabbed. A colleague, Lynn Holdsworth, has suggested giving the dynamically created alert div an aria-live attribute as in: newAlert.setAttribute(“aria-live”, “assertive”); instead of role=”alert”. We know it’s not ideal markup, but both the ‘alert’ message and the label are announced. What do you think?

  14. Well, the alert is basically an assertie live region. This is implicitly implied by the role of “alert”. Normally, a screen reader should act the same here: Interrupt anything and speak the assertive live message immediately. NVDA doesn’t support that distinction in its current release yet. That’s why it works differently right now.

  15. Just a few points to pick up on:

    Although the alert is semantically more correct here, the ARIA-Live=”assertive” attribute currently works better in both Jaws and NVDA.

    On the error-checking subject, in the interest of data integrity I’d say server-side checking is absolutely essential. If the same server-side code can be reused for client-side AJAX-based checks, so much the better.

What are your thoughts?