Monday, October 8, 2007

Scriptaculous

Scriptaculous is an MIT-licensed JavaScript library that provides visual effects, drag-and-drop support, and controls, such as sliders, to HTML pages. It is built on top of the Prototype JavaScript library, which provides AJAX support (and a number of other features) to the Ruby on Rail Web application framework. Because it's built on Prototype, scriptaculous has AJAX support, but its main focus is on providing highly interactive visual components that can take an AJAX application to the next level.

Installation
You can download scriptaculous from http://script.aculo.us. After extracting the archive, copy the contents of the src and lib subdirectories into a directory in the document root of your Web server. After doing that, you just need to include the prototype and scriptaculous libraries in your HTML files. The components of scriptaculous will be automatically included as needed as long as they are in the same directory as scriptaculous.js. An example of these includes is shown here:

<script src="/scriptaculous/prototype.js
"type="text/javascript"></script>
<script src="/scriptaculous/scriptaculous.js
"type="text/javascript"></script>

Visual Effects
One of the most exciting features of scriptaculous is its visual effects. These effects can be used to notify the user that an event has happened or that some content is updated. The effects can be applied to any DOM element, making them very versatile, because they will work no matter what the display type of the element is. To apply an effect, you create a new instance of a method of the Effects class, passing in the element to update. This element can be an ID or a DOM element accessed directly in JavaScript.

A wide variety of effects are provided. They perform two main tasks: showing or hiding elements and drawing attention to an element. Some of the show/hide effects are available in pairs and can be used with the Effect.toggle method to hide or show an element, doing the opposite of the element's current status. The rest of the functions can be used individually, like the simple examples in the following list of effects. An effects tester is also included so that you can see what each effect looks like. Scriptaculous also includes the lower-level methods that can be used to build new effects; the API for these methods is included on its Web site.

Hide/Show Pairs
BlindDown hides the element, and BlindUp shows it:
new Effect.toggle(element,'blind');
new Effect.BlindDown(element);
new Effect.BlindUp(element);

SlideDown hides the element, and SlideUp shows it:
new  Effect.toggle(element,'slide');
new Effect.SlideDown(element);
new Effect.SlideUp(element);

Fade hides the element, and Appear shows it:

new Effect.toggle(element,'appear');
new Effect.Fade(element);
new Effect.Appear(element);

A large number of nonpaired effects for hiding elements is also included:

new Effect.SwitchOff(element);
new Effect.DropOut(element);
new Effect.Squish(element);
new Effect.Shrink(element);
new Effect.Fold(element);

The Grow effect is the only unpaired effect for showing an element:
new Effect.Grow(element);

The Effects class also contains a number of methods for drawing attention to an element:
new Effect.Pulsate(element);
new Effect.Shake(element);
new Effect.Highlight(element);

The effects tester is located in the scriptaculousViewAllEffects.html file. Listing 8-9 shows a short example of how to apply various effects.

ScriptaculousViewAllEffects.html
1  <html>
2 <head>
3 <title>Script.aculo.us Visual Effects</title>
4 <script src="scriptaculous/prototype.js"
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8 </head>
9 <body>
10
11 <p>Reload the page to reset the effects.</p>
12
13 <div onclick="new Effect.Fade(this)">
14 Click Me to see a Fade Effect

15 </div>
16
17 <p>
18 <a href="#" onclick="new Effect.Puff(this)"
19 >Click to hide this link</a>
20 </p>
21
22 <p>
23 <a href="#" onclick="new Effect.Fold('cell')"
24 >Hide the table cell</a>
25 <a href="#" onclick="new Effect.Grow('cell')"
26 >Show the table cell</a>
27 </p>
28
29 <table border=1>
30 <tr>
31 <td>A cell</td>
32 <td id="cell">Cell to Hide</td>
33 </tr>
34 </table>
35
36 <p>
37 <a href="#" onclick=
38 "new Effect.toggle('box','blind')"
39 >Toggle the box below</a>
40 <div id="box"
41 style="border: solid 1px black;
42 width: 50px; height: 50px">
43 BOX
44 </div>
45 </p>
46 </body>
47 </html>

One way to attach an event is to tie it to the click event of a DOM element; this passes the element being clicked and performs the effect directly on the current element. This approach is easy to do and is shown on line 13 against a block-level element, and on line 18 against an inline element. There are few cases where this direct attachment is useful; in most cases, you'll want the effect to be performed against another element on the page because the point of the effect is to draw attention to the action that is happening. Line 23 hides the element with an ID of cell by using the Fold effect, whereas line 25 shows the same element using the Grow effect. Line 38 shows the toggle utility method, which alternately shows and hides an element. This method is useful for building interface elements that show optional information.

Drag-and-Drop
Drag-and-drop gives you the ability to visually drag elements around the page and have other elements that accept the drop. The scriptaculous implementation separates the drag-and-drop components into two parts, allowing you to make elements draggable without providing a place to drop them. This can be useful for adding palettes or note elements that can be moved anywhere within the window by the user. To create a draggable element, create a new instance of the Draggable class, passing in the element to drag and any options. A common option is revert; when it is set to true, the item returns to its original position when the user lets up on the mouse:

new Draggable(element,{revert:true});

In the second half of drag-and-drop, the drop target is provided by the Droppables class. Drop targets are useful in a number of cases, from building a visual shopping cart to allowing you to visually move mail to a new folder. Drop targets can be any element and can take a number of options, including an accept parameter that limits the elements that can dropped to those with a matching class. They can also include an onDrop handler, which is run when an element is added to the drop target:


Droppables.add(el, { onDrop: function(e) { alert(e);
});


Listing 8-10 shows a small drag-and-drop application. In this listing, there are three draggable boxes and one drop target. Only the first two boxes can be dropped on the target because the third box has a class that isn't in the accept list of the drop target. This example also uses the $() alias function; it works the same way as document.getElementById. Formatting for this example is done with CSS, which is included in a separate file to decrease the amount of noise.

ScriptaculousDragNDrop.html
1 <html>
2 <head>
3 <title>Script.aculo.us Drag and Drop</title>
4 <script src=""scriptaculous/prototype.js"
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8
9 <link rel="stylesheet" href="dnd.css"
10 type="text/css">
11 </head>
12 <body>
13 <div id="box1" class="box">Box 1</div>
14 <div id="box2" class="box">Box 2</div>
15 <div id="box3" class="other">Box 3</div>
16
17 <br style="clear: both">
18 <div id="drop">Drop Target</div>
19
20
21 <script type="text/javascript">
22 new Draggable('box1',{revert:true});
23 new Draggable('box2',{revert:false});
24 new Draggable('box3',{revert:true});
25
26 Droppables.add('drop', {accept: 'box',
27 onDrop: function(el) {
28 $('drop').innerHTML =
29 'Dropped: '+el.id;
30 }
31 });
32 </script>
33
34 </body>
35 </html>

Most of this page is set up in HTML with a small amount of JavaScript code to activate the drag-and-drop functionality. The page starts with a basic setup. Lines 47 include the scriptaculous JavaScript library, and lines 910 include a CSS file to do some basic formatting. Lines 1318 create the basic user interface; it is made up of three 200x200 pixel boxes that are floated next to each other. Below that is a 100x400 pixel drop target.

Lines 2132 make these boxes draggable and create a drop target for them. Lines 2224 create the draggable boxes; the first parameter is the ID of the box, and the second is a hash of options. On line 23, we set the revert property of the second box to false; this lets us drag it around the screen. This property isn't very useful for dragging to a drop target, but it can be useful for other use cases. Lines 2630 create the drop target; the first parameter is the ID of the element, and its second parameter is a hash of options. Here we're setting two options. The first is the accept variable, which takes a class to accept; in this case, it's set to box, which allows box 1 and 2, but not box 3, to be dropped. The second option is the onDrop function; this is called when a draggable element is released while over the drop target. The function displays some simple feedback displaying the ID of the dropped element in the drop target.

Sortables
A sortable is a predefined component built from the drag-and-drop building blocks that scriptaculous provides. Sortables make it easy to build graphically reorderable lists and can even be used to let you move items between multiple lists. Sortables are usually used with HTML lists, but they can also be used with floated elements. To create a sortable list, you simply run Sortable.create, passing in an ID and any options you want to specify, like so:

Sortable.create("element",{ghosting:true});

Some of the more commonly used properties are overlap, ghosting, and onChange:

  • The overlap property, which takes the values of horizontal, vertical, or false, limits how you can drag the elements around; the false setting has no limits.

  • Setting the ghosting property to TRue leaves the element in its current position; the user then drags a faded version until it is dropped.

  • The onChange property lets you set a callback function, which is called after an item has been moved.

If the elements in your sortable have the ID property set using the naming convention of name_item, you can use the Sortable.serialize method to quickly build a query string, which can be sent to the server and used to update the order on the server. An example output from the serialize method is this:



list[]=one&list[]=three&list[]=two&list[]=four

If you used this string as the query string on a request to a PHP page, $_GET['list'] will be populated with an array that contains the updated positions of the list. The array is ordered in its new position, with the value being the specified ID. Listing 8-11 shows an example of this operation.

ScriptaculousSortable.php
1  <html>
2 <head>
3 <title>Script.aculo.us Sortables</title>
4 <script src="scriptaculous/prototype.js"
5 type="text/javascript"></script>

6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8
9 <style type="text/css">
10 #list {
11 cursor: pointer;
12 }
13 </style>
14 </head>
15 <body>
16
17 <ul id="list">
18 <li id="i_one">One</li>
19 <li id="i_two">Two</li>
20 <li id="i_three">Three</li>
21 <li id="i_four">Four</li>
22 </ul>
23
24 <a href="javascript:updateList()"
25 >Send Changes to server</a>
26
27 <pre><?php
28 if (isset($_GET['list'])) {
29 var_dump($_GET['list']);
30 }
31 ?></pre>
32
33 <script type="text/javascript">
34 Sortable.create("list"});
35
36 function updateList() {
37 var update = Sortable.serialize('list');
38
39 window.location = '?'+update;
40 }
41 </script>
42
43 </body>
44 </html>

