You must define the callback function to handle the server’s response, and add any functionality necessary to reflect changes in the page that is viewed by the user. This requires modifying the HTML DOM. Finally, you can work in the IDE’s CSS Editor to add a simple stylesheet to the presentation.
Notice that the parseMessages() function is called only when the XMLHttpRequest.readyState is “4” and the status — the HTTP status code definition of the request — is “200”, signifying a success. You will define parseMessages() next in Updating the HTML DOM .
A readyState of “4” signifies the completion of the HTTP interaction. The API for XMLHttpRequest.readState indicates that there are 5 possible values that can be set. These are:
The callback function is called asynchronously at specific points during HTTP interaction when the readyState property of the XMLHttpRequest object changes. In the application you are building, the callback function is callback() . You recall that in doCompletion() , callback was set as the XMLHttpRequest.onreadystatechange property to a function. Now, implement the callback function as follows.
Updating the HTML DOM
The parseMessages()
function handles the incoming XML data. In doing so, it relies on several ancillary functions, such as appendComposer()
, getElementY()
, and clearTable()
. You must also introduce new elements to the index page, such as a second HTML table which serves as the auto-complete box, and ID’s for elements so they can be referenced in javascript.js
. Finally, you create new variables corresponding to ID’s for elements in index.php
, initialize them in the init()
function that you previously implemented, and add some functionality that is needed each time index.php
is loaded.
The functions and elements that you create in the following steps work interdependently. It is recommended that you work through this section, then examine the code once it is all in place.
-
Open
index.html
in the editor and type in the below code for the second row of the HTML table you previously created.
<tr>
*<td id="auto-row" colspan="2">
<td/>*
</tr>
This new row, which can be identified as ‘auto-row’, serves as a handle for the JavaScript code in order to insert a new HTML table that will form the auto-complete box.
-
Open
javascript.js
in the editor and the following three variables to the top of the file.
var completeField;
var completeTable;
var autoRow;
-
Add the following lines (in bold) to the
init()
function.
function init() {
completeField = document.getElementById("complete-field");
*completeTable = document.createElement("table");
completeTable.setAttribute("class", "popupBox");
completeTable.setAttribute("style", "display: none");
autoRow = document.getElementById("auto-row");
autoRow.appendChild(completeTable);
completeTable.style.top = getElementY(autoRow) + "px";*
}
One purpose of init()
is to make elements inside index.html
accessible to other functions that will modify the index page’s DOM. Above, the script creates a new HTML table
, adds the popupBox
class and modifies the element’s style to display: none
. Finally, it gets the element whose id
is auto-row
and inserts the new table
into it. In other words, the modified HTML looks as follows when the code is run.
<tr>
<td id="auto-row" colspan="2">
*<table class="popupBox" style="display: none"></table>*
<td/>
</tr>
-
Add
appendComposer()
tojavascript.js
.
function appendComposer(firstName,lastName,composerId) {
var row;
var cell;
var linkElement;
if (isIE) {
completeTable.style.display = 'block';
row = completeTable.insertRow(completeTable.rows.length);
cell = row.insertCell(0);
} else {
completeTable.style.display = 'table';
row = document.createElement("tr");
cell = document.createElement("td");
row.appendChild(cell);
completeTable.appendChild(row);
}
cell.className = "popupCell";
linkElement = document.createElement("a");
linkElement.className = "popupItem";
linkElement.setAttribute("href", "autocomplete.php?action=lookup&id=" + composerId);
linkElement.appendChild(document.createTextNode(firstName + " " + lastName));
cell.appendChild(linkElement);
}
This function creates a new table row, inserts a link to a composer into it using the data passed to the function via its three parameters, and inserts the row into the index page’s complete-table
element.
-
Add
clearTable()
tojavascript.js
.
function clearTable() {
if (completeTable.getElementsByTagName("tr").length > 0) {
completeTable.style.display = 'none';
for (loop = completeTable.childNodes.length -1; loop >= 0 ; loop--) {
completeTable.removeChild(completeTable.childNodes[loop]);
}
}
}
This function sets the display of the complete-table
element to ‘none’, (i.e., makes it invisible), and it removes any existing composer name entries that were created.
-
Add
getElementY()
tojavascript.js
.
function getElementY(element){
var targetTop = 0;
if (element.offsetParent) {
while (element.offsetParent) {
targetTop += element.offsetTop;
element = element.offsetParent;
}
} else if (element.y) {
targetTop += element.y;
}
return targetTop;
}
This function is applied to find the vertical position of the parent element. This is necessary because the actual position of the element, when it is displayed, is often dependent on browser type and version. Note that the complete-table
element, when displayed containing composer names, is shifted to the lower right of the table in which it exists. The correct height positioning is determined by getElementY()
.
See this explanation of offset
on http://www.quirksmode.org/.
-
Modify the
callback()
function to callclearTable()
each time new data is received from the server. Any composer entries that already exist in the auto-complete box are therefore removed before it becomes populated with new entries.
function callback() {
*clearTable();*
if (req.readyState == 4) {
if (req.status == 200) {
parseMessages(req.responseXML);
}
}
}
-
Add
parseMessages()
tojavascript.js
.
function parseMessages(responseXML) {
// no matches returned
if (responseXML == null) {
return false;
} else {
var composers = responseXML.getElementsByTagName("composers")[0];
if (composers.childNodes.length > 0) {
completeTable.setAttribute("bordercolor", "black");
completeTable.setAttribute("border", "1");
for (loop = 0; loop < composers.childNodes.length; loop++) {
var composer = composers.childNodes[loop];
var firstName = composer.getElementsByTagName("firstName")[0];
var lastName = composer.getElementsByTagName("lastName")[0];
var composerId = composer.getElementsByTagName("id")[0];
appendComposer(firstName.childNodes[0].nodeValue,
lastName.childNodes[0].nodeValue,
composerId.childNodes[0].nodeValue);
}
}
}
}
The parseMessages()
function receives as a parameter an object representation of the XML document returned by the autocomplete.php
file. The function programmatically traverses the XML document, extracting the firstName
, lastName
, and id
of each entry, then passes this data to appendComposer()
. This results in a dynamic update to the contents of the complete-table
element. For example, an entry that is generated and inserted into complete-table
might look as follows:
<tr>
<td class="popupCell">
<a class="popupItem" href="autocomplete?action=lookup&id=12">Antonin Dvorak</a>
</td>
</tr>
The dynamic update to the complete-table
element represents the final step of the process flow of communication that takes place during communication using Ajax. This update maps to the HTML & CSS data being sent to the presentation in the flow diagram above.