Better client-side error display

See more about:

This lesson’s goals

By the end of this lesson, you should:

  • Know how to report errors in a professional way.
  • Understand how page-wide, global error messages help the user.
  • Understand how JavaScript functions make error reporting easier to program.

The goal

In the last couple of lessons, we improved the server-side error processing. We came up with something like this:

Ugly error messages

Figure 1. Ugly error messages

This is, well, blah. Let’s improve it. We want the order form to look like this:

Empty order form

Figure 2. Empty order form

Suppose there are errors:

Order form with errors

Figure 3. Order form with errors

Let’s show error messages like this:

Error messages

Figure 4. Error messages

Try it, and you’ll see the error messages are animated.

Field and global error messages

Each field has its own place for error messages, right below the field. In addition, at the top of the form, there’s what I am calling a “global error message.” It shows that there is some error on the page.

Why have a global error message? Two reasons.

Some errors aren’t about a particular field

Let’s change the way a blank field is interpreted. If a user leaves a field blank, we’ll assume the user doesn’t want to order any of the product. We won’t make the user enter zero into the field.

But this creates a problem. Suppose the user leaves all the fields blank and clicks the order button. There’s an error; nothing was ordered, so clicking the order button makes no sense. But the error isn’t associated with any one field.

Having a global error message gives us a place to put an error message like this:

Nothing ordered

Figure 5. Nothing ordered

Drawing attention

You might have a long form with, say, 12 fields. This gives you a long page. The user won’t be able to see all of the fields at the same time.

Browser window

Figure 6. Browser window

The user has to scroll to see the entire page.

The global error message makes it easier for the user to see whether there are any errors. The small error icon:

Error icon

helps the user scan down the page and find information about specific errors.

Let’s look at the client side error reporting: the HTML, CSS, and JavaScript. We’ll combine it with PHP in the next lesson.

HTML for the error messages

Let’s look at the HTML code for the global error message. That’s the message at the top of the page, telling the user whether there were any errors at all. Remember that it should look like this when displayed:

Global error message

Figure 7. Global error message

Here’s the HTML:

<p id="global_error_message_container" class="message_container">
  <img src="error.png" alt="Error">
  <span id="global_error_message"/>
</p>

Figure 8. HTML for the global error message

There’s a <span> inside a <p>. The <span> is used to show the text of the error message. The <p> is a container for both the <span> and an error icon. Showing and hiding the <p> will affect both the icon and the message, treating them as a single unit.

Notice how I have named them. The <span> has an id of global_error_message. The <p> has an id of global_error_message_container. I’ve used that convention throughout the page. The id of the outer element ends in _container.

The icon came from the famfamfam silk icon set. It’s one of the most complete icon sets you can find.

That was the global error message. The HTML for each field’s error message is similar. Here’s one of them.

<p>
  <input type="text" name="frisbees" id="frisbees" size="3">
  Frisbees ($8.95 each)<br>
  <span id="frisbees_message_container" class="message_container">
    <img src="error.png" alt="Error">
    <span id="frisbees_message"/>
  </span>
</p>

Figure 9. HTML for the frisbee message

The error message starts on line 136. Again, there’s an error message element and a container element. This time, the container is a <span>.

The product error messages have an extra naming convention. For product x, the elements are called x_message and x_message_container. So for frisbees, the ids are frisbees_message and frisbees_message_container.

CC
CC

Why do you use these naming rules?

Kieran
Kieran

Because it makes programming easier. You’ll see why later.

JavaScript

Here’s the code that’s executed when the user clicks the Order button.

$("#order_button").click(function() {
  //Set flag showing everything is OK.
  var data_ok = true;

  //Check the frisbees.
  var frisbees = $("#frisbees").val();
  var frisbees_message = check_order_value(frisbees);
  if ( frisbees_message != '' ) {
    data_ok = false;
    show_product_error_message(frisbees_message, 'frisbees');
  }
  else {
    hide_error_message('frisbees');
  }

  //Check the giant chew ropes.
  var giant_chew_ropes = $("#giant_chew_ropes").val();
  var giant_chew_ropes_message = check_order_value(giant_chew_ropes);
  if ( giant_chew_ropes_message != '' ) {
    data_ok = false;
    show_product_error_message(giant_chew_ropes_message, 'giant_chew_ropes');
  }
  else {
    hide_error_message('giant_chew_ropes');
  }

  //Check the squeaky balls.
  var squeaky_balls = $("#squeaky_balls").val();
  var squeaky_balls_message = check_order_value(squeaky_balls);
  if ( squeaky_balls_message != '' ) {
    data_ok = false;
    show_product_error_message(squeaky_balls_message, 'squeaky_balls');
  }
  else {
    hide_error_message('squeaky_balls');
  }

  //Make sure something was ordered.
  if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
    show_global_error_message("Sorry, you must order at least one item.");
    data_ok = false;
  }
  
  if ( data_ok ) {
    hide_global_error_message();
    alert('Everything is OK... so far.')
  }
});

