Table of Contents
~~SLIDESHOW~~
JavaScript and HTML Documents: Part 2
Contact Hour 13: To be discussed on Tuesday 26th February, 2013.
Lecturer: Dr Chris P. Jobling.
Concluding our description of the objects, methods and properties that allow scripts to interact with HTML documents.
JavaScript and HTML Documents: Part 2
- This lecture concludes our description of some of the client-side features of JavaScript.
Based on Chapter 5 of Robert W. Sebasta, Programming the World-Wide Web, 3rd Edition, Addison Wesley, 2006 and Chapter 7 of Chris Bates, Web Programming: Building Internet Applications, 3rd Edition, John Wiley, 2006.
Contents of this Session
More on the event model, using events for form validation, and the DOM 2 event model.
Learning Outcomes
At the end of this lecture you should be able to answer these questions:
- What is the disadvantage of assigning event handlers to event properties?
- What are the advantages of assigning event handlers to event properties?
- Why is it good to use JavaScript to check the validity of form inputs before the form data is sent to the server?
- What three things should be done when a form input element is found to have incorrectly formatted data?
- What happens when an event handler for the
onsubmit
event returnsfalse
?
Learning Outcomes (continued)
At the end of this lecture you should be able to answer these questions:
- What event is used to trigger an event handler that checks the validity of input for a text button in a form?
- Explain the three phases of event processing in the DOM 2 event model.
- Give two examples of default actions of events.
- Explain the first two parameters of the
addEventListener
method.
Learning Outcomes (continued)
At the end of this lecture you should be able to answer these questions:
- How is an event handler registered so that it will he called during the capturing phase?
- How can an event handler be unregistered?
- What exactly do the
clientX
andclientY
properties store? - What purpose does the navigator object have?
Previous Example
Used HTML event attributes
Handling Events from Button Elements (more)
- An event handler can be registered by assigning it to a property of the JavaScript object associated with the HTML element. As in:
var dom = document.getElementById("myForm") for (index = 0; i < dom.planeButton.length; index++) { dom.planeButton[index].onclick = planeChoice; }
- This registration must happen after the handler function has been defined and when the HTML form has been loaded.
This works because form elements that have the same name are automatically coerced into a JavaScript array with that name.
To ensure correct assignment of even handlers, the script must either be the last thing that the browser loads or the execution of the registration code must be deferred until the document is ready.
Handling Events from Button Elements (continued)
- If this is done for a radio button group, each element of the array must be assigned
- In this case, the checked property of a radio button object is used to determine whether a button is clicked
Handling Events from Button Elements (continued)
- If the name of the buttons is
planeButton
:
var dom = document.getElementById("myForm"); for (var index = 0; index < dom.planeButton.length; index++) { if (dom.planeButton[index].checked) { plane = dom.planeButton[index].value; break; } }
Example
- This is the same example used in the last lecture. This time we register the event handler by assigning an event property: radio_click2.html - jsFiddle.
- Code - document:
<!DOCTYPE html> <!-- radio_click2.hmtl An example of the use of the click event with radio buttons, registering the event handler by assigning an event property --> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Illustrate messages for radio buttons</title> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-responsive.min.css"> <link rel="stylesheet" href="css/main.css"> </head> <body> <div class="container"> <h1>Illustrate messages for radio buttons</h1> <h2> Cessna single-engine airplane descriptions </h2> <form id="myForm" action="/cgi-bin/echo_params.cgi"> <fieldset> <legend>Types of Plane</legend> <label class="radio"> <input type="radio" name="planeButton" value="152" />Model 152</label> <label class="radio"> <input type="radio" name="planeButton" value="172" />Model 172 (Skyhawk)</label> <label class="radio"> <input type="radio" name="planeButton" value="182" />Model 182 (Skylane)</label> <label class="radio"> <input type="radio" name="planeButton" value="210" />Model 210 (Centurian)</label> </fieldset> <p> <input class="btn" type="reset" value="Cancel"> <input class="btn btn-primary" type="submit"> </p> </form> </div> <!-- /container --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script> <script src="js/vendor/bootstrap.min.js"></script> <script src="js/plugins.js"></script> <!-- Any external JS loaded here --> <script src="radio_click2.js"></script> </body> </html>
Code: JavaScript
var planeChoice = function() { // Put the DOM address of the elements array in a local variable var dom = document.getElementById("myForm"); // Determine which button was pressed for (var index = 0; index < dom.planeButton.length; index++) { if (dom.planeButton[index].checked) { plane = dom.planeButton[index].value; break; } } // Produce an alert message about the chosen airplane switch (plane) { case "152": alert("A small two-place airplane for flight training"); break; case "172": alert("The smaller of two four-place airplanes"); break; case "182": alert("The larger of two four-place airplanes"); break; case "210": alert("A six-place high-performance airplane"); break; default: alert("Error in JavaScript function planeChoice"); break; } }; // Register event handlers var dom = document.getElementById("myForm") for (index = 0; index < dom.planeButton.length; index++) { dom.planeButton[index].onclick = planeChoice; }
- Result:
Handling Events from Button Elements (concluded)
- The disadvantage of specifying handlers by assigning them to event properties is that there is no way to use parameters
- The advantages of specifying handlers by assigning them to event properties are:
- It is good to keep HTML and JavaScript separate
- The handler can be changed during use
Handling Events from Textbox and Password Elements
- The Focus Event
- Can be used to detect illicit changes to a text box by blurring the element every time the element acquires focus
- Example: nochange.html and as a jsFiddle.
- Explanation: The form presents an order for coffee. when the submit button is pressed the coffee price is recomputed. A blur is added to the totals box to prevent the user being able to adjust the total.
- Code HTML:
<!DOCTYPE html> <!-- template HTML file --> <html class="no-js" lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Accessing Radio Buttons</title> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-responsive.min.css"> <link rel="stylesheet" href="css/main.css"> <script src="js/vendor/modernizr-2.6.2-respond-1.1.0.min.js"></script> </head> <body> <div class="container"> <h1>The Focus Event</h1> <h2> Coffee Order Form </h2> <form class="horizontal-form" method="post" action="/cgi-bin/echo_params.cgi"> <!-- A bordered table for item orders --> <table class="table table-striped" border="1" summary="order form for coffee"> <!-- First, the column headings --> <thead> <th>Product Name</th> <th>Price</th> <th>Quantity</th> </thead> <!-- Now, the table data entries --> <tbody> <tr class="control-group"> <td> <label for="french">French Vanilla (500 g.)</label> </td> <td>$3.49</td> <td> <!-- Note use of the pattern attribute to ensure quantity is an integer For browsers that don't support the pattern attribute, you would need to include a JavaScript validation script that does the same thing --> <input class="span1" type="text" id="french" name="french" size="2" pattern="^\d{1,}$" /> </td> </tr> <tr class="control-group"> <td> <label for="hazlenut">Hazlenut Cream (500 g.)</label> </td> <td>$3.95</td> <td> <input class="span1" type="text" id="hazlenut" name="hazlenut" size="2" /> </td> </tr> <tr class="control-group"> <td> <label for="columbian">Columbian (500 g.)</label> </td> <td>$4.59</td> <td> <input class="span1" type="text" id="columbian" name="columbian" size="2" /> </td> </tr> </tbody> </table> <!-- Button for precomputation of the total cost --> <div class="input-append"> <input class="input-small" type="text" id="cost" onfocus="this.blur();" /> <input class="btn btn-info" type="button" value="Total cost"/> </div> <!-- The submit and reset buttons --> <p> <input class="btn btn-primary" type="submit" value="Submit Order" /> <input class="btn" type="reset" value="Clear Order Form" /> </p> </form> </div> <!-- /container --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script> <script src="js/vendor/bootstrap.min.js"></script> <script src="js/plugins.js"></script> <!-- Any external JS loaded here --> <script src="nochange.js"></script> </body> </html>
Code: JavaScript
// The event handler function to compute the cost var computeCost = function() { var french = document.getElementById("french").value; var hazlenut = document.getElementById("hazlenut").value; var columbian = document.getElementById("columbian").value; // Compute the cost document.getElementById("cost").value = totalCost = french * 3.49 + hazlenut * 3.95 + columbian * 4.59; }; document.getElementById("total_cost").onclick = computeCost;
- Result:
Checking Form Input
- A good use of JavaScript, because it finds errors in form input before it is sent to the server for processing
- So, it saves both:
- Server time, and
- Internet time
Checking Form Input: approach
- Things that must be done:
- Detect the error and produce an alert message
- Put the element in focus (the
focus
function) - Select the element (the
select
function)
<note>
- The
focus
function puts the element in focus, which puts the cursor in the element:
document.getElementById("phone").focus();
- The
select
function highlights the text in the element
</note>
Checking Form Input: other issues
- To keep the form active after the event handler is finished, the handler must return
false
- Problems:
- With IE6,
focus
andselect
only work if the handler is registered by assigning it to the elementevent
property - With NS7, select works, but
focus
does not
Checking Form Input — Example
- Comparing passwords
- The form just has two password input boxes to get the passwords, and Reset and Submit buttons
- The event handler is triggered by the Submit button
Comparing Passwords (continued)
- Handler actions:
- If no password has been typed in the first box, focus on that box and return
false
- If the two passwords are not the same, focus and select the first box and return
false
- if they are the same, return
true
- Here is the result: pswd_chk.html
- Code HTML:
<!DOCTYPE html> <!-- pswd_chk.html An example of input password checking, using the onsubmit event --> <html class="no-js" lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Illustrate password checking</title> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-responsive.min.css"> <link rel="stylesheet" href="css/main.css"> <script src="js/vendor/modernizr-2.6.2-respond-1.1.0.min.js"></script> </head> <body> <div class="container"> <h1> Illustrate password checking </h1> <h2> Choose a Password </h2> <form id = "myForm" method="post" action = "/cgi-bin/echo_params.cgi"> <p> <label for="initial">Your password: </label> <input type="password" name="password" id="password" /> <label for="second">Verify password: </label> <input type="password" name="password_confirmation" id="password_confirmation" /> </p> <p> <input class="btn" type = "reset" name = "reset" /> <input class="btn btn-primary" type = "submit" name = "submit" /> </p> </form> </div> <!-- /container --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script> <script src="js/vendor/bootstrap.min.js"></script> <script src="js/plugins.js"></script> <!-- Any external JS loaded here --> <script src="pswd_chk.js"></script> </body> </html>
- Code JavaScript:
// The event handler for password checking var chkPasswords = function () { var password = document.getElementById("password"); var confirmation = document.getElementById("password_confirmation"); if (password.value === "") { alert("You did not enter a password \n" + "Please enter one now"); password.focus(); return false; } if (password.value !== confirmation.value) { alert("The two passwords you enterd are not the same \n" + "Please re-enter both now"); password.focus(); password.select(); return false; } else { return true; } }; // Set submit button onsubmit property to the event handler document.getElementById("password_confirmation").onblur = chkPasswords; document.getElementById("myForm").onsubmit = chkPasswords;
- Result:
Another Example
- Checking the format of a name and phone number
- The event handler will be triggered by the change event of the text boxes for
- If an error is found in either, an alert message is produced and both focus and select are called on the text box element
- Here is the result: validator.html
- Code HTML:
<!DOCTYPE html> <!-- validator.html An example of input validation using the change and submit events --> <html class="no-js" lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Illustrate form input validation</title> <meta name="viewport" content="width=device-width"> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-responsive.min.css"> <link rel="stylesheet" href="css/main.css"> <script src="js/vendor/modernizr-2.6.2-respond-1.1.0.min.js"></script> </head> <body> <div class="container"> <h1>Illustrate form input validation</h1> <h2>Customer Information</h2> <form method="post" action = "/cgi-bin/echo_params.cgi"> <Label for="custName">Name</Label> <div class="control-group"> <input class="span4" type = "text" id = "custName" name="custName" placeholder="Last name, First name, Middle initial"> <span class="help-block">Please enter your name as 'Last name, First name, Middle initial'.</span> </div> <div class="control-group"> <label for="phone">Phone number</label> <input class="span4" type = "text" id = "phone" name="phone" placeholder="123-456-7890"> <span class="help-block">Please enter your telephone number matching pattern '123-456-7890'.</span> </div> <p> <input class="btn" type = "reset" id = "reset" /> <input class="btn btn-primary" type = "submit" id = "submit" /> </p> </form> </div> <!-- /container --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script> <script src="js/vendor/bootstrap.min.js"></script> <script src="js/plugins.js"></script> <!-- Any external JS loaded here --> <script src="validator.js"></script> </body> </html>
- Code JavsScript:
// The event handler function for the name text box var chkName = function() { var myName = document.getElementById("custName"); // Test the format of the input name // Allow the spaces after the commas to be optional // Allow the period after the initial to be optional var pos = myName.value.search(/^[A-Z][a-z]+, ?[A-Z][a-z]+, ?[A-Z]\.?$/); if (pos != 0) { alert("The name you entered (" + myName.value + ") is not in the correct form. \n" + "The correct form is: " + "Last name, First name, Middle initial \n" + "Please go back and fix your name"); myName.focus(); myName.select(); return false; } else return true; }; // The event handler function for the phone number text box var chkPhone = function() { var myPhone = document.getElementById("phone"); // Test the format of the input phone number var pos = myPhone.value.search(/^\d{3}-\d{3}-\d{4}$/); if (pos != 0) { alert("The phone number you entered (" + myPhone.value + ") is not in the correct form. \n" + "The correct form is: ddd-ddd-dddd \n" + "Please go back and fix your phone number"); myPhone.focus(); myPhone.select(); return false; } else return true; }; var validate_form = function() { return (chkName() && chkPhone()); }; // Set form element object properties to their // corresponding event handler functions document.getElementById("custName").onchange = chkName; document.getElementById("phone").onchange = chkPhone; document.getElementById("submit").onclick = validate_form;
- Result:
The DOM 2 Event Model
- Does not include DOM 0 features, but they are still supported by browsers
- DOM 2 is modularized – one module is Events, which has two submodules, HTMLEvents and MouseEvents, whose interfaces are Event (
blur
,change
, etc.) and MouseEvent (click
,mouseup
, etc.)
DOM 2 Event propagation
- The node of the document tree where the event is created is called the target node
- There are three phases
- The capturing phase
- The second phase
- The bubbling phase
The capturing phase
- Events begin at the root and move toward the target node
- Registered and enabled event handlers at nodes along the way are run
The second phase
- The second phase is at the target node
- If there are registered handlers there for the event, they are run
The bubbling phase
- Event goes back to the root; all encountered registered handlers are run
The DOM 2 Event Model (continued)
- Not all events bubble (e.g.,
load
andunload
) - Any handler can stop further event propagation by calling the
stopPropagation
method of theEvent
object - DOM 2 model uses the
Event
object method,preventDefault
, to stop default operations, such as submission of a form, if an error has been detected
Registering event handlers
- Event handler registration is done with the
addEventListener
method - Three parameters:
- Name of the event, as a string literal
- The handler function
- A Boolean value that specifies whether the event is enabled during the capturing phase:
node.addEventListener("change", chkName, false);
<note> The perceptive reader will see many parallels between DOM 2 event handling and the Java event model. </note>
Temporary Event Handlers
- A temporary handler can be created by registering it and then unregistering it with
removeEventListener
Handling an Event
- The
currentTarget
property ofEvent
always references the object on which the handler is being executed
Mouse events
- The
MouseEvent
interface (a sub-interface ofEvent
) has two properties,clientX
andclientY
, that have the x and y coordinates of the mouse cursor, relative to the upper left corner of the browser window
DOM 2 Example
- A revision of validator, using the DOM 2 event model: validator2.html.
- Note that this doesn't quite do the right thing as the submit button can be pressed and invalid data can be sent to the server.
- A solution for this is left as an exercise.
- Code HTML is almost the same as for previous example. Only JavaScript definition of the event registration and the event handlers changes when updating to DOM 2 events.
// validator2.js // An example of input validation using the change and submit // events, using the DOM 2 event model // Note: This document does not work with IE6 var chkName = function(event) { // Get the target node of the event var myName = event.currentTarget; // Test the format of the input name // Allow the spaces after the commas to be optional // Allow the period after the initial to be optional var pos = myName.value.search(/^[A-Z][a-z]+, ?[A-Z][a-z]+, ?[A-Z]\.?$/); if (pos != 0) { alert("The name you entered (" + myName.value + ") is not in the correct form. \n" + "The correct form is: " + "Last name, First name, Middle initial \n" + "Please go back and fix your name"); myName.focus(); myName.select(); } }; // The event handler function for the phone number text box var chkPhone = function(event) { var myPhone = event.currentTarget; // Test the format of the input phone number var pos = myPhone.value.search(/^\d{3}-\d{3}-\d{4}$/); if (pos != 0) { alert("The phone number you entered (" + myPhone.value + ") is not in the correct form. \n" + "The correct form is: ddd-ddd-dddd \n" + "Please go back and fix your phone number"); myPhone.focus(); myPhone.select(); } }; // Set form element object properties to their // corresponding event handler functions // Get the DOM addresses of the elements and register // the event handlers var customerNode = document.getElementById("custName"); var phoneNode = document.getElementById("phone"); customerNode.addEventListener("change", chkName, false); phoneNode.addEventListener("change", chkPhone, false); // Challenge ... how would you check both name and telephone number on submit?
<note> DOM 0 and DOM 2 event handling can be mixed in a document </note>
The Navigator Object
- Indicates which browser is being used
- Two useful properties
- The
appName
property has the browsers name - The
appVersion
property has the version number
- Example: navigate.html
<note>
The properties appName
and appVersion
are not as informative as you'd expect!
- Microsoft has chosen to set the
appVersion
of IE6 and IE7 to 4.0 (?) - Mozilla has chosen to set the
appName
of Firefox 2.x to “Nestcape” and theappVersion
to 5.0 (?)
Moral: don't rely on these to adjust behaviour! </note>
- Code:
<!DOCTYPE html> <!-- navigate.html An example of using the navigator object --> <html lang="en"> <head> <meta charset="utf-8" /> <title> Using navigator </title> <script> // The event handler function to display the browser name // and its version number function navProperties() { alert("The browser is: " + navigator.appName + "\n" + "The version number is: " + navigator.appVersion + "\n"); } </script> </head> <body onload = "navProperties()"></body> </html>
Summary of This Lecture
More on the event model, using events for form validation, and the DOM 2 event model.
Learning Outcomes
At the end of this lecture you should be able to answer these questions:
- What is the disadvantage of assigning event handlers to event properties?
- What are the advantages of assigning event handlers to event properties?
- Why is it good to use JavaScript to check the validity of form inputs before the form data is sent to the server?
- What three things should be done when a form input element is found to have incorrectly formatted data?
- What happens when an event handler for the
onsubmit
event returnsfalse
?
Learning Outcomes (continued)
At the end of this lecture you should be able to answer these questions:
- What event is used to trigger an event handler that checks the validity of input for a text button in a form?
- Explain the three phases of event processing in the DOM 2 event model.
- Give two examples of default actions of events.
- Explain the first two parameters of the
addEventListener
method.
Learning Outcomes (continued)
At the end of this lecture you should be able to answer these questions:
- How is an event handler registered so that it will he called during the capturing phase?
- How can an event handler be unregistered?
- What exactly do the
clientX
andclientY
properties store? - What purpose does the navigator object have?
There are some more review questions available.
Further Exercises 1
- Rewrite the document for the exercises of the previous lecture to use the DOM 2 event model.
- Develop, test, and validate an HTML document that has check boxes for apple (59p each), orange (49p each), and banana (39p each), along with a submit button. Each of the check boxes should have its own
onclick
event handler. These handlers must add the cost of their fruit to the total cost. An event handler for the Submit button must produce an alert window with the message"Your total cost is xxx p,"
where xxx is the total cost of the chosen fruit plus 17.5 percent VAT. This handler must returnfalse
(to avoid actual submission of the form data).
Further Exercises 2
- Develop, test, and validate an HTML document that is similar to that of Further Exercises 1.2. In this case, use text boxes rather than check boxes. These text boxes take a number, which is the purchased number of the particular fruit. The rest of the document should behave exactly like that of Exercise 1.2.
- Add reality checks to the text boxes of the document in Further Exercises 2.1. The checks on the check box inputs should ensure that the input values are numbers in the range 0-99.
- Revise the document of Further Exercises 2.1 to use the DOM 2 event model.