Waiting for JavaScript Events
First, let me state that I am a relative noob at working with JavaScript—and to be blunt about it, event-driven programming gives me a headache (on top of having to work with JavaScript at all, that is).
My most recent challenge was how to fit event handling into an object-oriented design approach; I wanted to load an image from a new URL (e.g. it isn’t already embedded within the HTML page), then get the loaded image’s dimensions for use elsewhere in a larger application.
The problem is not as obvious as it seems at first: I could attach an action to the image’s load event for execution when the image finishes loading, but by then my method will have already returned (with no result—because the image won’t have finished loading yet). Virtually all of the examples I found while looking for answers took this approach, and did little more than display an alert dialog with the results—which is virtually useless in the context of a larger application, since getting the event was never the problem, but rather getting the information back mid-process somewhere else.
I can’t use a while statement in my method to wait until the load event returns, either, because JavaScript runs in a single thread—we have a race condition because the event won’t fire until the loop exits, and the loop won’t exit until the event fires. I obviously can’t let the method return without the dimensions to return, either, as that’s the point of the exercise.
I was fairly sure one of the two client-side timing functions (setTimeout() and setInterval()) were the answer, but it took me a little longer to finally divine a solution. In brief, it looks something like this:
function getImageDimensions(image_url) {
myimg = new Image();
myimg.src = image_url;
var _timer = setInterval(function() {
if (myimg.complete) {
clearInterval(_timer);
return {'width': myimg.width, 'height': myimg.height};
}
}, 10);
}
The important bit is the _timer function, which itself schedules an anonymous function containing a single if loop. In this case, the timer runs every 0.01 seconds to check if the image has finished loading yet (via the complete property of the Image object). If the statement evaluates as false, the function tries again after the specified interval—without returning—until the image loads and the myimg.complete condition is met.
At that point, the example above returns dimension properties in an object, but your code might instead return another result, such as that of a private method that tests whether the dimensions fit inside defined minimum or maximum dimensions.
I realise that this code is probably useless for most applications (since it is without a doubt easier to not only verify image dimensions—but also do something constructive about it—on the back end compared to the front), but I ran into the need to do so myself and have seen others facing similar dilemmas. I hope this simple pattern helps save someone the headache I’ve endured trying to tease this solution into existence. For those concerned about such things, the code above was tested with and observed to work in Firefox 2, MSIE 6 and 7, and Konqueror 3.5.7.