reptile7's JavaScript blog
Monday, May 30, 2011
 
Ajax 242
Blog Entry #216
Your retrieval of remote XML data was successful!
We continue today our analysis of HTML Goodies' "How to Develop Web Applications with Ajax: Part 1" tutorial. At the end of our last episode, we sent off a GET HTTP request for a data.xml XML document via an xmlObj XMLHttpRequest object. What next? How do we access that document and its contents?

In the Select and copy section of Blog Entry #180, we very briefly noted that the DOM HTMLIFrameElement interface features a contentDocument attribute that references an iframe's src document. The XMLHttpRequest object has an analogous responseXML property that will serve as our gateway to a requested XML document. The responseXML return implements the DOM Document interface, meaning that once we get our hands on that return, we can use the interface's properties and methods to probe the requested document and walk through the document's DOM tree.

However, we can't just follow the xmlObj.send(null); command with some sort of xmlObj.responseXML expression, as the browser will hit that expression before the data.xml data arrives and consequently the expression will return null - not what we want, needless to say. Even for an asynchronous request, it is necessary to monitor the server's response so as to ensure that the requested document is available before we attempt to act on it; fortunately, this is easily accomplished via the XMLHttpRequest readyState property.

Since IE 4, Microsoft has provided a readyState property for tracking the loading progress of an object - go here and here for its documentation - this property can be applied to most HTML element objects but is most directly applicable to objects that load data themselves: the document object, the image object, the frame object, and so on. As an object loads, it goes through a series of ≤5 readyStates, not necessarily in this order: uninitialized, loading, loaded, interactive, and complete; to detect changes in these values, a companion onreadystatechange event handler was also implemented.

At least for the loading and complete stages, non-IE browsers now support the readyState property for the document object - Mozilla's document.readyState page is here. The W3C has standardized document.readyState and equipped new "media" (audio and video) elements with a readyState property in HTML5.

When Microsoft introduced the XMLHTTP object in IE 5 for Windows, it brought on board a modified readyState property having a scale of integer values running from 0 to 4, 4 meaning all the data has been received; a corresponding onreadystatechange event handler is again used to monitor changes in these values. The XMLHTTP readyState property and onreadystatechange event handler were subsequently ported by Netscape to the XMLHttpRequest object.

At the top of the tutorial's second page, the author attempts to detail the XMLHttpRequest readyState property by correlating its 0-4 scale with the uninitialized-to-complete readyState scale for other objects. As it happens, the two scales really don't match very well. The 0 and 4 values do respectively correspond to uninitialized and complete; however:

• The 1 value means A request has been opened, but the send( ) method has not been called and has no counterpart on the non-XMLHttpRequest side.

• The 3 value maps onto the loading value, but the 2 value is also a loading-type value indicating that the HTTP headers and status of the server's response are available.

• The interactive value means User can interact with the object even though it is not fully loaded and has no counterpart on the XMLHttpRequest side.

• Moreover, the loaded value means Object has finished loading its data and seems to be equivalent to the complete value.

In fairness to the author, the Object Properties section of Apple's "Dynamic HTML and XML: The XMLHttpRequest Object" article also implies an equivalency between the two scales; Mozilla also does this in the Step 2 - Handling the server response section of its "Getting Started [with Ajax]" article. To get sorted out on the XMLHttpRequest readyState property, you should consult
(a) the aforelinked Microsoft XMLHttpRequest readyState page,
(b) Mozilla's XMLHttpRequest.readyState page, and
(c) the States section of the WHATWG's XMLHttpRequest specification-in-progress.

Getting back to the tutorial code, the xmlObj.open("GET", file); command is preceded by a function expression that listens for changes in xmlObj's readyStates:

xmlObj.onreadystatechange = function ( ) { ... }

Microsoft's XMLHttpRequest onreadystatechange page states that you must set the event handler after calling open( ); maybe this is true for recent versions of IE but it's not true for the XMLHttpRequest-supporting browsers on my computer. Mozilla's send( ) method documentation used to say Note: Any event listeners you wish to set must be set before calling send( ) but no longer does so; I find that the script isn't hurt at all if the open( ) and send( ) commands are placed before the onreadystatechange expression.

The onreadystatechange function first checks if the request operation is complete - 4 is the only xmlObj readyState that we are really interested in:

if (xmlObj.readyState == 4) { ... }

If the if condition is true, then the onreadystatechange function next calls an updateObj( ) function

updateObj("xmlObj", xmlObj.responseXML.getElementsByTagName("data")[0].firstChild.data);

