| home / programming / javascript / rg / 1 | [previous] [next] |
|
|
3: OPTIONAL STEP: Generate the static XML file.
Use this option if you don't update your data too often. A good example of static data includes Countries, States/Provinces, and Cities. For such a large data set, you may also want to consider displaying a sub list which only contains the most common choices (IE: largest cities). Otherwise, you will be asking your visitors to download a lot of information each time they visit your site. Users with dialup connections may take issue with having to wait a couple of minutes to see the page!
4: Modify your ASP Page.
There are a few references that will have to be added to your asp page in order to make the listboxes work properly.
Include the following text in the Head section of the document:
<!--#INCLUDE
FILE="generateXML.inc" -->
<!--#INCLUDE FILE="createJsXmlStr.inc" -->
<META NAME="save" CONTENT="history">
<STYLE>
.saveHistory
{behavior:url(#default#savehistory);}
</STYLE>
<script language="Javascript1.2" src="URPM_III.js">
</script>
Add the following line to the <body> tag:
onLoad="initLists()"
OPTIONAL STEP: Add a hidden field with the default index for the base list. This number is zero-based, with the "0" element being the first blank entry. It must be in the same html form as the listboxes.
<input type=hidden name=defaultIndex value="3">
Add "class=saveHistory"
to the select tags of each URPM.
Also, make certain that each listbox has an ID of m1, m2, m3…starting with 1
for the base list. For example:
<select name="lstManufacturers" ID=m1 class=saveHistory>
Add the following line between the <select> tags of the base list. It will populate it using ASP:
<%=objXML.transformNode(objXSL)%>
Convert the "reset" button, if applicable, to a simple button and add the following code to the onClick() event. This is done so that the form resets before the lists.:
<input name="myButton" type=button value=reset onClick="this.form.reset(); resetLists();">
5: Copy the remaining files to your web directory.
ConvertRStoXML.xsl, generateXML.inc
And that's it. You're good to go.
The heart of the dynamic lists is the VBScript code in the generateXML.inc file. It takes the results of your query and converts it to XML to be used by the ASP page. The main function is getXML(), which returns an XML-formatted string. It does this by connecting to the database using the "URPMs" File DSN and running the "getLists" query. Other than these two objects, the generateXML.inc script is completely generic and should require no tweaking on your part. When called by the CreateJsXmlStr.inc script from within your ASP page, the XML is formatted into a JavaScript string, ready to be converted into an XML Document Object.
Where you'll likely need to make changes is in the URPM_III.js file because it ultimately controls the behaviour of the listboxes. To better understand where you might want to make modifications, here is a line by line explanation of the script.
The lists array holds references to the listboxes, as well as a sublists property, which holds each list's child list – much like a linked list.
var lists =
new Array();
lists.sublists = new Array();
Getting back to my automobile example, the lists array would hold 3 items:
lists[0] =
lstManufacturers
lists[1] = lstModels
lists[2] = lstLevels
The sublists property contains an associative array containing each list's related child list. An associative array uses a string to identify its elements:
lists.sublists["lstManufacturers"]
= lstModels
lists.sublists["lstModels"] = lstLevels
lists.sublists["lstLevels"] = "undefined" //no
more children
In a moment, we'll look at how these arrays are populated.
Next, we define some variables and do some very basic browser sniffing:
var xml;
var opera = (navigator.userAgent.toLowerCase().indexOf('opera') != -1);
var ie = (document.all && typeof(ActiveXObject) !=
"undefined" && !opera);
The initLists() function is the first to be
called. It is called from the BODY onLoad()
event. Its first order of business is to load the xml data into our
variable. The success of this function really depends on the browser supporting
ActiveX (Internet Explorer) or the DOMParser Object. We can test for the
DOMParser by using the typeof() function.
If the JavaScript interpreter doesn’t recognize the Object, it simply returns
an “undefined” string. Opera proved to be a really tricky customer, because
it spoofs IE in a number of ways. That made recognizing it very difficult.
Fortunately, it doesn't have a fake implementation of the DOMParser. In fact,
version 7.6 has a real one!:
// Load XML
if (ie)
{
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = false;
xml.loadXML(xmlString);
}
else
{
if (typeof(DOMParser) != "undefined")
{
xml = (new DOMParser()).parseFromString(xmlString,
"text/xml");
}
else
{
if (opera)
alert("Please upgrade to Opera 7.6.");
else
alert("Your browser does not implement the DOMParser
Object.");
return;
}
}
The xml.async = false line tells the scripting engine not to run asynchronously; we want the xml to be completely loaded before we continue execution of the script. We don’t need to do anything special for other browsers because the parseFromString() function always runs asynchronously. The opposite would have been to use synchronous loading, whereby the xml would be downloading at the same time as the script runs. Setting async to true could be dangerous because we can't use the xml until it has finished loading. Better play it safe!
You can find more information about this topic here: Synchronous vs. Asynchronous Processing Explained
Both the loadXML() and parseFromString() functions load the XML into the xml variable from a string. Don't bother looking for the xmlString variable declaration in the script. It was created by the createJsXmlStr.inc file:
Response.Write
"<Script Language=JavaScript>var xmlString = '" & _
Replace(Replace(objXML.xml,"'","\'"),vbCrLf,"") & "';</Script>"
If neither of these functions is supported, we display a message. Hopefully, we’ll never have to execute that code!
The other role of this function is to loop through each element of every form on the page, looking for elements whose className is "saveHistory" - that's the CSS class we created to remember the contents of the list when jumping between pages in Internet Explorer. To do this, I added a helper class and function:
var objIndexes =
new function() { this.formIndex = 0; this.eltIndex = 0; };
var list = getNextList(objIndexes);
You can tell that the first line is a class by the “new” keyword. The objIndexes object is a “holder” class because it holds related primitive variables to track form and element indexes. It allows us to pass both variables to the getNextList() function together by Reference. Without the class, the variables would not be updated by the function, and it would have to start looping from the top of the page every time we called getNextList().
When getNextList() finds a list, it is added to the lists array:
lists.push(list);
If we have already populated the first element, that means that we've now found a child list, so we proceed to store the sublists property for the previous element:
if (lists.length
> 1) //this won't execute the first time
through.
{
lists.sublists[lists[lists.length - 2].name] = list;
}
That variable assignment might look a little complicated, but it's actually a useful way to get at a list's child. Here's how it works, going from the inside out. The [lists.length - 2] expression gives us the parent element, stored in the lists array. For example, if we now have 2 lists stored, lists.length - 2 would equal 0, which is the first list. Now that we have the parent list, we can get it's name using the name property: lists[lists.length - 2].name. To create a sublist for this element we set it to that name: lists.sublists[lists[lists.length - 2].name]. This is where we'll store the child list.
Last but not least, we proceed to get the next list so that we know that there is another one, and then we attach a function to the onchange() event for the current element, using code that works in most browsers. It is crucial that we know that there is still at least one more list because we don’t want to attach the getSubList() function to the last child list. The single equals sign in the (list = getNextList(objIndexes)) line might look like a mistake, but it isn’t. The results of the function are assigned to the list variable using a single equals (=) sign which is then evaluated to be either true or false, or, more precisely, or not null(true) or null(false)! The inner parentheses are also essential for Mozilla to evaluate the expression correctly:
if ( (list =
getNextList(objIndexes)) )
{
//attach the event handler to the element
lists[lists.length - 1].onchange = function() { getSubList(this); };
}
The script expects the lists to be in order on the page, but not necessarily in immediate succession of each other. That is, the base list does not come after a child and children do not come before their parents. If your page does not display the URPMs in the order of parent, child, grandchild, etc, you will have to change this function to suit your purposes. I would recommend that you scrap the loop and simply assign the lists directly.
| home / programming / javascript / rg / 1 | [previous] [next] |
Created: March 27, 2003
Revised: June 2, 2005
URL: http://webreference.com/programming/javascript/rg/1