This page is mainly an HTML/JavaScript page with a small amount of PHP mixed in to show how a server-side language parses the output of Sortable.serialize(). The script starts with a basic setup, with lines 47 including the scriptaculous library. Then, on lines 912, we include a small amount of CSS, which gives all the sortable elements a pointer cursor. This is an important usability step; without it, the elements will have a text select cursor, and the user won't realize they are sortable. Lines 1722 build the list that will be sorted; each item has an ID in it, which defines the value that will be returned to the server. Lines 2425 complete the user interface, creating a link that reloads the page and sends the list's new order to the server.

Lines 2731 contain the small amount of PHP code in this script. If the list variable has been passed in by the query string, its outputs are echoed out using a debugging function. PHP and many other Web development languages automatically turn the query string provided by Sortable.serialize into an array; from here you could update the database with the new order.

Lines 3340 contain the JavaScript for this example. On line 34, we make the list element sortable, using most of the default options because they are optimized for use with HTML lists. Then, on lines 3640, we build a small function that builds a query string using Sortable.serialize (line 37) and then reloads the page by setting window.location.

Slider Control
Scriptaculous also provides a slider control, which is useful for selecting values that are in a range. This control can be used in its basic state to build something like a color selector. It can also be used as a building block for more advanced elements, such as a JavaScript-powered scrollbar for an AJAX grid. An example of the slider control in both horizontal and vertical modes is shown in Figure 8-1

The scriptaculous slider control shown in both horizontal and vertical modes

Scriptaculous provides only the behavior of the slider, not its looks. As long as you follow the pattern of a container element with a slide handle inside of it, you can make the slider look any way you want. Because you control the look of the sliders, you also control their usability. One simple usability tip is to set the cursor of the slide handle to a value of move. This gives you the browser's standard cursor icon for items that can be moved around, which helps users understand how to move the control. The slider returns a value from 0 to 1 as you scroll across its range; to translate this to a more usable value, you simply multiply it by the maximum value of your target range, rounding it if you want an integer, like so:

var outputValue = Math.round(100*sliderValue);

Listing 8-12 shows an example page that implements both a horizontal slider and a vertical slider.

ScriptaculousSlider.html
1  <html>
2 <head>
3 <title>Script.aculo.us Slider</title>
4 <script src="scriptaculous/prototype.jsv
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8 </head>
9 <body>
10
11 <h3>Horizontal Slider</h3>
12 <div id="track1" style="
13 width: 200px;

14 background-color: rgb(170, 170, 170);
15 height: 5px;">
16 <div id="handle1" style="
17 width: 5px;
18 height: 10px;
19 background-color: rgb(255, 0, 0);
20 cursor: move;
21 "> </div>
22 </div>
23 <div id="debug1"></div>
24
25 <h3>Vertical Slider</h3>
26 <div id="track2" style="
27 height: 100px;
28 background-color: rgb(170, 170, 170);
29 width: 5px;">
30 <div id="handle2" style="
31 width: 10px;
32 height: 5px;
33 background-color: rgb(255, 0, 0);
34 cursor: move;
35 "> </div>
36 </div>
37 <div id="debug2"></div>
38
39 <script type="text/javascript">
40 var d1 = document.getElementById('debug1');
41 var d2 = document.getElementById('debug2');
42
43 new Control.Slider('handle1','track1',{
44 onSlide:function(v){d1.innerHTML='slide: '+v},
45 onChange:function(v){d1.innerHTML='changed! '+v}
46 });
47
48 new Control.Slider('handle2','track2',{
49 axis:'vertical',
50 onSlide:function(v){d2.innerHTML=Math.round(100*v)},
51 onChange:function(v){d2.innerHTML=Math.round(100*v)}
52 });
53 </script>
55 </body>
56 </html>

Like the rest of the scriptaculous examples (Listings 8-98-11), this page includes the JavaScript library files in its header (lines 47). After that, the HTML for the sliders is laid out: first the horizontal slider (lines 1123) and then the vertical slider (lines 2537). Both sliders follow a similar pattern; first the slider track is defined, setting its ID, width, height, and color (lines 1215 and 2629). Then, the handles for the sliders are defined (lines 1621 and 3035). The handles set most of the same basic style elements as the track, adding a cursor of move for improved usability. The HTML definitions are finished by creating empty DIV elements to display the current value of the slider (lines 23 and 37).

The next section of the page is the JavaScript that turns these DIV groups into sliders. We start this process by assigning the debug DIV elements to variables so that we can easily reference them later (lines 4041). Then we create a slider instance for the horizontal slider control (lines 4346). The Control.Slider function takes three parameters: the track element, the handle element, and any options. In this case, we are setting two options: the onSlide and onChange event handlers. The onSlide handler is called as we move the handle around; the onChange handler is called when we're done dragging the handle. The onSlide handler is usually used to provide feedback, whereas the onChange handler is used to make the value of the slide accessible to other parts of the page, storing its value in an input box or JavaScript variable.

Lines 4852 follow much of the same process for the vertical slider. In this case, we set an extra option, axis, to the value of vertical setting. This does what it suggests and makes the slider work in a vertical fashion. We also translate the value of the slider to a 0100 scale in the onSlide and onChange handlers.

Scriptaculous Development Tips

Scriptaculous contains functionality for creating visually impressive Web sites. While using it, keep these tips in mind:

  • Most scriptaculous functions have further documentation and examples at http://script.aculuo.us, so if you're not sure how to make a function operate, start there.

  • Scriptaculous contains a variety of prepackaged effects and components, but if they don't meet your needs, it also provides the tools to build new ones.

  • Besides the Web site, you can find more scriptaculous examples in the tests directory, in the scriptaculous download. The functional tests are very useful in this regard.

  • Scriptaculous contains a number of additional controls that you should explore before building your own. These include the following:

    • Autocompleter: Provides Google Suggest style auto completing text fields

    • InPlaceEditor: Provides click-to-edit content with AJAX saving the changes