that will update the "sample data" p element of the current document. The first updateObj( ) call parameter, xmlObj (not xmlData as stated in the tutorial text), is the id value of the to-be-updated p element. As for the second updateObj( ) call parameter, what a mouthful, huh? This lengthy expression scoops up the textual content, including leading and trailing white space, of the data.xml document's data element. To see what's going on here, let's recast the expression on a feature-by-feature basis:

var xmldoc = xmlObj.responseXML;
var dataNodeList = xmldoc.getElementsByTagName("data");
var dataNode1 = dataNodeList[0];
var dataTextNode = dataNode1.firstChild;
var dataText = dataTextNode.data;
updateObj("xmlObj", dataText);


Per the discussion at the outset of the post, xmlObj.responseXML; gets the data.xml document as a whole. xmldoc specifically returns [object XMLDocument] with Firefox and Opera and [object Document] with Safari and Chrome.

To refresh your memory, here's the data.xml document again:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <data>
    This is some sample data. It is stored in an XML file and retrieved by JavaScript.
  </data>
</root>


xmldoc.getElementsByTagName("data"); gets an in-source-order list of data element nodes in the data.xml document. dataNodeList specifically returns [object NodeList] with Safari, Opera, and Chrome but strangely returns [object HTMLCollection] with Firefox.

dataNodeList[0]; gets the first-in-source-order (and only) data element in the dataNodeList NodeList. This statement's syntax is not quite standard; the W3C-kosher way to access the dataNode1 node is to use the item( ) method of the DOM NodeList interface, i.e., var dataNode1 = dataNodeList.item(0);. dataNode1 specifically returns [object Element] with all of the aforementioned browsers.

You may be wondering, "Can't we give the data element an id="dataID" attribute and then use the getElementById( ) method to grab it?" This is possible, but then we'd have to write up for the data.xml document a custom DTD that assigns the ID type to the id attribute. If you'd like to do that, be my guest. I'll pass.

dataNode1.firstChild; gets the first (and only) child node of the dataNode1 data element: an object implementing the DOM Text interface and representing the textual content of the element. dataTextNode specifically returns [object Text] with all of the aforementioned browsers.

dataTextNode.data; gets the character data of the dataTextNode Text node:

This is some sample data. It is stored in an XML file and retrieved by JavaScript.

In the data.xml source, this string is preceded by an end-of-line character and four space characters and is followed by an end-of-line character and two space characters; all of this white space is picked up by the data property but will be entirely or mostly discarded when we paste dataText into the current document.

The DOM textContent property can be substituted for the firstChild.data part of the original expression.

xmlObj.responseXML.getElementsByTagName("data").item(0).textContent;

We move now to the updateObj( ) function:

function updateObj(obj, data) {
    document.getElementById(obj).firstChild.data = data; }


The xmlObj id and the dataText string are initially assigned respectively to obj and data identifiers. Subsequently, the obj element's firstChild.data - all of the character data between the p element start-tag and the anchor element start-tag for the View XML data link - is replaced by the data string. Recall that we earlier zeroed out the View XML data link via a this.style.display='none'; command; if you're not going to bring back the link and are willing to overwrite it, then firstChild.data can be replaced by textContent in this case as well.

The author conspicuously does not provide a demo for his handiwork. How does it all go in practice? I'll give you the skinny and roll out my own demo in the following entry.

reptile7

Friday, May 20, 2011
 
Beginning Ajax for Non-Dummies
Blog Entry #215

Today we will take up HTML Goodies' "How to Develop Web Applications with Ajax: Part 1" tutorial, which is authored by Jonathan Fenocchi and which appeared originally at WebReference.com. The "Ajax" in the tutorial title refers to the Asynchronous JavaScript and XML group of technologies. As this tutorial marks our introduction to Ajax, let me begin by giving you some Ajax-related references:
(1) Mozilla's Ajax portal is as good a place as any to kick off your Ajax education.
(2) Be sure to check out Microsoft's XMLHttpRequest Object page.
(3) The W3C is developing an XMLHttpRequest specification, which is currently at the Candidate Recommendation stage. The W3C's work on an XMLHttpRequest specification has been taken over by the Web Hypertext Application Technology Working Group (WHATWG).
(4) At the end of the tutorial, the author himself links to an Apple Developer "Dynamic HTML and XML: The XMLHttpRequest Object" article.

The Ajax concept - at least as it plays out in the tutorial - is pretty straightforward. We are familiar with the idea of 'linked resources': external files (an image, a style sheet, a script) that a current document might call on. Heretofore the links to these resources were statically set in the current document source; for example, we would link to an external myScript.js JavaScript script via a

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

