Chapter 18. Using Early DOM Event Handlers

The HTML event handler attributes certainly work, and every browser continues to support them despite the fact that they were invented in very early versions of Netscape. However, as you may have surmised, handling events with HTML attributes can become a tedious endeavor mainly because you have to locate and add attributes to every element you want the user to interact with. Keep in mind that all those years ago, the majority of websites were primarily static, and the developers responsible for generating those websites had complete control over each page's HTML. It was also a time before the idea of separating markup (HTML) from style (CSS) and behavior (JavaScript) really gained traction.

The fourth generation of browsers, primarily Internet Explorer 4 and Netscape 4, introduced a new way of assigning event handlers by using what are now referred to as DOM Level 0 event handlers. Think of a "level" as a version; these event handlers are called DOM Level 0 event handlers because they existed before any W3C DOM standard. Yet all browsers support them. Even to this day, DOM Level 0 event handlers are widely used and are an integral part of many web applications.

Early DOM event handlers are so popular because they are supported by all browsers (something you'll appreciate in coming lessons), and you can use them in JavaScript code, effectively decoupling JavaScript from HTML.

ASSIGNING EVENT HANDLERS

DOM Level 0 event handlers are properties of DOM objects as well as the window object. So in order to assign an event handler, you must first retrieve an object from the DOM (or the window). The event handler property names closely resemble those of HTML attribute event handlers: they are lowercase and consist of on followed by the event's name.

For example, you handle the window object's load event by creating a function and assigning it to the onload property, like this:

function handleOnload() {
    alert("Loaded!");
}

onload = handleOnload;

Notice the lack of parentheses to handleOnload in the last line of this code. When assigning an event handler, it is important that you assign either a function or null (null tells the browser not to handle the event). Failing to assign one of these two values to an event handler property results in an error.

In the case of this code, executing handleOnload() when assigning it to the onload event handler does not actually assign the function — it assigns the result of the function's being executed. The result of a function is determined by a return statement within the function. Since handleOnload() does not contain a return statement, the result of the function's being executed is undefined. In the following line, undefined is assigned to onload:

onload = handleOnload(); // error; undefined assigned to onload

The value of undefined is not a function, nor is it null, so this code results in an error. It is possible, however, to write a function that returns another function. Let's modify handleOnload() to do just that with the following code:

function handleOnload() {
    return function() {
        alert("Loaded!");
    }
}

Since handleOnload() now returns a function, and that function is the desired function to handle the event, you can execute handleOnload() as you assign it to an event handler, as seen in the first line of this code:

onload = handleOnload(); // now it's ok; it returns a function
onload = handleOnload; // does not cause an error, but does not alert a message

The second line of this code is technically correct, but it does not result in the desired outcome of an alert window displaying the message "Loaded!" when the event fires. This is because the function handleOnload(), not the function handleOnload() returns, is assigned as the event handler. Always be mindful of how you assign your event handlers; assign the function you want to have execute when the event fires.

Despite the examples in this lesson, you will more than likely see event handlers assigned in slightly a different way — by having an anonymous function assigned to them, like this:

onload = function() {
    alert("Loaded!");
};

The advantage to this format is readability. The event handler and the function assigned to it are grouped together in the same statement — making it much easier to read and understand what the code does. In the grand scheme of things, it doesn't really matter how you assign a function to an event handler — as long as you do it correctly, of course.

Unlike the event handlers in Lesson 17, you cannot pass a value to DOM Level 0 event handlers. So you can't pass this as an argument as you did with the HTML attribute event handlers. You can, however, use this inside the event handler to refer to the element or object where the event occurred. For example:

var someElement = document.getElementById("divElement");

someElement.onclick = function() {
    this.style.color = "red";
};

This code gets an element with an id of divElement, and it assigns a function to handle the click event. When a user clicks the element, the color of the text it contains changes to red. Although you lose the ability to pass data to DOM Level 0 event handlers, you can still access the object where the event fired by using this inside the function.

Note

The browser actually passes an event object to DOM Level 0 event handlers. You learn about this object in Lessons 21 and 22.

TRY IT

In this lesson, you learn how to use early DOM event handlers.

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 free Visual Web Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/). Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for 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 Lesson18 in the JS24Hour folder you created in Lesson 1. Store the files you create in this lesson in the Lesson18 folder.

Step-by-Step