Figure 10. JavaScript

Line 25:

var data_ok = true;

creates a variable that keeps track of whether the data is OK, that is, whether there has been an error. When we get to the end of the validation, we can check this variable to know whether there have been any errors.

Variables like this are often called “flags.” They are usually either true or false, like a physical flag being up or down.

Flag up Flag up, data OK. Variable data_ok is true.

Flag down Flag down, data not OK. Variable data_ok is false.

When the flag is up (the data is OK), we want to do one thing. When the flag is down (there has been an error), we want to do another thing.

Look at lines 28 and 29:

var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);

The first one gets the value the user typed into the frisbee field and puts it into the variable frisbees. Line 29 calls the check_order_value() function, sending in frisbees.

check_order_value() is a JavaScript version of the PHP validation function we saw earlier. It does the same thing; returns either an error message, or an empty string if the the value is OK.

Here is the code:

//Check whether an order value is valid.
//Input
//  value_to_check: the value to check.
//Return: An error message, or empty string if no error.
function check_order_value(value_to_check) {
  if ( isNaN(value_to_check) ) {
    return 'Please enter a number';
  }
  if ( value_to_check != Math.round(value_to_check) ){
    return 'Please enter whole numbers only';
  }
  if ( value_to_check < 0 ){
    return 'Please enter positive numbers only';
  }
  return '';
}

Figure 11. check_order_value()

Line 80 contains something new.:

if ( value_to_check != Math.round(value_to_check) ){

This tests whether value_to_check is an integer, that is, a whole number. Math.round() rounds a number up or down. So:

Math.round(1.4) is 1

Math.round(1.7) is 2

Math.round(1) is 1

If you round a number that is already a whole number, you get the same number you started with. That is why the test works.

You can try round().

Back the to button pressing script. Here’s some more of it.

//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
  data_ok = false;
  show_product_error_message(frisbees_message, 'frisbees');
}
else {
  hide_error_message('frisbees');
}

Part of Figure 10 (again). JavaScript

Line 29 puts either an error message or an empty string in frisbees_message.

If frisbees_message isn’t empty (line 30), the flag gets set to false (line 31), so we can know later that there’s been a problem. Then this is run:

show_product_error_message(frisbees_message, 'frisbees');

This shows the message in the frisbees error spot. It might end up looking like this:

Frisbee error

Figure 11. Frisbee error

I want the show_product_error_message() function to work for every product. That’s why I pass in the product name (frisbees in this case), so the function will know where to show the error message.

Here’s what show_product_error_message() does, in pseudocode.

Set the product error text.
Show the text.
Set the global error text.
Show it.

Here’s the code:

//Show an error message for a product.
//Input
//  message: The message to show.
//  product_name: The product name the message is about.
//Return: nothing.
function show_product_error_message(message, product_name) {
  //Show the error message in the product's message area.
  $('#' + product_name + '_message').text(message);
  if ( $('#' + product_name + '_message_container').is(':hidden') ) {
    $('#' + product_name + '_message_container').show('medium');
  }
  //Show the global error message at the top of the page.
  show_global_error_message("Sorry, I can't process this order.");
}

Figure 13. show_product_error_message()

Look at line 96:

$('#' + product_name + '_message').text(message);

product_name is something like frisbee. So line 96 becomes:

$('#frisbee_message').text(message);

This matches the id in the HTML element:

<p>
  <input type="text" name="frisbees" id="frisbees" size="3">
  Frisbees ($8.95 each)<br>
  <span id="frisbees_message_container" class="message_container">
    <img src="error.png" alt="Error">
    <span id="frisbees_message"/>
  </span>
</p>

Figure 9 (again). HTML for the frisbee message

There it is, in line 138.

Renata
Renata

Aha! So that’s why you used those naming rules!