script element. With Ajax, we will complementarily link to a resource - specifically, an XML document - on the fly and then use that resource's data to modify the current document. Regarding the "A" of Ajax, our request for the XML document will be asynchronous, which in practice means that the request will not cause the operating system to block and that we won't need to wait for the server's response to do other things - in this respect, "asynchronous" is somewhat like the term "modeless" in a dialog context (we previously discussed the difference between modeless and modal dialogs in Blog Entry #184).

At the heart of Ajax is an XMLHttpRequest object that represents an XML request using HTTP, quoting Microsoft, and that will serve as the middleman between the current document and the XML document we'll be requesting. The XMLHttpRequest object is today supported by all of the major (GUI, JavaScript-capable) browsers; the properties and methods of the XMLHttpRequest object vary somewhat on the Microsoft and Mozilla sides but the XMLHttpRequest members we'll be using - the onreadystatechange, readyState, and responseXML properties and the open( ) and send( ) methods - all have cross-browser support.

With no further ado, let's get to the tutorial code, which replaces the textual part of a p element with the content of an XML <data> element - nothing too earth-shaking, as befits an introductory tutorial. Here's the p element we'll be updating:

<p id="xmlObj">
This is some sample data. It is the default data for this web page.
<a href="data.xml" title="View the XML data." onclick="ajaxRead('data.xml'); this.style.display='none'; return false;">View XML data</a>
</p>


The p element presents "some sample data" followed by a View XML data link. Clicking the link sends users without JavaScript support to the data.xml document below via the link's href attribute:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <data>
    This is some sample data. It is stored in an XML file and retrieved by JavaScript.
  </data>
</root>


What you see above is what you'll see at the data.xml page: the beginning XML declaration, the root and data element markup, and the data element content will all be visible (the white space formatting may vary a bit depending on your browser).

For users with JavaScript support, clicking the View XML data link executes three statements:
(1) ajaxRead('data.xml'); calls an ajaxRead( ) function and passes thereto the string data.xml.
(2) A this.style.display='none'; style command zeroes out the link (reduces its area on the page to 0).
(3) return false; suppresses the link's default action (going to the data.xml page via the href attribute).

The ajaxRead( ) function creates a new XMLHttpRequest object and then uses that object to connect to the data.xml document. Here's the XMLHttpRequest constructor code:

<script type="text/javascript"><!--
function ajaxRead(file) {
    var xmlObj = null;
    if (window.XMLHttpRequest) {
        xmlObj = new XMLHttpRequest( ); }
    else if (window.ActiveXObject) {
        xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); }
    else { return; }


XMLHttpRequest's Netscape/Mozilla support goes back to Netscape 6.x (from testing in the SheepShaver environment, I can verify that Netscape 6.2.3 supports it); its Microsoft support began with IE 7. However, the XMLHttpRequest "interface" - the package of properties and methods that is available to the XMLHttpRequest object - was actually first implemented by Microsoft in IE 5 for Windows. Microsoft called the object XMLHTTP back then and instantiated that object via the ActiveX framework as shown in the above code's else if clause - ActiveX is a can of worms we're not going to open but you can visit Microsoft's current ActiveXObject Object page here.

It follows from our discussion earlier that for most browsers, the code's if clause is operative: a new XMLHttpRequest object is created and given an xmlObj identifier.

Regarding the if condition, I'm not quite sure what the window.XMLHttpRequest expression represents; the definition offered at Microsoft's XMLHttpRequest Property page - Instantiates the XMLHttpRequest object for the window - seems dodgy, although a commenter yecril says, The description is incorrect ... [window.XMLHttpRequest] contains a reference to an XML HTTP Request Factory for HTML, whatever that means. Meanwhile, Mozilla doesn't list an XMLHttpRequest property in its DOM window Reference. In any case, a probe of the window.XMLHttpRequest return value reveals it to be browser-dependent - for example, it's [object XMLHttpRequest] with Firefox but it's function XMLHttpRequest() { [native code] } with Opera - typeof-wise, window.XMLHttpRequest is reported to be a function by the browsers on my computer excepting Safari, which says it's an object. But of course, what really matters is that the window.XMLHttpRequest condition converts to true for browsers that support the XMLHttpRequest object and to false for those that don't.

For any IE 5-6 for Windows users out there, the else if clause is operative: those guys correspondingly get a new XMLHTTP object, again with an xmlObj identifier and having access to the same basic properties and methods that an XMLHttpRequest object has. BTW, with IE 5.2.3 for Mac OS X - the final version of Internet Explorer for the Macintosh - window.ActiveXObject returns function ActiveXObject() { [native code] } and thus converts to true as an if condition but the xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); line throws an Object doesn't support this action runtime error.

