Get Dojo's support for standard Ajax requirements Sunil Patil concludes his introduction to the Dojo toolkit by walking you through an email client application that demonstrates Dojo’s support for common Ajax requirements. See for yourself how Dojo’s unified API takes the hard work out of implementing cross-browser compatibility, error handling, and data encoding in your Ajax applications. Level: Intermediate The first half of this article got you up to speed with the basics of working with the Dojo toolkit. You learned how to install Dojo in your Web applications, and you developed a simple Hello World program. In the process you learned first-hand how the toolkit’s infrastructure for classes and modules simplifies the writing of object-oriented, well-scoped JavaScript. In this second half of the article we’ll enter more advanced territory, with a look at the real-world requirements of Ajax applications. You’ll then learn about the infrastructure code Dojo provides to help you handle these requirements, even in complex application scenarios. Ajax is a foundational technology for Web 2.0 applications. Ajax applications typically have three requirements: A (usually asynchronous) request to the server that gets a response without requiring the browser page to be refreshedA modification to the page document — such as a color change or the addition or removal of some content — that indicates visually to the user that the response has occurredEventing, which allows you to attach a JavaScript function to a user- or system-initiated event — a button click by the user, for example — so that the function is called when the event occurs Dojo simplifies the development of all three of these Ajax requirements and ensures that the resulting application will work in multiple browsers. It does this by providing an infrastructure for making asynchronous requests, and for using cross-browser-compatible DOM-manipulation code. In the next sections I’ll introduce you to the key elements of Dojo’s Ajax infrastructure and show you how to use it in Ajax development. Making an XMLHttpRequest Most browsers provide a XMLHttpRequest object that allows you to make an asynchronous request to the server and get a response without refreshing the whole page. But if you try to use the XMLHttpRequest object directly without using a JavaScript framework such as Dojo, you’ll run into problems such as memory leaks and divergent, browser-specific APIs. The Dojo toolkit simplifies the use of XMLHttpRequest by providing a set of wrapper functions for the object. The wrapper functions address common infrastructural issues by providing a unified API, cross-browser compatibility, error handling, data encoding, and much more. The four wrapper functions all start with xhr (a short form of XMLHttpRequest): xhrGet() allows you to make an HTTP GET method call to the server.xhrPost() allows you to make an HTTP POST method call to the server.xhrPut() allows you to make an HTTP PUT method call to the server.xhrDelete() allows you to make an HTTP DELETE method call to the server. Dojo also provides a generic xhr() function that takes the name of the HTTP method that you want to use as an argument and makes the requested method call. Dojo defines properties that you can pass to the XHR methods. Most have a default value. To override the defaults, you can assign the values you want to a set of properties and pass that set as an argument to the DHR method you’re using. The properties Dojo defines are: url: This is a required property that has no default value. It indicates the URL that you want to make the request to and get a response from. It must be same host and port that serves the page, a restriction based on a browsers’ same origin policy.handleAs: This optional property indicates how you want Dojo to handle the server response. The available options are: text (the default): Return the response data as a string. It’s your responsibility to decode and use that data.xml: Returns the response data as a document object. If the client’s browser is other than Internet Explorer (IE), Dojo returns xhr.responseXML. If the client browser is IE, then it creates an XMLDOM object, loads the server response in that object, and returns it.json: Parse the response string, which is in the JavaScript Object Notation (JSON) format into a JavaScript object using the dojo.fromJson() call; throw an error if the response string is invalid JSON.javascript: Evaluate the response string as JavaScript in global scope using the dojo.eval() call.json-comment-optional: Check if the response string has JavaScript comments in it. If it does, throw an error; if not, parse that string as a JSON object using the dojo.fromJson() call. (This option was meant to deprecate warnings about the poor security of client-side JSON parsing and XML.)timeout: This optional property defines the time in milliseconds to wait before giving up the XHR call and throwing an error. The default value is 1000.sync: This is an optional property, with a default value of false, that determines if the XHR call to the server should be synchronous or asynchronous. A value of true causes Dojo to make a synchronous request to the sever.form: This optional property defines what form should be submitted with the request. Its value can be either a DOM node representing the form, or a string representing the value of the form’s id attribute. Dojo calls the dojo.formToObject() method with the supplied form id, and the dojo.formToObject() returns the values encoded in an HTML form as string properties in an object. Disabled form elements, buttons, and other non-value form elements are skipped. Multi-select elements are returned as an array of string values.content: If this optional property is set, it should contain a set of properties with string values. Dojo passes those properties in a request along with the data collected from the form. In addition, you can set the following three properties. Each one’s value acts as callback function that’s invoked once the response is returned from the server: load: This callback function is called if the Ajax request completes successfully. Dojo passes it the data returned by the response, along with the XHR properties that you passed to the dojo.xhr at the time of invoking it.error: This callback function is called if an error occurs in the Ajax request — either because the Ajax call timed out or there was some error on the server side or the URL was invalid. The basic idea is that if the HTTP response code is unsuccessful, the error function is called. The first parameter passed to the error function is a JavaScript Error object indicating what the failure was; the second argument is the set of XHR properties that you passed to dojo.xhr() at the time of invoking it..handle: If you don’t want two different functions to handle both successful and unsuccessful Ajax requests, then you can set a callback function that’s invoked under both conditions. It gets response data and an XHR properties object as arguments in the case of a successful Ajax request, and error and an XHR properties object as arguments if an error occurs. Choose this option with care, because you might end up writing your own infrastructure code to figure out if the request succeeds or fails and to handle it accordingly.The sample application Now that you know how the XHR methods operate, we’ll develop a sample email client application with Ajax features. The first page request will load a template page, with header, footer, side menu, and body. Thereafter every user interaction will change only the body of the page without refreshing the page. You’ll build the client side of the sample application using the Dojo toolkit. The server side is a Java EE application that uses Apache Struts 1.0. To keep things simple, we’ll use a static list of email messages in the user’s inbox, and every sent message will just print to the console. Download the sample code, which has server-side logic that you can use as-is. GET request The first thing that we want to do in our sample application is display email messages from the user’s inbox. Follow these steps: Create the index.jsp page, shown in Listing 1, which will act as a template page for the Web application. Listing 1. index.jsp<html> <head> <title>My Company Email Client</title> <script type="text/javascript" src="js/dojo/dojo.js" djConfig="isDebug: true"></script> <script type="text/javascript"> dojo.registerModulePath("custom","../../custom"); dojo.require("custom.javaworld.Email"); dojo.addOnLoad(custom.javaworld.Email.displayEmails); </script> </head> <body> <table class="legacyTable" border="1" bordercolor="black" width="100%" height="100%"> <tr height="10%"> <td colspan="2">Header</td> </tr> <tr > <td width="20%" id="sidemenu"> <table class="legacyTable"> <tr><td><a href="javascript:email.displayEmails();">Refresh</a></td></tr> </td> <td width="80%" id="body" align="top" valign="top"> Getting emails </td> </tr> <tr height="5%"> <td colspan="2">Footer</td> </tr> </table> </body> </html> The index.jsp page includes one big table that displays a header, footer, side menu, and body. These four parts all display static content. The index.jsp page also installs the Dojo toolkit by adding the dojo.js script element, and it includes simple JavaScript that loads the custom.javaworld.Email class on the page. The call to dojo.addOnLoad() ensures that the custom.javaworld.Email.displayEmails function is called as soon as the page is loaded. We need to create a custom JavaScript module that has all the JavaScript logic for the client application. Create the WebContent/custom/javaworld folder and then create an Email.js file in the folder. Add this code to the Email.js file: dojo.provide("custom.javaworld.Email"); custom.javaworld.Email ={ displayEmails: function(){ dojo.xhrGet({ url: "displayEmails.do", load: function(data, ioargs){ dojo.byId("body").innerHTML = data; }, error: function(error,ioargs){ alert(error); } }); } } When you develop a custom JavaScript module, it’s your responsibility as the developer to make sure that all the code in that module is contained in its own namespace. You can do that in two ways. You can declare a JavaScript class using dojo.declare() , similar to the Worker.js file that we developed in Part 1 . Or you can define a custom namespace and add member functions and variables as properties to it. For this sample application, custom.javaworld.Email={} defines the custom.javaworld.Email namespace, which has one member function — displayEmails() . The displayEmail() function makes one call to dojo.xhrGet() , passing the following properties to it: url: This property’s value is displayEmail.do, because we want to make the HTTP GET request to displayEmail.do.load: This property’s value is an anonymous function that first makes a dojo.byId("body") call to find a DOM node with id =="body". This is the Dojo equivalent of the browser DOM’s document.getElementById() method. Once we have body element, we assign the HTML markup returned by the displayEmail.do as the value of its innerHTML attribute.error: This property’s value is an anonymous function that shows an alert message with error information if the Ajax request is not completed successfully. Now we need to generate the markup for the user’s inbox, using the DisplayEmailAction class. The displayEmail.do URL forwards control to DisplayEmailAction, which in turn forwards a static list of the user’s email messages to displayEmails.jsp. Create displayEmails.jsp, shown in Listing 2. Listing 2. displayEmails.jsp<%@page import="java.util.ArrayList"%> <%@page import="com.javaworld.email.EmailForm"%> <link rel='stylesheet' href='css/style.css'> <%@page import="java.text.SimpleDateFormat"%> <table class="legacyTable" cellspacing="2" cellpadding="3" border="1" id="inbox"> <tr class="header" > <th>From</th> <th>Subject</th> <th>Date</th> </tr> <% ArrayList emailList =(ArrayList)request.getAttribute("emailList"); for(int i =0 ; i < emailList.size() ;i++){ EmailForm email = (EmailForm)emailList.get(i); SimpleDateFormat sd = new SimpleDateFormat("mm/dd/yyyy"); %> <tr class="row" id="<%=email.getEmailId() %>" > <td width="15%"><%=email.getEmailTo() %></td> <td width="75%"><%=email.getEmailSubject() %></td> <td width="10%"><%=sd.format(email.getEmailDate()) %></td> </tr> <% } %> </table> The displayEmail.jsp page reads the emailList request attribute, which contains a list of the user’s email messages, and then iterates through that list to display all the messages in tabular format. If you haven’t already downloaded the sample code, do so now and expand the ZIP file in a convenient location. Create the same directory structure in your EmailClient application that the sample package uses. Copy DisplayEmailsAction.java, DummyEmailDao.java, EmailDao.java, web.xml, and struts-config.xml from the sample-code package into the corresponding locations in your EmailClient application. The web.xml file defines index.jsp as a welcome page for the application. Deploy your code on the application server of your choice and access the EmailClient Web application. If you’re using Apache Tomcat, you can access the application at http://localhost:8080/EmailClient. On the first page you should be able to see all the email messages from the user’s inbox, as shown in Figure 1. Figure 1. Page displaying the user’s messages. (Click to enlarge.)POST request The ability to post HTTP forms is another common requirement for Ajax applications. Now we’ll change the EmailClient application so that it allows the user to send an email message via a posting form, using xhrPost. Follow these steps: Create an HTML form, shown in Listing 3, that users will use for composing email messages. The sendEmail.jsp file will be used to generate markup of that form. Listing 3. HTML form for composing messages<form id="emailForm"> <table class="legacyTable"> <tr> <td width="10%">To</td> <td><input name="emailTo" type="text" value="test@javaworld.com" /></td> </tr> <tr> <td width="10%">Subject</td> <td><input name="emailSubject" type="text" value="This is test subject" /></td> </tr> <tr> <td colspan="2"><textarea name="emailBody" cols="100" rows="10" >Sample mail body</textarea></td> </tr> <tr> <td ><input type="submit" value="Send" onclick="return custom.javaworld.Email.sendEmail();" /> </td> <td ><input type="reset" value="Discard" /> </td> </tr> </table> </form> The form has three inputs: emailTo: Recipient of the messageemailSubject: Subject of the messageemailBody: Body of the message The form does not have either action or method attributes, because we will take care of those in Dojo. The form’s id is emailForm . The custom.javaworld.Email.sendEmail() method is called when user submits the sendEmail form. Create a Compose Messages link in the side menu just below the Refresh link. When the user clicks on this link we want to display the sendEmail form in the page’s body section. Assign the custom.javaworld.Email.composeEmails() function as an event handler for the Compose Messages link. Add the following two functions to the Email.js file: composeEmails: function(){ dojo.xhrGet({ url: "sendEmailForm.do", load: function(data, ioargs){ dojo.byId("body").innerHTML = data; }, error: function(error,ioargs){ alert(error); } }); }, sendEmail: function(){ dojo.xhrPost({ url: "sendEmail.do", form: "emailForm", load: function(data, ioargs){ dojo.byId("body").innerHTML = data; }, error: function(error,ioargs){ alert(error); } }); return false; }, Lets take a closer look at both of these functions: composeEmails()is called when user clicks the Compose Messages link in the side menu. In this function we are making an HTTP GET method call to sendEmailForm.do, which in turn forwards control to sendEmail.jsp and returns the markup. Once markup is returned we set it as the body of the page to display the sendEmail form to the user.sendEmail() is called when the user clicks the Send button in the sendEmail form. In this function we are making a xhrPost() call to submit the form. The form property’s value is emailForm, because we want to send the form with id=="emailForm" as part of the POST request. The url property’s value is sendEmail.do, which means submit the form to the sendEmail.do URL.Copy the SendEmailAction.java and EmailForm.java files from the sample code. Build the current version of your code and deploy it on your application server. Once the application is deployed, reload the index page. When the page finishes loading, click the Compose Messages link. When the sendEmail form displays, add some content there and click Send. You should see the values that you entered on the form in your application server console output, and you will be redirected to the inbox. Searching for DOM nodes You’ve learned how to use Dojo’s wrapper on XMLHttpRequest to make a request to a server and get a response without refreshing the page. Once you get a response from the server, you want to give some kind of visual indication to the user. Before you update the document, you must locate the HTML element that you want to modify. You can use the browser’s DOM API for that purpose, but then you’d encounter the problem that different browsers support different DOM APIs (and that the browser APIs can be slow). REST methods The REST architectural style is generating a lot of interest lately. REST architecture suggests using HTTP GET, POST, PUT, and DELETE methods. So far in this article I’ve covered how to use xhrGet and xhrPost for sending HTTP GET and POST methods. Dojo also provides xhrPut()and xhrDelete(), which you can use to make HTTP PUT and DELETE method calls, respectively. Their syntax is very similar to that of xhrGet() and xhrPost(). The Dojo toolkit provides two functions — dojo.byId() and dojo.query() — to simplify locating elements in the document DOM. Let’s take a closer look at each of them. dojo.byId() The dojo.byId() function, a short form of the browser DOM’s document.getElementById() method, works in multiple browsers. In the sample application, we are already using the dojo.byId() function in the displayEmail() function to locate the DOM node with an id equal to body. Once the DOM node is found, we use this code to set the value of the innerHTML attribute equal to the response returned by the server: dojo.byId("body").innerHTML = data; The dojo.byId() function takes two arguments and returns the DOM node instance: The first argument can be either a string or a node. If it’s a string, then dojo.byId() looks for an HTML element with an id value equal to the value of the first argument and returns the native DOM node for that element. If the first argument is a DOM node, it returns that node.A second argument, which is optional, is an instance of a DOM document. By default the byId() function searches for an element in the current document. If you want to search an element in some other document — the document loaded in the HTML <IFrame> element, for example — then you can pass document instance for that <IFrame>.dojo.query() The dojo.byId() method is helpful if you want to search elements by their id, but you might have an advanced requirement such as “find all the <table class="legacyTable"> elements in the document or “find all the DOM nodes for which value of css style class is head.” The dojo.query() function can be used to solve these types of requirements. It lets you search for DOM nodes based on CSS3 selectors. The dojo.query() method takes two arguments: query is a string, which should be a CSS 3 expression that defines the elements that you want to search for.root is an optional argument that defines the scope of the search. For example, you might want to search for all the <tr> elements under a particular table element instead of the full document. In that case you’d set either the value of the id attribute or DOM node of the table element as the second argument, and the dojo.query() will search only in the childrens/grandchildren of the table element instead of the full document. The dojo.query() method returns the dojo.NodeList object, which can be traversed with regular JavaScript array techniques. Like any array, it has a length property, and you can loop through it with a for loop. Note that even if dojo.query() finds only one result, it returns a NodeList object containing that node. Common selectors The CSS3 specification provides powerful and flexible selectors for selecting nodes. Coverage of all the selectors is beyond this article’s scope, but I’ll discuss some of the common ones. See the CSS3 selector reference guide for detailed information on all the available selectors. Let’s add the decorateInbox() function to Email.js. Change both the displayEmails() and sendEmail() functions so that they call the decorateInbox() method from there load() callback function after setting the string returned in response as value of the <body> element’s innerHTML attribute: decorateInbox: function(){ //Query by tag. All the table elements in the document console.log(dojo.query("table")); //Query by class. All the elements with class==header console.log(dojo.query(".header")); //Query by id. All the elements with id==inbox console.log(dojo.query("#inbox")); //All the tr elements with value of class equal to header console.log(dojo.query("table.inbox")); //First find the inbox element and then return all the tr elements which are children of tbody var rowList =dojo.query("tbody tr","inbox"); The CSS3 selectors can be broadly classified into three types: Query by tag: You can query DOM nodes based on the HTML tag name. For example, dojo.query("table") returns all the table elements in the current document.Query by class: You can query DOM nodes based on their style class. For example, dojo.query(".header") returns all the DOM nodes for which the style class is header.Query by id: You can search DOM nodes based on id. For example, dojo.query("#inbox") returns a node with id="inbox". You can mix these three basic types to create powerful selectors such as dojo.query("table.inbox"), which returns all the <table class="legacyTable"> elements for which class="inbox". Or you can use dojo.query("td#sidemenu") to find out all the <td> elements with id="sidemenu". You can use dojo.query("table:first-child") to get a list of the first children of all table nodes. Common functions The dojo.query() method returns an instance of dojo.NodeList. The dojo.NodeList object has a length property that you can use to iterate over all its elements using the for loop: var rowList = dojo.query(".row"); for(var i = 0; i < rowList.length; i++ ){ rowList[i].style.backgroundColor='lightblue'; } This code first queries all the elements with class="row" and then iterates over the list returned by dojo.query() to set the backgroundColor to lightblue. That’s a lot of code to perform a simple operation. The dojo.NodeList provides the following helper methods to simplify your code: style(): The style() function allows you to get or set the CSS property for every element in the NodeList. To set the backgroundColor of all rows to lightblue, do this: dojo.query(".row").style('backgroundColor','lightblue'); The style() function takes two arguments. The first is the name of the CSS property that you want to either get or set. The second argument is optional. If you don’t set it, a style() call acts like get('property') . That is, dojo.query(".row").style('backgroundColor') returns a value of backgroundColor property for every row. If you set this argument, it acts as set call and sets the value of backgroundColor to lightblue for every node in the list. addClass(): You can use this function to add a particular style class to every node in the NodeList. It takes only one argument: the name of the class that you want to apply. forEach(): This method allows you to attach a certain function to each element of an array. You can use the forEach() function on the dojo.NodeList object that is returned by the dojo.query() function. For example, you can disable all the anchor elements on EmailClient‘s index page by adding this logic to the page: dojo.query("a").forEach(function(item,index,array){ item.removeAttribute('href'); item.style.color='grey'; }); The forEach() function takes only one argument, which is a callback function that’s called for each item in the array. The forEach() function passes three parameters to the callback function. The first is the item, which in our sample code is the anchor DOM node. The second argument is the index of the current element in the array, starting with 0. The third argument is the array itself. You can use the third argument to access either the element before or the element after the current element in the array. Our EmailClient page has two anchors, so the callback function gets called twice. Inside the callback function we are disabling the anchor by removing the anchor element’s href attribute and applying grey color to it. (Setting item.disabled to true works only in the IE browser.) filter(): This method allows you to filter the result set returned by the dojo.query() method. For example, you can add this code to the decorateInbox() method to set the color of every alternate row in the inbox to lightblue: dojo.query(".row","inbox").filter( function(thisRow) { return (nodeCount++) % 2 == 0; } ).style("backgroundColor",'lightBlue'); The filter() method takes one argument, which can be either a string or callback function. If it’s a string, it should be a CSS selector expression that will be used to filter nodes from the query node list. If it’s a function, that function is called once for every element in the NodeList . And if it’s a callback function, it can return either true or false to indicate whether that element should be kept in the list. Eventing One of the keys to creating dynamic Web pages is the use of event handlers. They allow you to execute specific JavaScript functions in response to a user-initiated action (such as a button click) a or system-initiated action. The browser can also invoke an event such as the completion of a document or image load. The browser’s DOM API provides methods for capturing events so that you can perform your own actions in response to them. Even though you can develop applications using DOM APIs, they are not easy to use and the resulting application might not work in all browsers. The Dojo eventing system is superior to using the browser’s DOM API because: It takes care of the cross-browser compatibility issues.It allows you to attach event handlers not only to DOM events such as button or link clicks, but also to JavaScript functions. For example, you can attach an event handler to the sendEmail() function that causes your callback function to be called every time the sendEmail() function is invoked.It allows you to attach multiple event handlers to a single event. You can attach two JavaScript functions to single button click, for example.When an event is invoked, the Dojo toolkit passes a normalized event object to your callback function, which simplifies handling event. Dojo provides dojo.connect() and dojo.disconnect() functions that should be able to handle most of your eventing requirements. Lets take a closer look at each one of them. dojo.connect() The dojo.connect() method lets you connect a function to either a DOM event or a JavaScript function. Once a function is connected to an event, that function is called every time that event occurs.You can attach more than one function to the same event by making multiple calls to dojo.connect(). The dojo.connect() method takes following five arguments. obj: The source object for the event function. If you want to listen for the DOM event, this should be the DOM node. If you want to connect to another function, this argument should be object that function is defined in. This argument can be null.event: The name of the event function. For a DOM event this could be something like onclick. For a function event this would be name of the function that you want to listen for.context: The object that this method will receive as this.method: Name or function reference of the JavaScript function that will be called to handle the event.dontFix: If obj is a DOM node, set dontFix to true to prevent delegation of this connection to the DOM event manager. One good thing about dojo.connect() is that you don’t need to specify all five arguments every time. If you skip an argument, it is considered to be null. Connecting to a DOM event To demonstrate how you can attach an event handler to a DOM event, let’s change our EmailClient application so that an event-handler function is called when the user clicks on any message in the inbox. Add this code to the decorateInbox() method: dojo.query(".row","inbox").forEach( function(thisRow) { dojo.connect(thisRow,'onclick',function(){ console.log("Got control in custom event handler"); }) } ) This code uses the dojo.query() method to find all the rows in the inbox. Once we have the list of rows, we iterate through them using forEach(). Inside the forEach() callback function we get the DOM node for the row and attach an anonymous function as a onclick event handler; it writes the message to the console when the user clicks on the row. Now try deploying this code on the server. When you click on any row you should see a message on the console that reads “Got control in custom event handler”. Connecting to an object event In addition to listening for DOM events, you might want to listen for some custom JavaScript function invocation to figure out when it gets called. Let’s say I want to be notified when the xhrPost method is called from any place in my application. I can do that by adding this code to index.jsp: dojo.connect(dojo,"xhrPost",null,function(args){ console.log("The dojo.xhrPost() method was called"); console.dir(args); }); This code attaches an anonymous function to the dojo.xhrPost() method. This function is called every time the dojo.xhrPost() method is called and gets the same arguments that dojo.xhrPost() is invoked with. (We are simply writing the arguments to the console.) Attaching your custom event handlers to Dojo’s function allows you to extend the Dojo toolkit’s functionality without touching the framework’s .js files. dojo.disconnect() The dojo.disconnect() function allows you to remove the link created by the dojo.connect() function. For example, you can remove the function attached to the xhrPost() method by calling the dojo.disconnect() method: var handle = dojo.connect(dojo,"xhrPost",null,function(args){ console.log("The dojo.xhrPost() method was called"); console.dir(args); }); dojo.disconnect(handle); The dojo.disconnect() method takes only one argument, which is the value returned by dojo.connect() at the time of creating the connection. The event object When you connect a function to DOM event with dojo.connect(), Dojo passes your event-handler function a normalized event object. This means that, regardless of the client’s browser type, you can count on set of standard attributes for the event and set of methods for manipulating the event. The event object has following properties: event.target: The element that generated the eventevent.currentTarget: The current targetevent.layerX: The x coordinate, relative to the event.currentTargetevent.layerY: The y coordinate, relative to the event.currentTargetevent.pageX: The x coordinate, relative to the view portevent.pageY: The y coordinate, relative to the view portevent.relatedTarget: For onmouseover and onmouseout, the object that the mouse pointer is moving to or out ofevent.charCode: For key-press events, the character code of the key pressedevent.keyCode: For key-press events, handles special keys such as ENTER and spacebarevent.charOrCode: A normalized version of charCode and keyCode, which can be used for direct comparison for alpha keys and special keys together The event object also provides two methods: event.preventDefault prevents an event’s default behavior (for example, prevents a link from loading a new page).event.stopPropagation prevents an event from triggering a parent node’s event. Let’s change the EmailClient sample application so that when you click on an email message in the inbox, it opens the selected message. Follow these steps: Copy the OpenEmailAction.java class from the sample code. This class reads the value of the emailId request parameter and queries details for that message. Then it forwards control to sendEmail.jsp for displaying the message. Change the decorateInbox function so that the event handler for the row looks like this: dojo.query(".row","inbox").forEach( function(thisRow) { dojo.connect(thisRow,'onclick',function(event){ var emailId = dojo.attr(event.target.parentNode,"id"); dojo.xhrGet({ url: "openEmail.do?emailId=" +emailId , load: function(data, ioargs){ dojo.byId("body").innerHTML = data; }, error: function(error,ioargs){ alert(error); } }); }); } ) We are attaching the anonymous function that takes an event object to the row’s onclick event. You can select the message by clicking the row’s From, Subject, or Date element. As a result, the event object that we get inside the handler will have a target property pointing to the <td> element that was clicked. But we want the id, which is set at the level of the <tr> element. We can access it by accessing the target’s parent. Now the problem is that different browsers have different APIs that you must use for accessing these attributes. To solve this issue the Dojo toolkit provides the dojo.attr() method, which can be used to read attributes of the DOM node. We are using it to read the id attribute, and then send an HTTP GET request to OpenEmailAction, which returns markup displaying the message’s content. Once you have the markup, assign it to the body of the document to display it to the user. In conclusion In this two-part article, you’ve learned how to write object-oriented, modular JavaScript code using the core component of the Dojo toolkit. You’ve also learned how Dojo’s infrastructure simplifies the development of Ajax applications. Dojo offers a lot more functionality than a single two-part article could cover. If you have requirements beyond the scope of what you’ve learned here, see the Resources section below for help. The Dojo API is an ideal starting point. Sunil Patil is a Java Enterprise/Portlet developer working for Ascendant Technology in San Francisco, California. He is the author of Java Portlets 101 (SourceBeat, April 2007) and has written numerous articles published by O’Reilly Media, IBM developerworks, and JavaWorld. Sunil was a member of IBM’s WebSphere Portal Server development team for three years and is actively involved in the Pluto community. In addition to being an IBM Certified WebSphere Portal Server Application Developer for both v5.0 and v5.1, he is a Sun Microsystems Certified Java Programmer, a Web component developer, and a business component developer. You can read Sunil’s blog at http://wpcertification.blogspot.com. Open SourceSoftware DevelopmentWeb DevelopmentApp TestingJavaJavaScriptDevelopment Approaches