You will rewrite the calculator application from Lesson 17. Instead of using HTML attribute event handlers, you will use the DOM Level 0 event handlers to decouple the HTML from the JavaScript.

  1. Open your text editor and type the following HTML:

    <html>
    <head>
        <title>Lesson 18: Example 01</title>
        <style type="text/css">
        td {
            border: 1px solid gray;
            width: 50px;
        }
    
        #results {
            height: 20px;
        }
        </style>
    </head>
    <body>
        <table border="0" cellpadding="2" cellspacing="2">
            <tr>
                <td colspan="4" id="results"></td>
            </tr>
            <tr>
                <td><a href="#">1</a></td>
                <td><a href="#">2</a></td>
                <td><a href="#">3</a></td>
                <td><a href="#">+</a></td>
            </tr>
            <tr>
                <td><a href="#">4</a></td>
                <td><a href="#">5</a></td>
                <td><a href="#">6</a></td>
                <td><a href="#">-</a></td>
            </tr>
            <tr>
                <td><a href="#">7</a></td>
                <td><a href="#">8</a></td>
                <td><a href="#">9</a></td>
                <td><a href="#">*</a></td>
            </tr>
            <tr>
                <td><a href="#">Clear</a></td>
                <td><a href="#">0</a></td>
                <td><a href="#">=</a></td>
                <td><a href="#">/</a></td>
            </tr>
        </table>
    <script type="text/javascript">
        function addDigit(digit) {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML += digit;
    
            return false;
        }
    
        function calculate() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = eval(resultField.innerHTML);
    
            return false;
        }
    
        function reset() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = "";
    
            return false;
        }
        </script>
    </body>
    </html>

    Save it as lesson18_example01.htm. This is almost the exact same page as in Lesson 17; the only differences are the content in the <title/> element, that none of the <a/> elements use the onclick attribute, and that the text of the multiplication link was changed to an asterisk (*) instead of x. The addDigit(), calculate(), and reset() functions remain untouched.

  2. Now handle the load event as shown by the bold code:

    <html>
    <head>
        <title>Lesson 18: Example 01</title>
        <style type="text/css">
        td {
            border: 1px solid gray;
            width: 50px;
        }
    
        #results {
            height: 20px;
        }
        </style>
    </head>
    <body>
        <table border="0" cellpadding="2" cellspacing="2">
            <tr>
                <td colspan="4" id="results"></td>
    </tr>
            <tr>
                <td><a href="#">1</a></td>
                <td><a href="#">2</a></td>
                <td><a href="#">3</a></td>
                <td><a href="#">+</a></td>
            </tr>
            <tr>
                <td><a href="#">4</a></td>
                <td><a href="#">5</a></td>
                <td><a href="#">6</a></td>
                <td><a href="#">-</a></td>
            </tr>
            <tr>
                <td><a href="#">7</a></td>
                <td><a href="#">8</a></td>
                <td><a href="#">9</a></td>
                <td><a href="#">*</a></td>
            </tr>
            <tr>
                <td><a href="#">Clear</a></td>
                <td><a href="#">0</a></td>
                <td><a href="#">=</a></td>
                <td><a href="#">/</a></td>
            </tr>
        </table>
        <script type="text/javascript">
        function addDigit(digit) {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML += digit;
    
            return false;
        }
    
        function calculate() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = eval(resultField.innerHTML);
    
            return false;
        }
    
        function reset() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = "";
    
            return false;
        }
    
        onload = function() {
            var links = document.getElementsByTagName("a");
            var length = links.length;
    };
        </script>
    </body>
    </html>

    This isn't entirely necessary since the code runs after the HTML is loaded into the browser. However, it does keep the global scope from being polluted with unnecessary variables — the links and length variables in this case.

  3. Now loop through the elements in the links node list:

    <html>
    <head>
        <title>Lesson 18: Example 01</title>
        <style type="text/css">
        td {
            border: 1px solid gray;
            width: 50px;
        }
    
        #results {
            height: 20px;
        }
        </style>
    </head>
    <body>
        <table border="0" cellpadding="2" cellspacing="2">
            <tr>
                <td colspan="4" id="results"></td>
            </tr>
            <tr>
                <td><a href="#">1</a></td>
                <td><a href="#">2</a></td>
                <td><a href="#">3</a></td>
                <td><a href="#">+</a></td>
            </tr>
            <tr>
                <td><a href="#">4</a></td>
                <td><a href="#">5</a></td>
                <td><a href="#">6</a></td>
                <td><a href="#">-</a></td>
            </tr>
            <tr>
                <td><a href="#">7</a></td>
                <td><a href="#">8</a></td>
                <td><a href="#">9</a></td>
                <td><a href="#">*</a></td>
            </tr>
            <tr>
                <td><a href="#">Clear</a></td>
                <td><a href="#">0</a></td>
                <td><a href="#">=</a></td>
                <td><a href="#">/</a></td>
            </tr>
    </table>
        <script type="text/javascript">
        function addDigit(digit) {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML += digit;
    
            return false;
        }
    
        function calculate() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = eval(resultField.innerHTML);
    
            return false;
        }
    
        function reset() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = "";
    
            return false;
        }
    
        onload = function() {
            var links = document.getElementsByTagName("a");
            var length = links.length;
    
            for (var i = 0; i < length; i++) {
                var link = links[i];
                var innerHTML = link.innerHTML;
    
            }
        };
        </script>
    </body>
    </html>

    This is a normal for loop, looping through every <a/> element in the links node list. The first statement creates two variables, link and innerHTML. The link variable contains the <a/> object at the specified index, and the innerHTML variable contains the <a/> element's HTML.

  4. Use a switch element to assign the appropriate functionality for each <a/> element object. Do so based on the element's innerHTML:

    <html>
    <head>
        <title>Lesson 18: Example 01</title>
        <style type="text/css">
        td {
            border: 1px solid gray;
    width: 50px;
        }
    
        #results {
            height: 20px;
        }
        </style>
    </head>
    <body>
        <table border="0" cellpadding="2" cellspacing="2">
            <tr>
                <td colspan="4" id="results"></td>
            </tr>
            <tr>
                <td><a href="#">1</a></td>
                <td><a href="#">2</a></td>
                <td><a href="#">3</a></td>
                <td><a href="#">+</a></td>
            </tr>
            <tr>
                <td><a href="#">4</a></td>
                <td><a href="#">5</a></td>
                <td><a href="#">6</a></td>
                <td><a href="#">-</a></td>
            </tr>
            <tr>
                <td><a href="#">7</a></td>
                <td><a href="#">8</a></td>
                <td><a href="#">9</a></td>
                <td><a href="#">*</a></td>
            </tr>
            <tr>
                <td><a href="#">Clear</a></td>
                <td><a href="#">0</a></td>
                <td><a href="#">=</a></td>
                <td><a href="#">/</a></td>
            </tr>
        </table>
        <script type="text/javascript">
        function addDigit(digit) {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML += digit;
    
            return false;
        }
    
        function calculate() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = eval(resultField.innerHTML);
    
            return false;
    }
    
        function reset() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = "";
    
            return false;
        }
    
        onload = function() {
            var links = document.getElementsByTagName("a");
            var length = links.length;
    
            for (var i = 0; i < length; i++) {
                var link = links[i];
                var innerHTML = link.innerHTML;
    
                switch (innerHTML) {
                    case "Clear":
                        link.onclick = reset;
                        break;
    
                    case "=":
                        link.onclick = calculate;
                        break;
                }
    
            }
        };
        </script>
    </body>
    </html>

    If the link's innerHTML is "Clear", you know it is the link used to clear the contents of the result field; assign its onclick property the reset() function. Similarly, if the link's innerHTML is an equals sign (=), assign the calculate() function to the link's onclick event handler.

  5. Wire up the events for the other links:

    <html>
    <head>
        <title>Lesson 18: Example 01</title>
        <style type="text/css">
        td {
            border: 1px solid gray;
            width: 50px;
        }
    
        #results {
            height: 20px;
        }
        </style>
    </head>
    <body>
        <table border="0" cellpadding="2" cellspacing="2">
            <tr>
                <td colspan="4" id="results"></td>
            </tr>
            <tr>
                <td><a href="#">1</a></td>
                <td><a href="#">2</a></td>
                <td><a href="#">3</a></td>
                <td><a href="#">+</a></td>
            </tr>
            <tr>
                <td><a href="#">4</a></td>
                <td><a href="#">5</a></td>
                <td><a href="#">6</a></td>
                <td><a href="#">-</a></td>
            </tr>
            <tr>
                <td><a href="#">7</a></td>
                <td><a href="#">8</a></td>
                <td><a href="#">9</a></td>
                <td><a href="#">*</a></td>
            </tr>
            <tr>
                <td><a href="#">Clear</a></td>
                <td><a href="#">0</a></td>
                <td><a href="#">=</a></td>
                <td><a href="#">/</a></td>
            </tr>
        </table>
        <script type="text/javascript">
        function addDigit(digit) {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML += digit;
    
            return false;
        }
    
        function calculate() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = eval(resultField.innerHTML);
    
            return false;
        }
    
        function reset() {
            var resultField = document.getElementById("results");
    
            resultField.innerHTML = "";
    
            return false;
    }
        function getHandlerFunction(innerHTML) {
            return function() {
                addDigit(innerHTML);
    
                return false;
            };
        }
    
        onload = function() {
            var links = document.getElementsByTagName("a");
            var length = links.length;
    
            for (var i = 0; i < length; i++) {
                var link = links[i];
                var innerHTML = link.innerHTML;
    
                switch (innerHTML) {
                    case "Clear":
                        link.onclick = reset;
                        break;
    
                    case "=":
                        link.onclick = calculate;
                        break;
    
                    default:
                        link.onclick = getHandlerFunction(innerHTML);
                }
            }
        };
        </script>
    </body>
    </html>

    Because there are only two links that have special capabilities (clear and calculate), the rest of the links can fall under the default switch.

    To handle the click event of these links, write a function called getHandlerFunction(), as shown in bold in the preceding code. It should accept one argument containing the element's innerHTML, and it should return a function to handle the link's click event.

  6. Load the page in your browser. It will behave exactly as Lesson 17's calculator does.

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

Note

Please select Lesson 18 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.147.74.211