Finally, users without XMLHttpRequest/XMLHTTP support will exit the ajaxRead( ) function via the return; statement of the code's concluding else clause.

After the constructor block we have an xmlObj.onreadystatechange = function( ) { ... } function expression that we'll go through in detail later, but what actually happens next, execution-wise, is the call to the XMLHttpRequest open( ) method that follows the expression:

xmlObj.open("GET", file, true);

Contra the tutorial, the admittedly poorly named open( ) method does not open a connection to a server nor does it request a file; it merely initializes (sets values for) a request to a server. The open( ) method can take as many as five parameters: the first two are required, the latter three are optional.

(1) The first open( ) parameter sets the HTTP request method we'll be using: in this case, we want to GET our hands on a resource. Mozilla recommends that the parameter value be written in all-uppercase form, although I find that get works OK when using Firefox.

(2) The second open( ) parameter sets the request URL: in this case, we want to connect to the file=data.xml resource. For security reasons, the requested resource and the calling document must be in the same domain, that is, you can't use the open( ) method to link from 1.com/page.html to 2.com/page.xml.

(3) The third open( ) parameter is a boolean switch for specifying an asynchronous or synchronous request: true (the default) gives you the former and false gives you the latter - the preceding command's true argument can thus be removed but there's no harm in leaving it there. Curiously, the author alleges that synchronous won’t work in this case, and I couldn't think of why that might be; in the event, toggling the command's true argument to false slows down the document modification effect a bit when using Safari, Opera, and Chrome but does choke off the effect with Mozilla-based browsers (Firefox, Camino, Netscape 9) for reasons beyond my understanding.

The other two open( ) parameters relate to user authentication - there's no need for us to discuss them, so we won't.

We are at long last ready to send off our request for the data.xml document, an action appropriately carried out by a subsequent call to the XMLHttpRequest send( ) method:

xmlObj.send("");

For a GET request, the send( ) method shouldn't have an argument at all - see the Example on the aforelinked Microsoft window.XMLHttpRequest page - and xmlObj.send( ); works A-OK with Firefox, Safari, Opera, and Chrome. However, with older Mozilla-based browsers and also with Camino, xmlObj.send( ); throws a Not enough arguments error; per Mozilla's practice, I find that the error is reliably preempted by equipping send( ) with a null argument. I don't know where the author's use of an empty string literal for the send( ) argument comes from - Apple uses this syntax but doesn't comment on it - it quashes the Not enough arguments error with Camino and Netscape 9 but a Could not convert JavaScript argument arg 0 [nsIXMLHttpRequest.send] error was thrown when I tried it with Netscape 6.2.3.

We'll track and process the server's response in the following entry.

reptile7

Monday, May 09, 2011
 
Checking via Focus Transmission
Blog Entry #214

When you encounter a set of checkboxes or radio buttons on a Web page






do you find it difficult to check an individual checkbox or radio button, and do you alternatively expect, from experience with other programs, to be able to check it by clicking on the text to its right? Would it disappoint you if you couldn't check a checkbox or radio button by clicking on its adjoining text? Conversely, does the ability to check a checkbox or radio button by clicking on its adjoining text make your user experience more satisfying and enjoyable? Maybe I need to get out more. Alleging that many users answer yes to the above questions, the author of HTML Goodies' "JavaScript and HTML Tricks" tutorial devotes to this matter the tutorial's fourth section, Using Labels to Create Checkboxes and Radio Buttons With Clickable Descriptions.

The clickable description concept is illustrated via the same login form that accompanies the preceding tutorial section and whose embellishment with a fieldset we discussed in detail in the previous post. Control-wise, the login form
(a-b) begins with User name and Password fields,
(c-e) subsequently presents a menu of three Security radio buttons, and
(f) concludes with a submit button.

Please enter your Yahoo! Login Information
User name
Password
Security

You'll notice in the form above that one can click on the descriptions of the security options in order to choose the desired option, the Using Labels ... section begins. Actually, I didn't notice that (at first), but upon checking it out I can report that the clickable description effect holds for almost all of the GUI browsers on my computer in both the OS X and SheepShaver environments - pre-Gecko versions of Netscape were the exceptions.

The effect initially seemed fishy to me; more specifically, I thought, "This must be another example of default browser behavior but not default HTML behavior à la the 'Submit the Form Using Enter' effect." But a careful reading of the relevant sections in the HTML 4.01 Specification reveals that this is standard behavior, or can be interpreted as such. To see why this is so, let's take a close look at the radio button/description code:

