Chapter 37. Handling Errors

The common mistakes covered in Lesson 36 are caused by the developer's breaking some of the syntax rules in JavaScript. As you will undoubtedly find out in your own time, errors in your application can occur even if you follow JavaScript's syntax rules. These are called runtime errors because they occur while the program is running. Runtime errors are caused by a variety of factors:

  • A flaw in your code's logic, such as a failure to validate user input before processing it

  • Conditions beyond your control, such as a network error on the user's computer

  • Murphy's Law (anything that can go wrong will go wrong)

Note

Technically, syntax errors are usually runtime errors as well, because the errors occur while the browser loads and attempts to execute the code.

When writing an application you want to be informed of every error that occurs so that you can fix it. But when you deploy your app, the last thing you want users to see is an error message that means nothing to them. Bugs will occur in our software—it's a given. But we can do our best to prevent them, and handle them when they do occur, to provide the best user experience we can.

PREVENTING ERRORS

There are a number of things you can do if you want error-free web pages. First, ensure that your HTML markup is up to snuff. Close all elements that need closing, and properly nest elements. Many HTML-validator services are available for free on the Internet. A popular one is the W3C's Markup Validation Service at validator.w3.org.

Second, thoroughly check your pages in as many browsers as possible. This is easier for developers who use Windows because they have every major browser readily available; developers using OS X or a flavor of Linux have a limited set of browsers to choose from. However, with virtualization technology becoming ubiquitous in our computer systems, it's getting far easier for OS X and Linux users to test in Internet Explorer (IE) by installing Windows on a virtual machine. Alternatively, you can decide which browsers you want to support for your application and verify that your code works in them.

Note

Keep in mind that IE is the most-used browser at present. As of this writing, 60 percent of all users on the Internet use it. If you're writing a web app for everyone, it needs to work in IE.

Third, validate user input. Users may or may not knowingly input invalid data in your form controls. For example, you may have a textbox that asks for numeric data, and somewhere in your code do some math with it. What would happen if the user typed an alphabetic character in that textbox? The answer is that your code would not work. Either an error would be thrown, or more likely your math operations would return incorrect results—thus breaking your application.

Essentially, thoroughly testing your app, following the best practices for HTML, CSS, and JavaScript, and validating user input will get you on the right track. Let's say you've done all that, and your app works perfectly in your development environment. Unfortunately, things can still go wrong, and issues can crop up that are beyond your control.

Your author wrote a content-management system many years ago that was used by many people to update content on their web pages. While it was in development, Jeremy tested his app and found and fixed bugs. When it was released and in use, some users complained that parts of the application didn't work correctly and that they were receiving errors when adding or editing new content. Come to find out, these users used a particular brand of security software that injected its own JavaScript files into the page, and in doing so caused the application's JavaScript to fail at certain key points.

There was really no way Jeremy could have planned for this, and the only way he could fix the problem was to handle the error using a try ... catch block to catch the thrown errors and provide an alternative means for the users of that particular security software to add or edit their data.

THE TRY...CATCH STATEMENT

The try ... catch statement isn't new to you; you saw them used in Lessons 29 and 32. As a quick refresher, they enable you to write code that can fail gracefully. You try to execute code that may fail, and then you catch any exception that might have been thrown as the result of your code's failing. Exception is a programmer's term for an error that occurs as the result of unforeseen circumstances (as opposed to just error, which usually refers only to something in the code that has been written incorrectly).

To use try ... catch statements, you place the code that could fail inside the try block. Then you put code inside the catch block, which executes only if an exception is thrown by the try code. Look at the following code:

try {
    alert("No code to fail here");
} catch(exception) {
    alert("Ooops. " + exception.message);
}

In this code, an alert() call is placed inside the try block. There is no code in try that can throw an error, but if one were thrown, code execution would drop to the catch block. After the catch keyword is a pair of parentheses with an identifier inside. When an exception is thrown, an object of type Error is created and passed to the catch block. So in this code, exception works like a function's parameter (in fact, you can think of catch like a function). Error objects have a property called message, which contains a description of the thrown exception. Let's add a deliberate error to this code:

try {
    alert("No code to fail here");
    abert("But now there is");
} catch(exception) {
    alert("Ooops. " + exception.message);
}