Read more...!

Monday, October 1, 2007

Sarissa

Sarissa is a GPL license library focusing on providing a cross-browser wrapper for the native JavaScript XML APIs. It provides an ECMA style API on all browsers it supports, which allows you to write to the standard no matter what browser you might be using. Its major features are AJAX communications, XPath, and XSLT support. Sarissa supports most major browsers, including Firefox and other Mozilla-based browsers, Internet Explorer (MSXML 3.0+), Konqueror (KDE 3.3+), Safari, and Opera. The code has reached a stable level and no longer has frequent releases, but the forums are busy and the developers respond to questions. Sarissa can be downloaded from http://sourceforge.net/projects/sarissa, and it has online documentation available at http://sarissa.sourceforge.net/.

Installation
Sarissa is a pure JavaScript library, so it's quite easy to install. Download the zip file from the SourceForge.net download page, and extract its contents to an accessible location on your Web server. The examples in this chapter use Sarissa version 0.9.6.1 installed at http://localhost/sarissa/; the Sarissa code is extracted into a subdirectory below that.

The release includes API documentation, including a basic tutorial located in the doc directory. It also includes unit tests that can be run by loading testsarissa.html and a sample application, minesweeper, in the sample-apps/minesweeper directory.

Making an AJAX Request
Sarissa gives you the ability to access XMLHttpRequest directly (or on IE6, a wrapper classes that looks the same), but that's not how you usually want to use it to make AJAX requests. Sarissa is designed around loading XML documents, so you can easily use the load command on its DOM documents to make a remote request.

Listing 8-1 does three main tasks: It includes the Sarissa library, creates a loadDoc function (which does an AJAX load of an XML file), and provides a simple UI for running the loadDoc function. The Sarissa library is included on line 5; in this example, the library is installed in the Sarissa subdirectory. Lines 921 define the loadDoc function; it's made up of a number of subtasks. Line 10 gets an empty Sarissa DomDocument. Lines 1217 define a handler function that is called each time the ready state of the DomDocument is called. This ready state handler is just like the one on XMLHttpRequest; state 4 is reached when the document is fully loaded. When this state is reached (line 13), we use the Sarissa.serialize method to turn the loaded document back into its textual XML representation and then turn < style="font-weight: bold;">SarissaMakingAnAJAXRequest.html

1  <html>
2 <head>
3 <title>Making an AJAX Request with Sarissa</title>
4
5 <script type="text/javascript" src="sarissa/sarissa.js">
6 </script>
7
8 <script type="text/javascript">
9 function loadDoc() {
10 var oDomDoc = Sarissa.getDomDocument();
11
12 var rHandler = function() {
13 if(oDomDoc.readyState == 4) {
14 document.getElementById('target').innerHTML =
15 Sarissa.serialize(oDomDoc).replace(/</g,'&lt;');
16 }
17 }
18
19 oDomDoc.onreadystatechange = rHandler;
20 oDomDoc.load("sarissaNews.xml");
21 }

22 </script>
23 </head>
24 <body>
25 <a href="javascript:loadDoc()">Load news.xml</a>
26 <pre id="target"></pre>
27 </body>
28 </html>

Basic XML Features
The Sarissa library focuses on providing good cross-browser XML support. To provide this, it creates a standardized interface to DOM documents loaded from any source. Most of this work is providing compatibility methods for Internet Explorer, hiding the fact that the XML capabilities are provided by the MSXML ActiveX control instead of by native JavaScript objects.

Working with DOM Documents
DOM documents are created in Sarissa through the use of the Sarissa.getDomDocument() method. Once you have a document, you can load content into it using three different methods. You can load remote data using AJAX (as shown in Listing 8-1), you can parse a string that contains XML data, or you can create the elements using standard DOM functions. Sarissa also includes a utility method, Sarissa.serialize(), for working with DOM documents. This prints out the document as its XML output, which is useful for debugging or in cases in which you want to send XML payloads to the server. To use the serialize method, just send the method a DOM document; a basic example is shown here:

Sarissa.serialize(domDoc);

Loading DOM Documents from a String
Loading DOM documents from a string gives you the ability to load a number of XML documents in a single request and then parse them into DOM documents to work with them. This can be a useful strategy for preloading XML during the normal page load, or it can be used with XMLHttpRequests that return data other than XML. (An example of such data is JSON.) A small example HTML page, which loads a short XML string into a Sarissa DOM document, is shown in Listing 8-2.

SarissaDOMDocumentString.html
1 <head>
2 <title>Loading a DOM document with an XML string</title>
3
4 <script type="text/javascript" src="sarissa/sarissa.js">
5 </script>
6
7 <script type="text/javascript">
8 var xmlData = '<rss version="2.0"></rss>';
9
10 function loadDoc() {
11 var parser = new DOMParser();
12 var domDoc = parser.parseFromString(
13 xmlData, "text/xml");
14
15 document.getElementById('target').innerHTML =
16 Sarissa.serialize(domDoc).replace(/</g,'&lt;');
17 }
18 </script>
19 </head>
20 <body>
21 <a href="javascript:loadDoc()">Load XML String</a>
22 <pre id="target"></pre>
23 </body>
24 </html>

In Listing 8-2, all the Sarissa interaction takes place within the loadDoc function, which is defined on lines 1017. The Sarissa library is loaded on lines 45, and an example XML string is defined on line 8. In practice, this string would be generated from a server-side language like PHP, allowing XML data to be accessed without an extra HTTP request. Line 10 starts our worker loadDoc functions. First we create a DOMParser (line 11), and then we use its parseFromString method to parse our XML string data contained in the xmlData var (lines 1213). parseFromString takes two parameters: the XML string and its content-type. Content-type is usually text/xml, but application/xml and application/xhtml+xml can also be used. The parseFromString method returns a DOM document, which can be used just like the one from Sarissa.getDomDocument().

On lines 1516, we print out the document using some basic entity replacement so that we can see the output in the browser. The rest of the XML is a link to run the example, line 21, and a pre-element that we use as a target for the printed-out DOM node.

Creating a DOM Document Manually
Because Sarissa works with DOM documents, all the normal DOM methods and properties are available. This allows you to create a DOM document with just its root node specified and then append additional nodes to it. In most cases, you won't use this functionality to create a complete DOM document; instead, you will use it to update a document loaded by one of the other methods. When creating a document manually, you'll want to specify the root node to create to the getdomDocument method; this is done by filling in geTDomDocument's optional parameters. Sarissa.getDomDocument takes two parameters: the namespace of the root and the local name of the root node. Listing 8-3 shows a small example using this method.

SarissaCreateNodesWithDom.html
1  <html>
2 <head>
3 <title>Sarissa: Create elements on a DomDocument</title>
4
5 <script type="text/javascript" src="sarissa/sarissa.js">
6 </script>
7
8 <script type="text/javascript">
9 function loadDoc() {
10 var domDoc = Sarissa.getDomDocument(null,'foo');
11
12 var elBar = domDoc.createElement('bar');
13 domDoc.firstChild.appendChild(elBar);
14
15 var elBaz = domDoc.createElement('baz');
16 var text = domDoc.createTextNode('Some Text');
17 elBaz.appendChild(text);
18
19 domDoc.firstChild.appendChild(elBaz);
20
21 document.getElementById('target').innerHTML =
22 Sarissa.serialize(domDoc).replace(/</g,'&lt;');
23 }
24 </script>
25 </head>
26 <body>
27 <a href="javascript:loadDoc()">Create an
28 XML document manually</a>
29 <pre id="target"></pre>
30 </body>
31 </html>