Kieran
Kieran

Yes! That’s it.

Here are the next lines in show_product_error_message() (Figure 12):

if ( $('#' + product_name + '_message_container').is(':hidden') ) {
  $('#' + product_name + '_message_container').show('medium');
}

Part of Figure 12 (again). show_product_error_message()

This will detect whether the message’s container is hidden, and, if it is, show it.

Remember the pseudocode.

Set the product error text.
Show the text.
Set the global error text.
Show it.

We’ve done the first two. How about the last two? Like this:

show_global_error_message("Sorry, I can't process this order.")

That’s right, another function.

//Show a global error message. It applies to the entire
//  page, not just one product.
//Input
//  message: The message to show.
//Return: nothing.
function show_global_error_message(message) {
  $("#global_error_message").text(message);
  if ($('#global_error_message_container' ).is(':hidden') ) {
    $('#global_error_message_container').show('medium');
  }
}

Figure 13. show_global_error_message()

It’s much like show_product_error_message(), but without the product name part.

Let’s look at the last part of a field’s validation:

//Check the frisbees.
var frisbees = $("#frisbees").val();
var frisbees_message = check_order_value(frisbees);
if ( frisbees_message != '' ) {
  data_ok = false;
  show_product_error_message(frisbees_message, 'frisbees');
}
else {
  hide_error_message('frisbees');
}

Part of Figure 10 (again). JavaScript

If there’s no error, then line 35 runs:

hide_error_message('frisbees');

Here’s the function:

//Hide a product error message.
//Input
//  product_name: The product name the message is about.
//Return: nothing.
function hide_error_message(product_name) {
  $('#' + product_name + '_message_container').hide('medium');
}

Figure 14. hide_error_message()

Send it a product_name, and it hides the error message for that product.

There is one more check we have to do. What if the user orders none of every product?

//Make sure something was ordered.
if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
  show_global_error_message("Sorry, you must order at least one item.");
  data_ok = false;
}

Figure 15. Was anything ordered?

What if there are no errors?

if ( data_ok ) {
  hide_global_error_message();
  alert('Everything is OK... so far.')
}

Figure 16. Nothing wrong

hide_global_error_message() does what you think. Here it is:

//Hide global error message.
//Return: nothing.
function hide_global_error_message() {
  if ($('#global_error_message_container' ).is(':visible') ) {
  $('#global_error_message_container').hide('medium');
  }
}

Figure 17. Hide the global error message

hide() only runs if the error message is visible.

Nothing else happens, except for an alert(). We’ll add the PHP in the next lesson.

The entire page