The bold line in this code adds a call to abert(), which doesn't exist. So when JavaScript attempts to execute the bolded statement, the engine throws an exception, which is caught by the catch block. In IE9 the alert() method displays the message "Ooops. Function expected" because exception.message returns the string "Function expected"—no function called abert() exists.

THROWING EXCEPTIONS

It may seem strange, but there are times when you want to throw your own exception. For example, this can be useful when users input invalid data. To throw an exception you use the throw keyword followed by the object you want to throw. Generally that object is an instance of Error. The following code throws an Error object:

throw new Error("This is my own exception");

But the real power behind throwing your own exceptions is the ability to throw any object you want. So if you are validating a form, and the data in a textbox isn't valid, you could do something like this:

throw {
    element : txtTextBox,
    message : "Data is invalid"
};

This code throws an object defined using object literal notation. It has a property called element that contains an HTML element object, and a message property containing a description of the error. When throwing your own objects, it's a good idea to include a message property because it helps emulate objects of the Error data type.

TRY IT

In this lesson, you learn how to handle exceptions that may arise from unforeseen circumstances by encapsulating code inside a try...catch block. You also learn how to throw your own exceptions.

Lesson Requirements

For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users, Notepad is available by default on your system or you can download Microsoft's Visual Web Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/), both of which are free. Mac OS X users can use TextMate, which comes as part of OS X, or download a trial of Coda (www.panic.com/coda/). Linux users can use the built-in VIM.

You also need a modern web browser. Choose any of the following:

  • Internet Explorer 8+

  • Google Chrome

  • Firefox 3.5+

  • Apple Safari 4+

  • Opera 10+

Create a subfolder called Lesson37 in the JS24Hour folder you created in Lesson 1. Store the files you create in this lesson in the Lesson37 folder. Copy your eventUtility.js file into this folder.

Step-by-Step