Listing 8-3 follows the same pattern as the previous examples: A loadDoc function is called by a small HTML interface. On lines 56, we include the Sarissa library, followed by the main JavaScript block, which defines loadDoc (lines 824). Line 10 creates the empty DOM document; we're not setting the XML namespace, so we pass null into that property, and the root node has a value of foo. Line 12 creates a new element with a tag name of bar; this is appended to the document on line 13. The bar element is appended to the firstChild of the DOM document, not directly to the document. This appending is done because an XML document can have only a single root element.

Lines 1519 repeat the same process for an element with the tag name of "baz". This time, however, the difference is that we add a child node to "baz". In this case, it is a DOM text node with the value of "Some Text", but it could also be any other XML element. There are two main types of nodes you work with in XPath: element nodes, which represent the XML tags, and text nodes, which hold the content within tags. This distinction also exists in HTML, but you don't see it as often because you can use the innerHTML property to grab the text content without worrying about DOM notes. Lines 2122 use Sarissa.serialize to output the generated document to the target element.

Using XPath to Find Nodes in a Document
Many times, when you're displaying data from an XML document, you'll want to look only at specific portions of the document. This is especially true for formats such as RSS that contain a number of news entries. XPath is an XML technology that allows you to select specific nodes within a document. A basic XPath follows the nodes from the root of the document to the element you're specifying. Each element can be directly addressed by a path; these paths start with a / and contain a / between each node (/rss/item). Further specificity can be provided by adding a bracketed number after the node name (/rss/item[1]). This path selects a particular occurrence of the node when there are multiple instances of a tag in this particular branch of the document. XPath can also query a document by starting with a double slash (//); these paths return any matching nodes (//item). Listing 8-4 shows an XML document that is used in some subsequent examples in this chapter.

An Example XML File
1  <rss>
2 <item>
3 <title>AJAX Defined</title>
4 </item>
5 <item new="true">
6 <title>Web 2.0 News</title>
7 </item>
8 </rss>

You can refer to the nodes of this document in a number of different ways. First, there are absolute paths. The path /rss/item[1] refers to the item node that starts on line 2 and ends on line 4. The path /rss/item[2]/title refers to the title node on line 6. You can also query style paths; the path //item refers to both the item node on lines 24 and the item node on lines 56. These queries can also look at attributes by using an "@"; the path //item[@new="true"]/title refers to the title node on line 6.

XPath is able to do more complex queries than what is shown in this simple overview. If you're dealing with XML documents in the browser, you will find XPath to be an important tool. XPath is a W3C standard, so you can easily find more information to move past the basics.

Sarissa provides the IE XPath API to all the browsers it supports, which provides an easy to use cross-browser API. The API consists of two methods on a DOM document: the selectSingleNode method and the selectNodes method. Each method takes an XPath, with selectSingleNode returning a single DOM node and selectNodes returning a node collection that you can iterate over to access all the nodes. Listing 8-5 is a small example page that shows how to use these XPath methods.

SarissaSearchingWithXpath.html
1  <html>
2 <head>
3 <title>Sarissa: Searching XML with XPath</title>
4
5 <script type="text/javascript" src="sarissa/sarissa.js">
6 </script>
7 <script type="text/javascript"
8 src="sarissa/sarissa_ieemu_xpath.js"></script>
9
10 <script type="text/javascript">
11 var domDoc;
12 function loadDoc() {
13 domDoc = Sarissa.getDomDocument();
14
15 var rHandler = function() {
16 if(domDoc.readyState == 4) {
17 document.getElementById('target').innerHTML =
18 "Document Loaded, ready to Search";
19
20 document.getElementById('afterLoad'
21 ).style.display = 'block';
22 }
23 }
24
25 domDoc.onreadystatechange = rHandler;
26 domDoc.load("sarissaNews.xml");
27 }
28

Lines 18 perform the basic HTML setup. Besides including the main Sarissa library file, we also include the sarissa_ieeme_xpath.js file. This file provides the IE XPath API to other browsers, and it is how Sarissa provides cross-browser XPath support. Lines 1227 define a loadDoc function, which loads the remote XML document we will be searching in this example. This code is identical to the earlier AJAX XML loading examples. The only exception is that now, we're defining the domDoc variable outside of the function so that it can be used elsewhere. In addition, we're showing a DIV element, which contains more links when the document is loaded instead of just printing it out. This file is continued in Listing 8-6 where the logic appears for searching the DOM using XPath.

SarissaSearchingWithXpath.html Continued
29 function searchBuildDate() {
30 var el = domDoc.selectSingleNode('//lastBuildDate');
31 document.getElementById('target').innerHTML =
32 "Build date = " + el.firstChild.nodeValue;
33 }
34
35 function searchItems() {
36 var list = domDoc.selectNodes('//item/title');
37
38 var target = document.getElementById('target');
39 target.innerHTML = "Number of Items = "+ list.length+
40 "<br>Titles:<br>";
41
42 for(var i = 0; i < list.length; i++) {
43 target.innerHTML +=
44 list[i].firstChild.nodeValue + "<br>";
45 }
46 }
47 </script>
48 </head>
49 <body>
50 <a href="javascript:loadDoc()">Load news.xml</a>
51 <div id="afterLoad" style="display: none">
52 <a href="javascript:searchBuildDate()">Last build date</a>
53 <a href="javascript:searchItems()">List item titles</a>
54 </div>
55 <pre id="target"></pre>
56 </body>
57 </html>

Lines 2933 define the searchBuildDate function; this function performs an XPath query against the loaded document to find the last build date of the document. This information is provided in a single tag called lastBuildDate, so the XPath to get the information is //lastBuildDate. The XPath query happens on line 30 when we call selectSingleNode. The value of the resulting node is then displayed in the target element. Because the lastBuildNode is from an XML document, we can't just use the innerHTML attribute. Instead, we access the text node inside the returned element and get its value (line 32).

Lines 3545 define the searchItems function; this function performs an XPath query that selects all the title nodes that are inside item nodes from the document and then outputs their value in the target element. The XPath query takes place on line 36; it returns a node collection to the list variable. On line 39, we use the collection's length attribute to output the number of items in the loaded RSS document. Lines 4245 loop over the returned nodes, outputting the value of the nodes to the target; this lists the title of each item in the RSS feed.

Lines 5055 create the document's basic user interface. Links are provided to run each JavaScript function with the search links that are accessible only after the RSS document is loaded. This delay is accomplished by putting them inside a DIV that is hidden until the document's onreadystatechange change callback shows it on line 21.

Transforming XML with XSLT
XSLT is a powerful XML-based template language. XPaths are used inside the template, which allows you to easily apply multiple subtemplates to different XML templates. Describing how to create an XSLT template could take a book as long as this one, so we focus only on the API that Sarissa provides to transform documents. The API is easy to use; you create a new XSLTProcessor, load a stylesheet that contains the transformation rules, and then transform the document using the processor's TRansformToDocument method. You'll usually want to import the resulting document into the main HTML document using its importNode method so that you can add it to the DOM and display the results. A short example is shown in Listing 8-7. The data is the same RSS feed of the Sarissa news used earlier; the only exception is that the stylesheet is shown in Listing 8-7.

transform.xsl
1 <?xml version="1.0"?>
2 <xsl:stylesheet version="1.0"
3 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4
5 <xsl:output method="html" />
6 <xsl:template match="/rss">
7 <div>
8 <xsl:for-each select="//item">
9 <h2><xsl:value-of select="title"/></h2>
10 </xsl:for-each>
11 </div>
12 </xsl:template>
13 </xsl:stylesheet>

This is a really basic stylesheet with a single template that matches the root rss element in the document (lines 511). Inside this template, we output a DIV container so that we have an HTML element encasing the rest of the output, which will make it easy to add to the main document. Lines 810 loop over the results from an XPath query. The query //item selects each item node in the document. The code then displays the value of the title of each item inside an h2 tag (line 9). The rest of the file is basic XSLT boilerplate. This XSLT stylesheet is used by an HTML and JavaScript page to transform an XML document; this page is shown in Listing 8-8.