<input type="radio" name="secure" value="home" id="secure_home" />
<label for="secure_home">Remember my user name and password on this computer</label><br />
<input type="radio" name="secure" value="group" id="secure_group" checked="checked" />
<label for="secure_group">Remember my user name only</label><br />
<input type="radio" name="secure" value="public" id="secure_public" />
<label for="secure_public">Do not remember my user name or password</label>


The 'magic ingredient' that enables the effect is the HTML label element, which oddly gets no mention/credit in the Using Labels ... section. Each radio button input element is explicitly associated with a label element holding the button description; this association is achieved by setting the id attribute of the input element and the for attribute of the label element to the same value (secure_home, secure_group, and secure_public, respectively, for the three radio buttons). Clicking on label element text imparts focus to the label element. Towards the end of its label element documentation, the W3C states:
When a LABEL element receives focus, it passes the focus on to its associated control. See the section below on access keys for examples.
Moving to the Access keys section, we find:
The action that occurs when an element receives focus depends on the element. ... When a user activates a radio button, the user agent changes the value of the radio button. When the user activates a text field, it allows input, etc.
As Thomas Dolby might say, "Quod erat demonstrandum, baby."

Here's the author's take on the code:
[E]ach radio button must be labeled with an HTML ID. The clickable description has an attribute for="HTML ID" which allows the browser to understand that the radio button should be selected when the user clicks the description.
This is fair enough if the input elements and label elements are explicitly associated; subtracting the id attributes and for attributes from the above code will definitely kill the effect (as will subtracting the label markup altogether). However, we have seen previously that an input element and a label element can be associated implicitly via placing the input element in the content of the label element:

<label><input type="radio" name="secure" value="home" />
Remember my user name and password on this computer</label>
<!-- The radio buttons at the top of the post are implicitly labeled, as are the (first seven) controls of the "Dummy Form" cited below. -->


The effect also works with this arrangement, which does not require the label elements and input elements to have for attributes and id attributes, respectively.

You can see how the effect works with other types of controls by clicking on the Input #[1-7] labels in the "Dummy Form" of HTML Goodies' "How to Populate Fields from New Windows Using JavaScript" tutorial.

Image bullets

The last tutorial section, Using List-Style-Image to Make Beautiful Bulleted Lists, doesn't deserve its own entry, so let's deal with it here and now. This section briefly discusses the use of the CSS list-style-image property to replace the disc list item markers of unordered lists (disc being the initial value of the CSS list-style-type property) with small images, a topic we previously addressed in Blog Entry #97. Unfortunately, the author's list-style-image demo, which attempts to mark the items of a class="orangearrow" unordered list

<ul class="orangearrow">
  <li>Meat</li>
  <li>Potatoes</li>
  <li>Water</li>
</ul>


with a 0067_circular_arrow.png image

ul.orangearrow li {
    list-style-image: url(http://0--0.us/0067_circular_arrow.png); }


works at neither the HTML Goodies version nor the WebReference version of the tutorial: the orangearrow list items have disc markers as the 0067_circular_arrow.png image is not available.

I would tell you that the http://0--0.us/0067_circular_arrow.png link is dead, but bizarrely there is no trace of the 0--0.us domain on the Web; moreover, an Internet Archive search for http://0--0.us turns up nothing. But a Google search for the image itself - specifically, for "0067_circular_arrow.png", including the quotes - hits pay dirt, returning a number of pages that either (a) feature the image as part of a collection of images offered for download (e.g., here) or (b) use the image in some way (not necessarily as a list item marker), allowing your humble narrator to step into the breach and show you what the orangearrow list should really look like:
  • Meat
  • Potatoes
  • Water
Worth the wait, huh?

Additional comments
This is done by selecting the li elements contained in the unordered lists and specifying a URL corresponding to the list-style-image attribute.
In fact, the style rule's li descendant selector is unnecessary - as shown in the W3C's documentation, list-style-image can be set at the ul level:

ul.orangearrow {
    list-style-image: url(image_path/0067_circular_arrow.png); }

The best thing about list-style-image is that style sheets already provide this new design tool, making complicated graphic design work unnecessary.
The list-style-image property goes back to CSS Level 1 and has indeed been around for a while. As for it making complicated graphic design work unnecessary, well, this is certainly true if you avail yourself of other people's images.

Next up in the Beyond HTML : JavaScript sector is a codeless "Book Review: Head First JavaScript", which I think we can skip. But the tutorial after that, "How to Develop Web Applications with Ajax: Part 1", offers us something new - an introduction to AJAX - and we'll take it on in the following entry.

reptile7


Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)