views:

38

answers:

1

Hello Everyone,

I've run into a problem that I haven't been able to find a solution to yet.

I'm creating a resource booking application which uses AJAX to process information given by the user into a back-end database. Whenever the user wants to create a booking they fill out a form with relavent information which gets sent via AJAX to a servlet that processes the information and updates the database. Because we have included a booking recurrence option sometimes the database can take a while to be updated. As such we have implemented a "processing..." graphic to be displayed while the servlet does it's thing.

The way we have implemented the processing graphic and ajax calls has worked fine in firefox, opera and ie, the problem is with safari and chrome... so probably something to do with WebKit.

In Safari/Chrome the processing graphic doesn't show up on the page until after the database has completed its work and the ajax call returns... defeating the entire purpose of a "processing..." graphic.

Does anyone know why the graphic (which is inserted before the AJAX request gets called) doesn't show up until after the ajax call returns?

Here is how we have implemented the graphic and ajax calls. This is the function that calls both the function to create the graphic and to send out the ajax request:

/**
 * Implements AJAX calls to add/remove bookings from the database
 */
function processBooking(selection, responseID, formElement, removing, fromManager, resourceType) {

 if (!removing) {
 purpose = formElement.purpose.value;
 email = formElement.email.value;

            /* These values should only be set if the user is an admin otherwise they do not exist so send in the default empty
             * values to the processing servlet
             */
            if (formElement.repeatEveryUnits != undefined && formElement.repeatFor != undefined && formElement.repeatForUnits != undefined)
            {
                repeatEvery = formElement.repeatEveryUnits.value;
                repeatForAmount = formElement.repeatFor.value;
                if (repeatForAmount == '') {
                        repeatForAmount = '0';
                }
                repeatForUnit = formElement.repeatForUnits.value;
            }
            else
            {
                repeatEvery = '0';
                repeatForAmount = '0';
                repeatForUnit = '0';
            }

 }

    /* Function to be passed in as the callback to our asynchronous request */
    callback = function() {
        document.getElementById(responseID).innerHTML += '<p class="button-small" onclick="removeDiv(\'' + responseID + '\')>Clear</p>';
        document.getElementById(responseID).style.padding = '0 0 5px 0';
    }

    /* Parameters that are to be used in our asynchronous request */
    postData += parseArray(selection);
    postData += '&removing=' + removing;
    postData += '&availability=false';
    postData += '&type=' + resourceType;

    /* Play the loading graphic */
    startLoading();

    /* Make the call to the servlet */
    response = sendAsyncRequest(url,postData,callback,false);

    /* reset global variables used */
    reset();

    if ((!removing) || (!fromManager))
    {
        week = document.getElementById("selectedWeek").value;

        window.location = "../../servlet/ResourceBooking?action=schedule&type=" + resourceType + "&week=" + week;
    }

    return response;
 }

This is the code that inserts the graphic:

/**
 *      Begins a loading animation that is to be played while the database does its work 
 */
function startLoading() {

    document.getElementById("container").style.opacity = "0.2";
    document.getElementById("cover").style.display = "block";
    var newDiv = document.createElement("div");

    newDiv.setAttribute("id", "loading");
    newDiv.style.height = 120 + "px";
    newDiv.style.width = 350 + "px";

    newDiv.innerHTML = "<div id='progress'><p id='progress'><img id='progress_image' src='/intranet/images/ajax-loader.gif' alt=''><br/><h3>Processing database...</h3></p></div>";
    document.body.appendChild(newDiv);

    ignoreClicks = true;
 }

And this is the code that makes the XMLHttpRequest for the AJAX call:

function sendAsyncRequest(servletUrl, postData, callback, async) {

try {
      asyncRequest = new XMLHttpRequest();
}
catch (e) {
        // Internet Explorer Browsers
        try {
                asyncRequest = new ActiveXObject("Msxml2.XMLHTTP");
        } catch(e) {
                try {
                asyncRequest = new ActiveXObject("Microsoft.XMLHTTP");
                } catch(e){
                        // Something went wrong
                        alert('Request Failed');
                        return false;
                }
        }
}

asyncRequest.onreadystatechange = function() {
    if (asyncRequest.readyState == 4 && asyncRequest.status == 200) {
                try {
                                callback();
                }
                catch (e) {
                         // IE fails unless we wrap the string in another element.
                        var childDiv = current_element.getElementsByTagName("div");
                        if (childDiv.length == 0) {
                                var wrappingDiv = document.createElement('div');
                                //wrappingDiv.setAttribute("id", current_element.id+"Div");
                                wrappingDiv.innerHTML = asyncRequest.responseText;
                                current_element.appendChild(wrappingDiv);
                        }
                        else {
                                var wrappingDiv = document.createElement('div');
                                //wrappingDiv.setAttribute("id", current_element.id+"Div");
                                wrappingDiv.innerHTML = asyncRequest.responseText;
                                current_element.replaceChild(childDiv[0], wrappingDiv);
                                //childDiv[0].innerHTML = asyncRequest.responseText;
                        }
                }
        }
};

asyncRequest.open('POST', servletUrl, async);

if (postData == null)
{
    asyncRequest.send(null);
}
else
{
    asyncRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    asyncRequest.setRequestHeader("Content-length", postData.length);
    asyncRequest.setRequestHeader("Connection", "close");
    asyncRequest.send(postData);
}

return response;
}

Is there anything about WebKit that would prevent the startLoading div to be dynamically added to the page before the request is made?

I have tried commenting out the request itself ( sendAsyncRequest(...) ) after we load the graphic and it the graphic will appear perfectly fine, so it seems like something is happening to it when we follow it up with the request.

Any help is appreciated, I'm at a loss...

Kristen

A: 

You could set a callback for the image load event and then only execute the ajax POST when it has loaded. Like so...

function processBooking(selection, responseID, formElement, removing, fromManager, resourceType) {
    //...some stuff
    startLoading(function(){
        sendAsyncRequest(url,postData,callback,false);
    });
    //...some more stuff
}

function startLoading(callback) {
    document.getElementById("container").style.opacity = "0.2";
    document.getElementById("cover").style.display = "block";
    var img = new Image();
    img.onload=callback;
    img.src="/intranet/images/ajax-loader.gif";
    var newDiv = document.createElement("div");

    newDiv.setAttribute("id", "loading");
    newDiv.style.height = 120 + "px";
    newDiv.style.width = 350 + "px";

    newDiv.innerHTML = "<div id='progress'><p id='progress'><img id='progress_image'     src='/intranet/images/ajax-loader.gif' alt=''><br/><h3>Processing database...</h3></p>     </div>";
    document.body.appendChild(newDiv);

    ignoreClicks = true;
 }
pixl coder
Thanks for the response pixl I will definitely give it a try!
Kristen D.