gusmueller
Thursday, February 24th, 2005

I've got a little bit 'o JavaScript code to share tonight.

People have been talking about
XMLHttpRequest object alot lately, since Google managed to legitimize it for everyone with gmail and their new online maps. Anyway, if you didn't know already, this little bit of code allows you to do requests asynchronously back to the webserver which you then take and update your web interface with. The requests seem to be a bit faster because I think the browser has to do less UI updating, and the requests are usually smaller.

A bunch of tutorials have been going up, but for some reason they keep on not using my two favorite tricks when making the requests. But that's ok, I'll just share them with you right now. (Maybe that's because they are bad ideas? They seem to work for me anyway :))

All the examples I've seen, have taken a newly created XMLHttpRequest object, and assigned it's onReadyStateChange handler to a general function to check it's state and do whatever. It basicly looks like this (stealing from Apple's sample code):

var req;

function processReqChange() {
    // only if req shows "loaded"
    if (req.readyState == 4) {
        // only if "OK"
        if (req.status == 200) {
            // ...processing statements go here...
        } else {
            alert("There was a problem retrieving the XML data:\n" + req.statusText);
        }
    }
}

// this is called but a button or link or whatever
function doSomething() {
    // do our browser specific branching in getXMLHttpRequest()
    req = getXMLHttpRequest();
    req.onreadystatechange = processReqChange;
    req.open("GET", url, true);
    req.send(null);
}


But what happens if we want to have two different requests going on at the same time? Uh oh. Our pointer to req is going to get dereferenced on the 2nd request. How are we going to know what happened to the first request? That's where anonymous functions come in.

function doSomething() {
    // do our browser specific branching in getXMLHttpRequest()
    var req = getXMLHttpRequest();
    req.open("GET", url, true);

    req.onreadystatechange = function() {
        // only if req shows "loaded"
        if (req.readyState == 4) {
            // only if "OK"
            if (req.status == 200) {
                // ...processing statements go here...
            } else {
                alert("There was a problem retrieving the XML data:\n" + req.statusText);
            }
        }
    }

    req.send(null);
}


Tada! It's one less function and no global vars!

The other trick has to do with JavaScript's eval. Sure, you could process all that XML or whatever, throw in some while loops and branch based on conditions or whatever. Or you just return javascript instead of XML and do this:

function doSomething() {
    // do our browser specific branching in getXMLHttpRequest()
    var req = getXMLHttpRequest();
    req.open("GET", url, true);
    req.onreadystatechange = function () {
        if (req.readyState == 4) {
            if (req.status == 200) {
                eval(req.responseText);
            } else {
                alert("There was a problem retrieving the XML data:\n" + req.statusText);
            }
        }
    }
    req.send(null);
}


Just make sure that your response sends a content type of text/xml even if it isn't.

Anyway, just a couple of techniques to use if you're doing this kind of work.

One other note while I'm thinking about it... Some things that seem like a great idea to use XMLHttpRequest for turn out to not be so great in practice. Here's an example:

I took a wiki I wrote a little while back and modified it so that when you clicked on a link to an entry, instead of refreshing the whole page it just updated the main content of the page + a title field. It worked great and it was pretty fast as well. Until I wanted to go back a page. Without thinking I just hit the back button on the browser, and that didn't do what I wanted because there was no history to go back to. And I don't think there is any way to fix that... Oh well.

Just something to think about. New tech sometimes brings new usability issues to deal with.

(And later... a little update on the code to make the req variable local to the function, pointed out to me by Jeff Watkins in the comments. What? You thought I actually tested this code?)

comments (4)   # posted 9:28 pm (uct-6)


So, Kirstin has left me and I'm moving back in to my Mom's house.

...

Er, wait. That sounds bad. Let me try that again.

Kirstin is at a PT conference in New Orleans, and we're getting the hardwood floors redone next week. So everything has to move to the basement and I've got to find somewhere else to sleep for a little while.

There, that sounds better. Just a little update of what's going on in Augieville.

That and I just turned in my Vacation time for June 6-10 this summer, so I guess I am going to WWDC. Only 100 days to go!

And more randomness: It looks like I'm going to loose the "
When is Tiger going to ship?" bet in about 4 days. Something tells me Brent is going to be closest with his wager of June 7th, the day after WWDC starts.

I can't wait for the NDA to be lifted, there's sooo much cool stuff in Tiger I'm just dying to talk about.

comments (4)   # posted 1:27 pm (uct-6)