SarissaTransformWithXSLT.html
1  <html>
2 <head>
3 <title>Sarissa: Transforming Documents with XSLT</title>
4
5 <script type="text/javascript" src="sarissa/sarissa.js">
6 </script>
7 <script type="text/javascript"
8 src="sarissa/sarissa_ieemu_xslt.js"></script>
9
10 <script type="text/javascript">
11 var domDoc = Sarissa.getDomDocument();
12 var styleSheet = Sarissa.getDomDocument();
13 styleSheet.load("transform.xsl");
14 var processor = new XSLTProcessor();
15
16 function loadDoc() {
17 var rHandler = function() {
18 if (domDoc.readyState == 4) {
19
20 document.getElementById('target').innerHTML =
21 "Document Loaded, ready to transform";
22
23 document.getElementById('afterLoad'
24 ).style.display = 'block';
25 }
26 }
27
28 domDoc.onreadystatechange = rHandler;
29 domDoc.load("sarissaNews.xml");
30 }
31
32 function transform() {
33 processor.importStylesheet(styleSheet);
34 var output = processor.transformToDocument(domDoc);
35
36 var target = document.getElementById('target');
37 target.appendChild(document.importNode(
38 output.firstChild,true));
39 }
40 </script>
41 </head>
42 <body>
43 <a href="javascript:loadDoc()">Load news.xml</a>
44 <div id="afterLoad" style="display: none">
45 <a href="javascript:transform()">Display Items</a>

46 </div>
47 <div id="target"></div>
48 </body>
49 </html>


Listing 8-8 takes the sarissaNews.xml file, transforms it with the transform.xsl XSLT stylesheet, and then adds its results to the main document's DOM. The Sarissa library is included on lines 58. Notice that we're including the cross-browser XSLT support files as well as the main library file. On lines 1114, we set up the objects we will use on the rest of the transformation process. On line 5, we set up an empty DomDocument into which we will load our RSS feed; then, on line 6, we create a similar object into which to load the stylesheet. On line 13, we load TRansform.xsl into the styleSheet document; you could also use the string parser to load transform.xsl. This would be accomplished by loading the contents of TRansform.xsl into a JavaScript variable and then creating the DomDocument using the DOMParser. Doing this would let you reduce the number of HTTP requests needed to load the document, which is helpful from a performance standpoint as long as the stylesheet is small. Finishing the basic setup, we create a new XSLTProcessor on line 14.

Lines 1630 define the loadDoc function, which loads sarissaNews.xml so that it can later be transformed. This works the same as the earlier examples; we're just adding a few more actions to perform after the document is loaded. On lines 2021, we output a message saying the document is loaded, giving the user feedback that something has happened. Then, on lines 2324, we show a DIV in the main HTML document. This DIV contains the links that do the actual transformation; by keeping it hidden until the document is loaded, we are able to prevent errors from happening. The rest of the method contains the simple Sarissa document loading processes; on line 28, we register the callback function, and on line 29, we load the sarissaNews.xml document.

Lines 3239 define a JavaScript function that does the transformation. This is a three-part process. On line 33, we import the stylesheet we previously set up, and then on line 34, we transform the document assigning the result to a variable. We finish the processes on lines 3638, selecting an output element and then appending the output to it after importing it to the HTML document. When importing the nodes, passing a Boolean value of true as the second parameter to importNode makes the method perform a deep import. A deep import imports the element passed in and all its children; without this flag, only the top-level element is imported.

The rest of the document is the basic HTML user interface. A link is provided on line 43 to load the sarissaNews.xml document, with the transform link enclosed in a hidden DIV so that it will be available only after the news document is loaded (lines 4446). We finish up with a target DIV on line 47 that we use for giving messages to the user and for showing the transformed document.

Sarissa Development Tips

Sarissa is a highly focused library that provides an easy-to-use, cross-browser API to the major browsers' XML functionality. If you're looking to use XML technologies such as XSLT or XPath, then Sarissa is a perfect solution for you. While using Sarissa, keep in mind these tips:

  • Be sure to include the sarissa_ieemu_xpath.js or sarissa_ieemu_xslt.js files if you're working with XPath or XSLT. Without them, your scripts will work only in Internet Explorer.

  • Use the XML string-loading capabilities to cut down on the number of individual XML files that you need to load.

  • Run the test cases in testsarissa.html to make sure your browser is supported if you're on a less commonly used browser.

  • Mix Sarissa with other libraries if Sarissa meets only some of your needs; Sarissa is focused on XML.

  • XPath is extremely effective at searching XML documents; try using it before creating custom solutions to search XML.

  • If you have a question about what method to use, check out the project's Web site; it contains complete API documentation.

Read more...!

Monday, September 17, 2007

Dumping Variables

As shown in lines 1017 of Listing 7-4, there are lots of cases in JavaScript in which you have an object but don't know what properties it contains. This is especially common when you are passing JSON-encoded data from the server to the client. JavaScript provides the "for in" loop for looping over the properties of any object. Using this loop, you can construct a basic dump function that allows you to quickly see the properties of an object. Listing 7-7 shows an example function.

A JavaScript Property Dumping Function

function dump(input) {
var msg = '';
for(var i in input) {
msg += i+':'+input[i]+"\n";
}
return msg;
}

You can then use this function to alert the contents of the object or assign the debug output to a debug element on a page. A simple function like this can save a great deal of time in the debugging and development processes. Building on this same concept, the HTML AJAX library provides a utility function called varDump that provides additional information. This method is based on the var_dump function in PHP and provides the type of each element as well as its value. It also supports recursion, giving you the output of any child objects. Listings 7-8 and 7-9 show the output of varDump against different inputs. Sample usage is shown in the following code:

alert(HTML_AJAX_Util.varDump(input));

Sample varDump Output (an Array with Another Nested Inside It)
array(3) {
[0]=>
number(1)
[1]=>
array(3) {
[0]=>
number(1)
[1]=>
number(2)
[2]=>
number(3)
}
[2]=>
number(3)
}

Sample varDump Output (an Object)
object(Object) (2) {
["bar"]=>
string(3) "baz"
["bat"]=>
number(5)
}

Read more...!

JavaScript Exceptions

JavaScript has a number of language features that are useful in the debugging process. The biggest one of these is the ability to catch any JavaScript error as an exception. This is done by wrapping code that can cause errors inside of a try block followed by a catch block. In the catch block, you have access to an Error object, which contains additional information about the error. This Error object contains the name of the error and a message. On Firefox, it also includes the file in which the error happened, the line on which it happened, and a call stack. Listing 7-4 shows an example of catching an error. Listings 7-5 and 7-6 show the output of the catch block in Firefox and IE.

exception.html

1  <html>
2 <body>
3 <div id="error">
4 </div>
5
6 <script type="text/javascript">
7 try {
8 alert(IDontExist);
9 }
10 catch (e) {
11 var msg = '';
12 for(var i in e) {
13 msg += i+':'+e[i]+"<br>\n";
14
15 }
16 document.getElementById('error').innerHTML = msg;
17 }
18 </script>
19 </body>
20 </html>

Listing 7-4 is simple. It contains a basic HTML page with a JavaScript block that creates the error and then prints out the resulting error object. Lines 34 give us an element to which to write an error message. Line 7 starts the try block; it's ended by the bracket on line 9. All JavaScript errors that happen in the TRy block will cause the catch block to be run. If no error happens, the code in the catch block is ignored. The catch block on lines 1017 takes the error object and prints out each property. This text is then added to the error element, showing the value of the error object.

Output of exception.html in Firefox
message:IDontExist is not defined
fileName:http://localhost/debug/exception.html
lineNumber:8
stack:@http://localhost/debug/exception.html:8
name:ReferenceError

