| home / programming / javascript / domwrapper | [previous] [next] |
|
readyState Attribute and onreadystatechange EventAs mentioned earlier, there is a load() method on the DOM Document in both IE and
Mozilla that allows the developer to load an XML file directly into the DOM Document. Naturally,
there also must be a way to determine when the XML file has been fully loaded. As usual, IE and
Mozilla take different approaches to this.
In IE, the DOM Document has a readyState attribute. The readyState
attribute is an integer value indicating what the current state of loading is. These values are:
| Code | String | Description |
| 0 | uninitialized | The DOM Document has been created, but the load() method has not yet been called. |
| 1 | loading | The load() method has been called and is executing. |
| 2 | loaded | Loading is complete, the DOM Document is now parsing the file. |
| 3 | interactive | Some of the XML data has been parsed, the DOM Document is now available, but is incomplete and read-only. |
| 4 | completed | The XML data has been fully parsed and the DOM Document is available in its entirety. |
In addition, IE also provides a readystatechange event that can have an event handler
assigned to it. Every time the readyState attribute changes, the readystatechange
event fires. So, if you assign a function to the onreadystatechange event handler, you can
easily see if the DOM Document is done loading.
Switching back to Mozilla for a moment, their approach is totally different, if not a bit more
logical. Mozilla fires a load event when the DOM Document is done loading. So, by assigning
the onload event handler, you can be alerted when the DOM Document is completely loaded.
This differs from IE's method in that there are not various states of loading: the DOM Document is
either loaded or it is not, and there is no attribute to tell you this. Once again, it is our job to
tame Mozilla to behave more like IE.
First, we will add the readyState attribute to the Document class and
initialize it to 0:
Document.prototype.readyState = "0";
Now comes the interesting challenge. There are five possible values of readyState. We
know that we can simulate the first, 0, because nothing has happened. Which of the others
can we simulate? Looking it over quickly, two of the remaining four are doable.
If we can listen in on the load() method, we can set the readyState value
to 1 just before it executes. Also, when the Mozilla load event is fired,
we can change the readyState to 4. For 2 and 3
there's just no way of knowing this information at the present time.
Let's begin with the task of "listening in" on the load() method. Of course, there is
no way to actually do that. What we need to do is save the original load() method into
another variable, then define our own load() method that changes the readyState
attribute and then calls the original method. Sound confusing? Let's break it down:
First, save a reference to the current load() method into a "hidden"
__load__ attribute:
Document.prototype.__load__ = Document.prototype.load;
Next, set the load() method to point to our own function:
Document.prototype.load = _Document_load;
Now, when someone calls load() on the Document, it will call our _Document_load()
function. So let's define that function:
function _Document_load(strURL) {
//change the readyState
this.readyState = 1;
//call the original load method
this.__load__(strURL);
}
Now, when the load() method is called, the readyState will accurately change
to 1. So how about when the XML has been fully loaded? This is a little bit different.
In order to change the readyState attribute to 4, we need to add an
event listener to the Document's load event. This can't be done until the Document is
instantiated, which means that this must be done back in the createDOMDocument() function:
//determine if this is a standards-compliant browser like Mozilla
if (isMoz) {
//create the DOM Document the standards way
objDOM = document.implementation.createDocument(strNamespaceURI, strRootTagName, null);
//add the event listener for the load event
objDOM.addEventListener("load", _Document_onload, false);
}
By adding this line, when the XML data has finished being loaded, it will call our
_Document_onload() function, which is as follows:
function _Document_onload() {
//change the readyState
this.readyState = 4;
}
We can't forget about our loadXML() method. The readyState attribute also
changes when it is called, so let's update our code:
//add the loadXML() method to the Document class
Document.prototype.loadXML = function(strXML) {
//change the readystate
this.readyState = 1;
//create a DOMParser
var objDOMParser = new DOMParser();
//create new document from string
var objDoc = objDOMParser.parseFromString(strXML, "text/xml");
//make sure to remove all nodes from the document
while (this.hasChildNodes())
this.removeChild(this.lastChild);
//add the nodes from the new document
for (var i=0; i < objDoc.childNodes.length; i++) {
//import the node
var objImportedNode = this.importNode(objDoc.childNodes[i], true);
//append the child to the current document
this.appendChild(objImportedNode);
} //End: for
//change the readystate
this.readyState = 4;
} //End: function
Congratulations, we've now completed our exercise in mimicking the IE readyState
attribute. Our next task: mimic the readystatechange event.
This step is actually not all that difficult. Let's assume someone wants to assign an
event handler for the readystatechange event. They would do it without using IE's
attachEvent() method or Mozilla's addEventListener() method. In short,
they would use the old-fashioned way of assigning an event handler, as:
objDoc.onreadystatechange = myFunction;
If this is the case, then we can make an onreadystatechange attribute in Mozilla's
Document class, and then just call it each time we change the readyState attribute. So,
let's start by adding the attribute:
Document.prototype.onreadystatechange = null;
Next, we need to go back to the places we changed the readyState attribute and call the
onreadystatechange function (if there is one). Since this may be done in several areas,
we'll make a changeReadyState() function to encapsulate the functionality. The
changeReadyState() function will take two parameters: the Document object to change and
the value to set the readyState attribute to. Here's the code for this function:
function changeReadyState(objDOMDocument, iReadyState) {
//change the readyState
objDOMDocument.readyState = iReadyState;
//if there is an onreadystatechange event handler, run it
if (objDOMDocument.onreadystatechange != null && typeof objDOMDocument.onreadystatechange == "function")
objDOMDocument.onreadystatechange();
}
Now, let's go and put this into the load() method and load event handler:
function _Document_load(strURL) {
//change the readyState
changeReadyState(this, 1);
//call the original load method
this.__load__(strURL);
}
function _Document_onload() {
//change the readyState
changeReadyState(this, 4);
}
We also need to update the loadXML() method that we wrote for the
Document class:
//add the loadXML() method to the Document class
Document.prototype.loadXML = function(strXML) {
//change the readystate
changeReadyState(this, 1);
//create a DOMParser
var objDOMParser = new DOMParser();
//create new document from string
var objDoc = objDOMParser.parseFromString(strXML, "text/xml");
//make sure to remove all nodes from the document
while (this.hasChildNodes())
this.removeChild(this.lastChild);
//add the nodes from the new document
for (var i=0; i < objDoc.childNodes.length; i++) {
//import the node
var objImportedNode = this.importNode(objDoc.childNodes[i], true);
//append the child to the current document
this.appendChild(objImportedNode);
} //End: for
//change the readystate
changeReadyState(this, 4);
} //End: function
With this in place, a developer has a common way to determine if a file has been loaded. When the
file is finished loading, the onreadystatechange event handler is called. Within that
function, you can check the value of readyState. If it is 4, then you know
the file has been loaded and you are ready to roll.
The DOM standards support both synchronous and asynchronous loading of external XML files. Both the
IE ActiveX version and the Mozilla native version support the async property on a DOM
Document, such as the following:
objDOMDocument.async = true;
If async is set to false, the external XML file begins to load, but control is returned
to the next line of JavaScript before the file has finished loading. If async is set to true,
then control is not returned to the next line of JavaScript until the file has finished loading.
This is usually set just before loading a file, such as:
objDOMDocument.async = true;
objDOMDocument.load("myfile.xml");
| home / programming / javascript / domwrapper | [previous] [next] |
Created: June 13, 2002
Revised: June 13, 2002
URL: http://webreference.com/programming/javascript/domwrapper/4.html