Here’s all the code together. Run through it again in your mind, so you see how it all fits together.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>Order Form</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style type="text/css">
      body {
        font-family: Verdana, sans-serif;
        font-size: 14px;
        background-color: #FFFFE0;
      }
      .message_container {
        display: none;
        font-weight: bold;
        color: red;
      }
    </style>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
    <script type="text/javascript">
      $(document).ready(function() {
        $("#global_error_message_container").hide();
        $("#frisbees").focus();
        $("#order_button").click(function() {
          //Set flag showing everything is OK.
          var data_ok = true;

          //Check the frisbees.
          var frisbees = $("#frisbees").val();
          var frisbees_message = check_order_value(frisbees);
          if ( frisbees_message != '' ) {
            data_ok = false;
            show_product_error_message(frisbees_message, 'frisbees');
          }
          else {
            hide_error_message('frisbees');
          }

          //Check the giant chew ropes.
          var giant_chew_ropes = $("#giant_chew_ropes").val();
          var giant_chew_ropes_message = check_order_value(giant_chew_ropes);
          if ( giant_chew_ropes_message != '' ) {
            data_ok = false;
            show_product_error_message(giant_chew_ropes_message, 'giant_chew_ropes');
          }
          else {
            hide_error_message('giant_chew_ropes');
          }

          //Check the squeaky balls.
          var squeaky_balls = $("#squeaky_balls").val();
          var squeaky_balls_message = check_order_value(squeaky_balls);
          if ( squeaky_balls_message != '' ) {
            data_ok = false;
            show_product_error_message(squeaky_balls_message, 'squeaky_balls');
          }
          else {
            hide_error_message('squeaky_balls');
          }

          //Make sure something was ordered.
          if ( frisbees + giant_chew_ropes + squeaky_balls == 0 ) {
            show_global_error_message("Sorry, you must order at least one item.");
            data_ok = false;
          }
          
          if ( data_ok ) {
            hide_global_error_message();
            alert('Everything is OK... so far.')
          }
        });
      });

      //Check whether an order value is valid.
      //Input
      //  value_to_check: the value to check.
      //Return: An error message, or empty string if no error.
      function check_order_value(value_to_check) {
        if ( isNaN(value_to_check) ) {
          return 'Please enter a number';
        }
        if ( value_to_check != Math.round(value_to_check) ){
          return 'Please enter whole numbers only';
        }
        if ( value_to_check < 0 ){
          return 'Please enter positive numbers only';
        }
        return '';
      }

      //Show an error message for a product.
      //Input
      //  message: The message to show.
      //  product_name: The product name the message is about.
      //Return: nothing.
      function show_product_error_message(message, product_name) {
        //Show the error message in the product's message area.
        $('#' + product_name + '_message').text(message);
        if ( $('#' + product_name + '_message_container').is(':hidden') ) {
          $('#' + product_name + '_message_container').show('medium');
        }
        //Show the global error message at the top of the page.
        show_global_error_message("Sorry, I can't process this order.");
      }

      //Show a global error message. It applies to the entire
      //  page, not just one product.
      //Input
      //  message: The message to show.
      //Return: nothing.
      function show_global_error_message(message) {
        $("#global_error_message").text(message);
        if ($('#global_error_message_container' ).is(':hidden') ) {
          $('#global_error_message_container').show('medium');
        }
      }

      //Hide a product error message.
      //Input
      //  product_name: The product name the message is about.
      //Return: nothing.
      function hide_error_message(product_name) {
        $('#' + product_name + '_message_container').hide('medium');
      }

      //Hide global error message.
      //Return: nothing.
      function hide_global_error_message() {
        if ($('#global_error_message_container' ).is(':visible') ) {
          $('#global_error_message_container').hide('medium');
        }
      }
    </script>
  </head>
  <body>
    <h1>Order Form</h1>
    <p>What would you like?</p>
    <p id="global_error_message_container" class="message_container">
      <img src="error.png" alt="Error">
      <span id="global_error_message"/>
    </p>
    <p>
      <input type="text" name="frisbees" id="frisbees" size="3">
      Frisbees ($8.95 each)<br>
      <span id="frisbees_message_container" class="message_container">
        <img src="error.png" alt="Error">
        <span id="frisbees_message"/>
      </span>
    </p>
    <p>
      <input type="text" name="giant_chew_ropes" id="giant_chew_ropes" size="3">
      Giant chew ropes ($12.95 each)<br>
      <span id="giant_chew_ropes_message_container" class="message_container">
        <img src="error.png" alt="Error">
        <span id="giant_chew_ropes_message"/>
      </span>
    </p>
    <p>
      <input type="text" name="squeaky_balls" id="squeaky_balls" size="3">
      Squeaky balls to chase ($1.95 each)<br>
      <span id="squeaky_balls_message_container" class="message_container">
        <img src="error.png" alt="Error">
        <span id="squeaky_balls_message"/>
      </span>
    </p>
    <p>
      <button id="order_button" type="button">Order</button>
    </p>
  </body>
</html>

Figure 18. The entire page

Make sure you try the page. Type in invalid data, and see what happens.

Exercise: Sign-up form

Make a sign-up page for a Web service. It starts out like this:

Empty form

Figure 1. Empty form

All fields must be completed. Suppose the user does this:

Empty field

Figure 2. An empty field

Here is the result:

Error messages

Figure 3. Error messages

Hint: Make a function called check_value() that returns either an error message or an empty string. The only error is an empty field.

You can see my solution, but don’t look at the source code until you do it yourself first!

Upload your solution to your server. Enter the URL below.

(Log in to enter your solution to this exercise.)

Summary

You learned:

  • How to report errors in a professional way, with colors, icons, and animated effects.
  • How page-wide, global error messages help the user.
  • How JavaScript functions make error reporting easier to program.

What now?

Let’s put everything together. We’ll do form validation that:

  • Has professional-looking error messages.
  • Shows client-side and server-side error messages in the same way. There’s no need to bother the user with the difference.

Does not work in IE 7

This solution does not seem to work in IE 7 only displaying the first field on the test forms.


How to...

Lessons

User login

Log in problems? Try here


Dogs