Output of exception.html in Internet Explorer
name:TypeError
message:'IDontExist' is undefined
number:-2146823279
description:'IDontExist' is undefined

Looking at the error messages, you can see that exceptions in Firefox are more useful for general debugging, but exceptions are still useful even without line properties because you end up knowing which block of code caused the error. They also allow you to handle the error programmatically, letting you give the user an error message or even perform a workaround instead of having your code silently break.

Read more...!

JavaScript Debugging Tools

The tools we've looked at so far are good at giving you a picture of the communication between the client and the server, but they're not a huge help if the bug is somewhere in your JavaScript code. JavaScript isn't known for having a large number of tools to help the developer, and if you're working with Internet Explorer, that lack of tools is still true; however, Firefox (and other browsers) gives you a lot of tools that are helpful with debugging and developing JavaScript. For instance, the most basic Firefox development tool is the JavaScript console (see Figure 7-17). Any JavaScript errors are shown in it, and clicking one of them will show you the line in your code where the error happened. You can view the console by selecting it from the Tools menu; it's labeled either JavaScript Console or Error Console, depending on your Firefox version. (Note that a number of Firefox extensions, including Firebug, provide quicker access to this information.)

Firefox JavaScript Console

Out of the box, Internet Explorer gives you a particularly hard time debugging JavaScript because it doesn't provide correct lines numbers when external files are used. The solution to this is to install the Microsoft Script Debugger (www.microsoft.com/downloads/details.aspx?FamilyID=2f465be0-94fd-4569-b3c4-dffdf19ccd99&DisplayLang=en). After you install the debugger, you need to turn it on. To do so, follow these steps:

1. Open Internet Explorer and go to Tools -> Internet Options.

2. Select the Advanced tab.

3. In the Advanced tab, you see an option labeled Disabled Script Debugging (Internet Explorer). Uncheck this option to enable the debugger. This debugger dialog box is shown in Figure 7-18.

Enabling the Microsoft Script Debugger

4. Once you have the debugger enabled, you receive a dialog box for each JavaScript error. The dialog box asks if you want to debug your error (see Figure 7-19). Selecting Yes will open the debugger and show you the line in the file where the error occurred.

Script Debugger Error dialog box

The basic debugger interface is shown in Figure 7-20. From this screen, you can step through the code using the Debug menu, just as with any other debugger. The debugger has two main drawbacks: It creates a popup dialog box for each JavaScript error that has to be dealt with, and it tends to become unstable when actually using it for debugging. That being said, the Script Debugger quickly gives you the actual line where JavaScript errors happen, which is a big advantage over the normal IE error message.

Basic Script Debugger interface

Read more...!

Thursday, September 13, 2007

Looking at AJAX Communications

Commonly, when you get an error, it's caused by a small problem, and all you need to do to solve it is to look at what the client sent to the server and then look at the server's response. Some libraries provide logging mechanisms to record this information, and you can easily create a generic request logger using PHP, but I find tools that directly interact with my browser to be more effective. However, you don't always have access to these tools, or you may need to debug in a nondevelopment environment. In such cases, you'll want to know how to build a logger.

Building an AJAX Logger
To build an AJAX logger, you first need to identify the information that is sent from the client. This information includes three types of information: query parameters, HTTP headers, and possibly a POST payload. In PHP, the query parameters are automatically parsed and made available in the $_GET variable; if the data sent to the server is a form submission, the POST variables are made available under a similar variable, which is $_POST. You can also read the raw POST submission by reading from php://input; this raw access is required to see the information if the client sent a JSON or XML payload. Finally, you can access the headers through the $_SERVER variable. Listing 7-1 shows an example of reading this information.

read.php

1  <?php
2
3 echo "Query (GET) parameters<br>";
4 var_dump($_SERVER['QUERY_STRING']); // raw
5 var_dump($_GET); // parsed
6
7 echo "POST parameters<br>";
8 var_dump(file_get_contents('php://input')); // raw
9 var_dump($_POST); // parsed
10
11 echo "HTTP Headers<br>";
12 $headers = array();

13 foreach($_SERVER as $name => $val) {
14 if (preg_match('/HTTP_(.+)/',$name, $m)) {
15 $headers[$m[1]] = $val;
16 }
17 }
18 $other = array('CONTENT_TYPE','CONTENT_LENGTH');
19 foreach($other as $o) {
20 if (isset($_SERVER[$o])) {
21 $headers[$o] = $_SERVER[$o];
22 }
23 }
24 var_dump($headers);
25 ?>


The query parameters are the easiest inputs with which to work; the raw version is available as an index in the $_SERVER array (line 4), and that same data turned into an array is available through $_GET (line 5). POST data is available only when a POST HTTP request has been made. This can be done by using either a form or XMLHttpRequest. The raw POST header is read from php://input (line 8), and the parsed version is on line 9. $_POST is populated only when the POST has a Content-type of application/www-form-urlencoded or multipart/form-data.

Reading the headers is a little harder because they are stored in $_SERVER along with a bunch of other data. The majority of the HTTP headers are prefixed with HTTP_, so we can display them by looping over $_SERVER and doing a regular expression match (lines 1317). This match will store the matching headers into an array. A couple of important HTTP headers don't have an HTTP prefix, so we look for the Content-type and Content-length headers using a secondary check (lines 1823).

The examples for this chapter, which can be downloaded from the book's Web site, include a small test page (test.php) so that you can see the output of this script. The output for a GET request with a query, a form POST, and a POST from XMLHttpRequest are shown in Figures 7-1 through 7-3.

A GET request with a query string

A form POST


A POST request from XMLHttpRequest

To make this code usable for AJAX logging, we just need to format the output and add the ability to write it to a file. It's also useful to let the server code log what data it is going to send to the client. The final setup is a class called logger. It has two methods: storeServer, which sets the content we're going to send to the client, and write, which puts the information in a file. Listing 7-2 shows an example of a page that generates HTML chunks for an AJAX page.

pageChunk.php
1  <?php
2 require_once 'logger.class.php';
3
4 $logger = new logger();
5
6 // create some content
7 // use an output buffer so we can log it
8 ob_start();

9 ?>
10 <p>
11 Some random content
12 It could be anything generated from PHP
13 or static like this
14 </p>
15 <?php
16 echo $_GET['input'];
17
18 // page is done
19 $logger->storeServer(ob_get_contents());
20 $logger->write();
21
22 ob_flush();
23 ?>


Using the Logger
Adding our logger is a fairly simple process. We include the logger class (line 2) and then create a new instance of it (line 4). When the logger instance is created, it automatically grabs the information from the client, so if the $_GET, $_POST, or $_SERVER variables are changed by other parts of the code, our logging is not messed up. Then, we start an output buffer (line 8). This will allow us to log what is being sent back to the client without changing any of our existing code. Lines 1016 contain some example input, and then we finish our logging process. Line 19 uses the ob_get_contents method to read all the output that has been created and to set it on our logger. Then, on line 20, we write out a log entry. The script ends by calling ob_flush (line 22), which sends out all the generated content we've been buffering. This simple logging process stores most of the details of an example entry from the log file, as Listing 7-3 shows.

LogEntry
################################################################################
Request to: /debug/pageChunk.php
Time: 2006-02-25 11:45:13

RAW Query String: input=test
_GET:
array (
'input' => 'test',
)

RAW POST:
_POST:
array (
)

