This is Steve Meredith again. Last time, I wrote about XML Data Island support in IE Mobile. The <xml> element is an IE-only extension. Most web authors prefer Ajax these days. This time, I want to change the sample page we developed last time to use XMLHTTP instead of the <xml> element. If you haven’t already, please go read the Data Islands article first.
As a reminder, we are creating a web page that lets you select a food from two dropdown lists. The first list contains a category of food: fruit, vegetables, or nuts. The second list contains foods appropriate to the currently selected category. So if “fruit” is selected, the second list contains kinds of fruit. If “vegetables” is selected, the second list contains kinds of vegetables. We don’t want the browser to request a new page from the server each time the category is changed.
Below is the HTML that used the <xml> element. Again, I have left out the bodies of the two script functions.
<html>
<head>
<title>IE Mobile XML Data Island Example</title>
</head>
<body onload='loadCategoryList(); loadFoodList()'>
<!-- xml data island: 3 categories for dropdown 1, each with different foods for dropdown 2. -->
<xml id='myFoods'>
<categories>
<category name='Fruit'>
<food>Apples</food>
<food>Bananas</food>
<food>Strawberries</food>
</category>
<category name='Vegetables'>
<food>Peas</food>
<food>Carrots</food>
<food>Corn</food>
</category>
<category name='Nuts'>
<food>Almonds</food>
<food>Walnuts</food>
<food>Pecans</food>
<food>Pistachios</food>
</category>
</categories>
</xml>
<script type="text/javascript">
function loadCategoryList() {}
function loadFoodList() {}
</script>
<hr />
Select from the available foods:
<form id='myForm'>
<select id='categoryList' onchange='loadFoodList()'></select>
<select id='foodList'></select>
</form>
</body>
</html>
Let’s talk about what it will take to change this to an XMLHTTP sample.
- First, we remove the <xml> element from the HTML.
- We need a function to request the XML from server. Let’s call it requestFoodListFromServer().
- We need a function that handles the response from that request. Let’s call it processFoodListResponse().
- We need to change what function gets called in the body’s onload handler.
- We need a global to hold the XMLHTTP request object.
- We need another global to hold and XML DOM object with the results from the server.
Our new HTML page looks like this. We’ll fill in the bodies of the functions later.
<html>
<head>
<title>IE Mobile XMLHTTP Example</title>
</head>
<body onload='requestFoodListFromServer()'> <!-- Step 4 -->
<script type="text/javascript">
var g_request; // XMLHTTP request object Step 5
var g_foodXML; // XML document object Step 6
function loadCategoryList() {}
function loadFoodList() {}
function requestFoodListFromServer() {} // Step 2
function processFoodListResponse() {} // Step 3
</script>
Select from the available foods:
<form id='myForm'>
<select id='categoryList' onchange='loadFoodList()'></select>
<select id='foodList'></select>
</form>
</body>
</html>
Let’s fill in the body of requestFoodListFromServer() first.
// Make an HTTP request for the food list.
function requestFoodListFromServer()
{
// Cancel any outstanding requests.
if (g_request)
{
g_request.abort();
}
// Make a new XMLHTTP request object.
g_request = new ActiveXObject("Msxml2.XMLHTTP");
if (g_request)
{
// Call processResponse() when response is received.
g_request.onreadystatechange = processFoodListResponse;
// Make an asynchronous request.
g_request.open("GET", "http://iemobile.members.winisp.net/XMLHTTP/myFoods.xml", true);
// Send it.
g_request.send();
}
}
We create a new XMLHTTP request object. The way to do this in IE Mobile is with ActiveXObject(). “Msxml2.XMLHTTP” is the name to pass to this function. We set up the onreadystatechange handler, which is a function that gets called at various times as the request object is processed. Passing ‘true’ to open() as the 3rd parameter causes this process to be asynchronous.
Let’s move on to processFoodListResponse().
// Called when the http request is finished.
function processFoodListResponse()
{
// Done getting response.
if (g_request.readystate == 4)
{
// Success.
if (g_request.status == 200)
{
// Treat as a plain-text reply.
g_foodXML = new ActiveXObject("Msxml2.DOMDocument");
g_foodXML.loadXML(g_request.responseText);
// Load both listboxes.
loadCategoryList();
loadFoodList();
}
else
{
alert("Could not get requested food list from server.");
}
g_request = null;
}
}
There’s not much new here. We create a new XML DOM object instead of using the one on the XMLHTTP request object. We do this because we don’t control the content-type for this particular XML file. Normally, in a web app, we control the server and this won’t be necessary.
We have to make a small change to loadCategoryList(). We remove the reference the XML DOM attached to the myFoods <xml> element and replace it with a reference to our global XML DOM object, g_foodXML. Here’s the modified code:
// Initialize the first dropdown (categories) from the XML.
function loadCategoryList()
{
// Get all the categories.
categories = g_foodXML.getElementsByTagName('category');
for (g = 0; g < categories.length; g++)
{
// Add the category to the <select> element.
option = new Option();
option.text = categories[g].attributes.getNamedItem('name').text;
option.value = option.text;
document.all.categoryList.options.add(option);
}
}
Finally, we have to make the same change to loadFoodList(): replace the reference to the <xml> element’s XML DOM and replace it with g_foodXML. Here’s the modified code:
// Initialize the second dropdown (foods) from the XML based on
// selected category.
function loadFoodList()
{
// Find the name of the selected category in the 1st box.
selectedIndex = document.all.categoryList.options.selectedIndex;
categoryName = document.all.categoryList.options[selectedIndex].text;
// Search the XML for the selected category.
categories = g_foodXML.getElementsByTagName('category');
for (g = 0; g < categories.length; g++)
{
// Found the selected category.
if (categoryName == categories[g].attributes.getNamedItem('name').text)
{
// Delete any current option elements.
document.all.foodList.length = 0;
// Fill the select element with food from this category.
foods = categories[g].getElementsByTagName('food');
for (i = 0; i < foods.length; i++)
{
option = new Option();
option.text = foods[i].text;
option.value = foods[i].text;
document.all.foodList.options.add(option);
}
}
}
}
That was easy. You can see that the XMLHTTP method requires a little more code than the XML Data Island approach. It also gets the XML data in a separate, asynchronous request so the rest of the UI can be display before the listboxes are initialized. This may be important in a mobile browsing scenario, depending on the size of the data set and speed of the network connection.
You can find the complete HTML source for this sample here: http://iemobile.members.winisp.net/XMLHTTP/sample.htm