You will revisit the form-validation routine you wrote in Lesson 28 and use try ... catch, along with throwing your own exceptions, to make the code easier to read and maintain. If you like, copy lesson28_example01.htm from the Lesson28 directory and paste it into the Lesson37 directory, rename it lesson37_example01.htm, and make it look like the HTML in Step 1. Or feel free to forego copy/paste and start with Step 1.

  1. Open your text editor and type the following:

    <html>
    <head>
        <title>Lesson 37: Example 01</title>
    </head>
    <body>
    <form name="theForm" action="" onsubmit="validateForm(event)">
        <p>
            Name: <input type="text" name="txtName" />
        </p>
        <p>
    Age: <input type="text" name="txtAge" />
        </p>
        <p>
            Email: <input type="text" name="txtEmail" />
        </p>
        <p>
            Password: <input type="password" name="txtPassword1" />
        </p>
        <p>
            Retype Password: <input type="password" name="txtPassword2" />
        </p>
    
        <input name="btnSubmit" type="submit" value="Submit" />
    </form>
    <script type="text/javascript" src="eventutility.js"></script>
    <script type="text/javascript">
    function isEmpty(value) {
        return (value === "");
    }
    
    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    }
    </script>
    </body>
    </html>

    Save this file as lesson37_example01.htm.

  2. Instead of throwing anonymous objects, create a data type called ValidationException. It should accept two arguments: an element object and a message. Add the following bold code:

    <html>
    <head>
        <title>Lesson 37: Example 01</title>
    </head>
    <body>
    <form name="theForm" action="" onsubmit="validateForm(event)">
        <p>
            Name: <input type="text" name="txtName" />
        </p>
        <p>
            Age: <input type="text" name="txtAge" />
        </p>
        <p>
            Email: <input type="text" name="txtEmail" />
    </p>
        <p>
            Password: <input type="password" name="txtPassword1" />
        </p>
        <p>
            Retype Password: <input type="password" name="txtPassword2" />
        </p>
    
        <input name="btnSubmit" type="submit" value="Submit" />
    </form>
    <script type="text/javascript" src="eventutility.js"></script>
    <script type="text/javascript">
    function ValidationException(element, message) {
        this.element = element;
        this.message = message;
    }
    
    function isEmpty(value) {
        return (value === "");
    }
    
    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    }
    </script>
    </body>
    </html>
  3. Now start validating each field. Throw a ValidationException object if a form field does not validate. Start with the txtName textbox. Add the following bolded code to the validateForm() function. (Note that the HTML and other JavaScript is removed for printing.)

    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    
        try {
            if (isEmpty(txtName.value)) {
    throw new ValidationException(txtName, "Please enter your name.");
            }
        }
    }

    This new code checks to see if the txtName textbox is empty and, if so, throws an exception.

  4. Now validate the field for the user's age. Ensure that the data is a number and that age is greater than zero. The new code is bold:

    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    
        try {
            if (isEmpty(txtName.value)) {
                throw new ValidationException(txtName,
                    "Please enter your name.");
            }
    
            if (isNaN(age) || age < 1) {
                throw new ValidationException(txtAge, "Please enter your age.");
            }
        }
    }

    The same basics apply here. If age is not a number or is less than 1, throw an exception.

  5. Validate the txtEmail field. Add the bold code:

    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    
        try {
            if (isEmpty(txtName.value)) {
                throw new ValidationException(txtName,
                   "Please enter your name.");
            }
    
            if (isNaN(age) || !(age > 0)) {
    throw new ValidationException(txtAge, "Please enter your age.");
            }
    
            if (!(txtEmail.value.indexOf("@") > 0)) {
                throw new ValidationException(txtEmail,
                     "Please enter a valid email address.");
            }
    
        }
    }
  6. Now validate the password fields. They should match, and they can't be empty. The new code is bold:

    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    
        try {
            if (isEmpty(txtName.value)) {
                throw new ValidationException(txtName, "Please enter your name.");
            }
    
            if (isNaN(age) || !(age > 0)) {
                throw new ValidationException(txtAge, "Please enter your age.");
            }
    
            if (!(txtEmail.value.indexOf("@") > 0)) {
                throw new ValidationException(txtEmail,
                     "Please enter a valid email address.");
            }
    
            if (isEmpty(txtPassword1.value)) {
                throw new ValidationException(txtPassword1,
                     "Password cannot be blank");
            }
    
            if (txtPassword1.value !== txtPassword2.value) {
                throw new ValidationException(txtPassword1,
                     "Passwords do not match. Please reenter them");
            }
        }
    }
  7. Now add the catch block. Use the alert() method to alert the exception's message, and use the element property to give focus to the element. Also don't forget to prevent the default action of the form's submit event, and enable the button. Add the following bold code:

    function validateForm(event) {
        var theForm = document.theForm;
        var txtName = theForm.txtName;
        var txtAge = theForm.txtAge;
        var txtEmail = theForm.txtEmail;
        var txtPassword1 = theForm.txtPassword1;
        var txtPassword2 = theForm.txtPassword2;
        var button = theForm.btnSubmit;
        var age = parseInt(txtAge.value, 10);
    
        button.disabled = true;
    
        try {
            if (isEmpty(txtName.value)) {
                throw new ValidationException(txtName, "Please enter your name.");
            }
    
            if (isNaN(age) || !(age > 0)) {
                throw new ValidationException(txtAge, "Please enter your age.");
            }
    
            if (!(txtEmail.value.indexOf("@") > 0)) {
                throw new ValidationException(txtEmail,
                    "Please enter a valid email address.");
            }
    
            if (isEmpty(txtPassword1.value)) {
                throw new ValidationException(txtPassword1,
                     "Password cannot be blank");
            }
    
            if (txtPassword1.value !== txtPassword2.value) {
                throw new ValidationException(txtPassword1,
                     "Passwords do not match. Please reenter them");
            }
        } catch(exception) {
            var element = exception.element;
    
            alert(exception.message);
            element.focus();
            element.select();
    
            eventUtility.preventDefault(event);
            button.disabled = false;
        }
    }

In this new code, the exception.element property is assigned to the element variable. Next, an alert box displays the error message to the user, and then the focus() and select() methods are called on the element object. Then, since an error occurred, the eventUtility.preventDefault() method prevents the form from being submitted. Finally, the button is enabled to allow the user to resubmit the form.

Open this file in your browser and test it. You'll find that it works very much as it did in Lesson 28, with less code!

To get the sample code files, download Lesson 37 from the book's website at www.wrox.com.

Note

Please select Lesson 37 on the DVD to view the video that accompanies this lesson.

Step-by-Step
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.5.201