HTTP Headers:
HOST:localhost
USER_AGENT:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.1)
Gecko/20060111 Firefox/1.5.0.1
ACCEPT:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,te
xt/plain;q=0.8,image/png,*/*;q=0.5
ACCEPT_LANGUAGE:en-us,en;q=0.5
ACCEPT_ENCODING:gzip,deflate
ACCEPT_CHARSET:ISO-8859-1,utf-8;q=0.7,*;q=0.7
KEEP_ALIVE:300
CONNECTION:keep-alive
COOKIE:clearhealth=fcf23cdc7394e71b5c83a9929f0fdb7e
CACHE_CONTROL:max-age=0

Sent to client
<p>

Some random content

It could be anything generated from PHP

or static like this

</p>

test
Logging like this is easy to build into the AJAX library that you are using (if it doesn't already have its own). It's easy because the process happens automatically. If you're using an HTML page chunk approach, you could also build it right into your framework; in that case, the logging could be done on a preconfigured set of pages, or it could be turned on by sending a custom header when you make a request using XMLHttpRequest. Logging like this is especially useful in large-scale testing or production environments in which you don't have access to the browser to see error messages.

Firebug: A Firefox Debugging Extension
While logging provides you with a lot of information, it's not the most efficient way to get the information you need. It means keeping another file open and parsing through large text entries for the required information. Logging also doesn't have access to what is happening to the JavaScript on the browser, so it doesn't contain the full picture. Tools that can be added to the browser can get around most of these problems and have the ability to offer you a rich user interface.

If you're using Mozilla Firefox, extra functionality can be added to your browser through the use of an extension. The Firebug extension (www.joehewitt.com/software/firebug/) adds a popup bar at the bottom of the browser that lets you see JavaScript errors. With it, you can also view details of each XMLHttpRequest request, inspect DOM elements, and run JavaScript commands against the current page. An installation link is available from the project's Web site. The basic interface is shown in Figure 7-4.

Firebug: a Firefox debugging extension

Firebug attacks the debugging process from the JavaScript side. Whenever a JavaScript error happens, an error icon is shown in the lower-right corner (see Figure 7-5). Clicking this icon will bring up the console, showing you the errors. In its default configuration, these errors include errors generated by CSS, the browser, and its extensions (see Figure 7-6). To limit the displayed errors to just JavaScript ones, use the Errors drop-down box to deselect Show Errors From Chrome and Show CSS Errors. You can also get to the dialog box even when an error hasn't happened by clicking the error icon in the lower-right corner (see Figure 7-7). Doing this gives you an easy way to get to the XMLHttpRequest inspector.

Firebug error icon

Firebug error selector

Firebug status icon

The XMLHttpRequest inspector lets you see the POST payload that was sent to the server, the response from the server, and the properties of the XMLHttpRequest object that sent it. A new row is added to the Firebug pane each time a request is made using XMLHttpRequest, so you can also use it as an overview of your AJAX activity. Each request entry includes the HTTP request type (generally GET or POST) and the URL to which the request was made. Figure 7-8 shows Firebug with several requests in it.

Firebug with multiple XMLHttpRequest entries

Now if you have a request that isn't working as expected, you can start the debugging process by doing the following:

1. Open Firebug and find the request in the pane. Requests are added to the bottom of the list, so scroll down to the most recent one. Selecting the request expands it, giving you three tabs from which to choose.

2. Select the Post tab on the right side to see the POST payload that you sent to the server.

3. Select the Response tab to see the data that the server returned.

4. Select the Headers tab to see the HTTP headers that the server returned.

Depending on the configuration of the server, you may see a large number of headers returned with the data, as in Figure 7-9. The most important ones are Content-type and Set-Cookie. Content-type has to be set to text/xml for XML-based requests to work. Many libraries also use this header to determine if the server is sending JSON that the library needs to decode, or if the content is just plain HTML. The Set-Cookie headers mark which cookies were set on this request; you can use them to verify that new authentication or other cookies were set as needed.

Firebug showing the Headers tab of an XMLHttpRequest entry; the important fields are outlined

Firebug also has the capability to run any JavaScript command against the current page. To do this, type a JavaScript command such as document.getElementByName('test').style.color = 'red'; into the field at the bottom of the page (see Figure 7-10). You can also inspect page elements, get layout information, and view all elements, DOM properties, and events (see Figure 7-11). These features are useful in the overall development processes, but the biggest aid to debugging AJAX requests is the XMLHttpRequest inspector.

Running a JavaScript command in Firebug

Inspecting a DOM element with Firebug

Fiddler
If you develop in Internet Explorer or just need to track down an Internet Explorer-specific bug, you obviously won't be able to use Firebug, but there still are a number of useful tools to help you. One tool is Fiddler, which is an HTTP debugging proxy that lets you see each request made by Internet Explorer and the response it receives from the server. Because it is an external tool, it can't look at the JavaScript the way Firebug can, but it does have an easy-to-use interface and it does give you an easy-to-use view of each request.

You can download Fiddler from www.fiddlertool.com/; this Web site also includes a wealth of information on how to use its advanced scripting and transformation features. Once you have it installed, you just need to run it, and Internet Explorer will automatically be set up to use it. You can also use it with other browsers by setting them up to use Fiddler as an HTTP proxy. You just set the proxy host to localhost and the port to 8888. An example of setting up the proxy in Firefox is shown in Figure 7-12.

Setting up Firefox to use Fiddler as its proxy

Once you've run Fiddler, open your browser and perform the request that is giving you problems. Each HTTP request is shown as an entry in the main pane; secondary requests for images and CSS files will be shown with gray text, and the two HTML pages will be shown in blue. You can clear entries from the pane by right-clicking them and selecting remove. The basic session browsing interface is shown in Figure 7-13.

Main Fiddler interface

Once you've identified the HTTP request you want to inspect, select it in the sessions pane on the left side and then look at the pane on the right side. This pane has three main tabs. For debugging purposes, the most useful tab is the Session Inspector. After you select the Session Inspector tab, the pane will be split into two sections: the top showing the information that was sent to the server and the bottom showing the server's response (see Figure 7-14). For both the request and the response, you can view the request in a number of different formats, the most useful being the Headers and TextView views. If you're using XML to move data between the client and the server, you will also find the XML view to be useful because it will show the XML data in a formatted display.

Fiddler session Inspector interface

The Headers view shows you a formatted version of the HTTP headers used in the request (see Figure 7-15). You can use this view to verify cookies are being sent, to see the Content-type of the data, and to view any HTTP status codes. The online help gives more details, but outside of verifying expected Content-types, you shouldn't need to spend much time here. The TextView view shows you the content that was sent to the server in a POST request and the results that the server sent back. The view offers the ability to search the content inline (the blue Find bar) and to open another program, such as your favorite text editor. (The open with command is the button to the right of the blue Find bar). You can see an example of the TextView in Figure 7-16. Fiddler offers a number of other features, which are useful for transforming the request and responses, but they are not all that useful in basic AJAX debugging situations.

Fiddler Headers view

Fiddler TextView view

General Debugging Scenarios
Firebug, Fiddler, and custom logging code all provide extra information about an AJAX request. These types of tools are useful in scenarios in which you make an AJAX request and the request dies without giving useful information to your JavaScript code. In most cases, looking at the response from the server will quickly point out the problem. It could be an error in your server code or just some unexpected data being returned.

Debugging tools such as Fiddler or Firebug can help you figure out what data you're sending to the server. This can be especially useful once you start using more complex JavaScript widgets and AJAX libraries. These libraries offer a lot of functionality, but they move the AJAX operations far away from the actual event. This can make it hard to see what is really happening, so by using a tool like Firebug or Fiddler, you can see the HTTP request that was sent to the server and know what is really going on.

Read more...!

Monday, September 10, 2007

Two Sides to Debugging

When you look at an AJAX application, it is important to remember that there are two sides to the equation: the server, which interacts with the back end, and the client, which adds interactivity to the user's environment. Although this two-sided model has always existed, what has changed dramatically with the rise of AJAX is the complexity of the code running on the client. A secondary complexity is the number of interaction points between the two sides. Added complexity always makes debugging harder, so your focus when debugging an AJAX application needs to be on the various ways you can reduce that complexity.



The first step to debugging any problems is to separate the two sides as much as possible. This is generally an easy process on the server side; if you are generating HTML page chunks, you can go to the URL and inspect the output, looking for errors generated by the server code. If you're using a JSON or other RPC-based approach, it's slightly harder. However, you can always check the code that is being run by the RPC call, using normal development tools to look at its state before it's encoded. Many people find unit tests to be especially helpful in verifying the operation of the server-side code. It's especially useful for applications that expose chunks of functionality as services to the client. Unit tests work well on the server side because every major language has mature tools for managing these processes, and it narrows the type of problems you'll see on the data-production side.

On the JavaScript side, unit tests have a harder time being effective. This is due both to the lack of tools and the difficulty of testing an environment that relies on user-created events. There are a number of jUnit ports to JavaScript, and by using these testing frameworks and working hard, you can successfully use unit tests on the JavaScript side as well. Unit tests are a tool, and in AJAX development, they can be powerful because they provide a framework to test both sides of the equation separately. Note, however, that they are not your only option. The most important item to remember is to debug the easy stuff first. If you get an error, always verify that your server-side code is working properly before moving on to the JavaScript.

Read more...!

Friday, September 7, 2007

Common Usability Problems

Although AJAX has the ability to create a more usable and efficient interface, it doesn't always achieve this goal. Usability problems can steal away any gains created from the use of new technology. This section discusses some common usability problems and then gives examples on how to fix them. Many of these problems can happen without the help of AJAX, but they are prevalent in AJAX applications, which are more active by nature.

Stealing Focus with Validation Messages
A common use of AJAX is to perform complex validation before the user has submitted a form. This is especially useful on large forms where large amounts of data are input before validation can take place. Simplistic attempts at continuous validation are often worse than waiting until the form is submitted to perform the validation, because they continuously steal the user's focus.

Figure 6-1 contains a basic registration form. AJAX is used to validate that the username hasn't already been taken and that the zip code matches the city/state information. The problem here is that validation steals the user's focus because it uses popup alerts to give validation errors as the user tabs to the next field.

A validation message stealing focus on a user registration page


One solution to this problem is to change from a popup error message to an inline one. In some cases, this is an acceptable solution, but it still causes problems. (In this case, the problem is that the more useful error message will cause the form to move down quite a bit in the user's view.) Although usable, this approach pushes most of the form off the user's page after a couple error messages are shown. It's also not clear which fields need to be updated. Figure 6-2 shows an inline validation message.

A validation message that pushes the rest of the form down


Figure 6-3 shows a possible solution to these problems. The size of the error messages was reduced to a single line, and the use of a "More Info" link now provides information on how to solve the error. The fields with errors were also highlighted to show what needed to be updated.

Validation messages with the usability problems fixed

Stealing focus problems often happen when trying to meet the guideline of providing user feedback. Although user feedback is important, it should not prevent the user from completing his or her current task. This is especially important when feedback is combined with automatic actions, because the user has no reason to expect the feedback to happen.

Preventing Undo with Autosave
In conventional Web applications, large forms can be dangerous because they are impossible to save automatically, and submitting the form on a regular basis depends on the user clicking a button. Plus, such saving interrupts the user's workflow. Here's an example: A content management system was upgraded with AJAX to save an article automatically every five minutes. This seemed like a great solution because then only five minutes of work could be lost by a browser crash or an accidental closing; however, after more use, a couple of problems with this approach were found.

One problem occurred when an author opened an article to edit it. He or she would make some changes and then decide, perhaps, that he or she didn't like the approach taken. In other words, the author ended up wanting to revert to the old version, but couldn't; during the editing process, the autosave process had already overwritten the original. This was not good.

As you can see from the previous scenario, editing couldn't be done on live articles because the autosave process would push out changes before they were complete. This problem can be solved easily by creating a separate autosave area to store the data from this process. This autosave area can keep anywhere from one to an unlimited number of autosaves. An interface is then added to let users load an autosave and save it as a normal version.

In some content management systems, documents are versioned so that the original problem of overwriting data doesn't happen. However, saving each autosave as a new version can also be problematic because it makes for a clumsy document history and can cause data-storage needs to explode. In a versioned content management system, a separate autosave area would also work, but it may be more useful to use a subtree in your version history to which autosaves are written. The application can then remove the old autosaves when each normal save is complete. This allows autosaved articles to be accessed by the normal article-management process without causing an explosion in version history.

Preventing undo operations often happens when AJAX implements autosave. This prevention results in data-loss situations from a process that was supposed to prevent them. Whether you are using time-based saving while editing an article or autosaving a form by detecting what field is edited, you are changing the way a Web form normally operates. Because you are moving the save decisions out of the user's hands, you need to provide a way for the user to revert his or her changes.

Updating Sections of a Page Without the User Realizing It
One way to use AJAX is to update parts of the page with new content in response to a user's action. This AJAX updating process could be used in technical documentation to load definitions or related information. A basic example of this is shown in Figure 6-4. When you click any term with a dashed link, its definition is loaded into the sidebar on the right.

Documentation Browser with no feedback

The page in the preceding figure is already taking some usability steps, such as using a different link style for definition links, but it still has one problem: No feedback is given to the user regarding the fact that something has changed. Feedback is important in this case because we are redefining a standard browser action. Normally, when you click a link, it loads a new page. If the user were expecting a new page to load and didn't notice the change on the right, he or she might think the link was just broken.

Providing feedback is about finding the right balance; we want to show that something has changed without annoying the user with effects that are overly flashy and distracting. There are a number of different techniques we can use in this case. One technique is to show a loading message while the browser is waiting for the server to respond. At this same time, we can fade out the current content and then fade it back in after the new content has arrived. Figure 6-5 shows this approach in progress.

Documentation Browser while loading new content

An alternative to providing loading feedback is to wait until the new content is loaded and then highlight it in a pale yellow background. Over a period of three to five seconds, this yellow then fades to the white background color. Figure 6-6 shows the beginning of this process. In a documentation browser, I would recommend this approach because it doesn't distract the user until there is actually something to see. However, in another application where the content may take a while to load, immediate feedback would also be needed. You could combine both approaches, but in most cases, a more subtle approach is just as effective and is more aesthetically pleasing.

Highlighting content to provide feedback that the content has been updated

Breaking Bookmarking by Using AJAX to Load Entire Pages
Many times, when developers adopt a new technology, they see it as the solution to every problem they have. One area where AJAX can be overused is as a replacement for HTML framesets. This AJAX usage allows you to skip the loading of the navigation sections and just update the main body of the document. However, unless you take steps to make bookmarking work, you have the same problems as using framesplus a more complicated development process. Figure 6-7 shows an example of this mistake; here, everything below the navigation bar is loaded using AJAX.

Loading an entire page using AJAX

In most of these cases, the simple solution is to use only normal pages; because navigation is seldom a large part of a page's loading time, these approaches save little in load times. In other cases, you may find that trying to load entire pages using AJAX is just a symptom of larger design issues, and you'll need to start at the beginning and focus on how users will interact with your information. AJAX is a great tool, but it is not the solution to every problem. Design and development should always keep the user in mind. The end result might not use every technology you would like, but it will make for the best user experience.

Making AJAX Required on a Web Store
Although AJAX adds great capabilities to a Web site, it's not always worth the cost. The biggest cost is preventing users from using a site because they're not using an advanced-enough Web browser. While it's easy to say "Please upgrade," doing so is not always possible, especially if the user is using a mobile device, such as a PDA or cell phone. Let's look at a fictional scenario to see some of the problems that might be caused by requiring AJAX. A Web-based store updated its shopping cart to use AJAX; it then tested AJAX on all the major browsers, and everything worked fine. Because the store was supporting all major browsers, it decided to drop its non-AJAX version. After rolling out the new shopping cart, a large number of complaints came into the company's support email. After some research, the store noticed that all the complaints were from users using Pocket IE on smart phones or PDAs.

This is a fictional example, but it shows important points. Although no new computer will come with a browser that can't be used with AJAX, lots of mobile devices still will. If your site has a broad user base, requiring AJAX may shut out a sizable part of your audience.

Read more...!