Hope for the best but expect the worst from the users    

  AJAX In Action (4): Preloading Images

  Abstract

This article discusses the right way of preloading a large amount of alternating images. The problem may require an asynchronous approach by its nature. The demonstrated solution is a good example of situations where we can trigger a process synchronously, i.e., sequentially after an asynchronous process.

  Historical Review

The idea of preloading images is almost as old as the Internet. Yet, many misconceptions are circulating in the web developers' community. The basic problem is that downloading large files, mostly pictures and other multimedia files, may last longer than your visitor's patience. For movie or music files continuous streaming is the solution, when, instead of downloading the complete file before playing it, loading and playing occur simultaneously. In a previous article I discussed some possible solutions to play sound on the web. Here we only concentrate on how to display image files fast.

The concept of preloading is based on the idea that large or repeatedly used files should be downloaded in advance, held in memory or at least on the client's hard drive. This way the delay of displaying them would not be noticeable. A typical application of this idea is the switching of images upon the "onmouseover" and "onmouseout" events. The switch must be prompt. If the images are relatively small, default browser settings may automatically handle the local storage of the pictures properly. The following picture of a closed folder is supposed to change, i.e., open when the user moves the cursor over it and close when the cursor leaves the picture: folder

  Possible Solutions

The changing image is accomplished with the following snippet:

<img alt="folder" src="images/folder_closed.gif"
  onmouseover='this.src="images/folder_open.gif"'
  onmouseout ='this.src="images/folder_closed.gif"' />

It should work fine without preload, with the default settings of Internet Explorer or Firefox. If the pictures don't alternate fast for you, you or somebody else may have changed the browser's setting. You, as a developer, can hope for the best but you should prepare your web application for the worst. You cannot underestimate the user's creativity in messing up default settings. If, for example, the user sets IE's cache size to minimum and the "Check for newer versions of stored pages" to "Every visit to the page", not much playground is left for the web developer. Lately I gave up handling extreme users. I usually issue a warning message to those, who disable JavaScript or use ancient browser versions but I'm not willing to make extra efforts to serve them the same way as the rest of the word, which luckily makes up at least 90% of the users. However, you should prepare your app for handling normal situations, like preloading larger pictures that would not be kept automatically in the client's hard drive or memory.

Here is the preloading version of the previous example. The following snippet should be placed in the HEAD section:

<script type="text/javascript">
  imgArray = new Array();
  imgArray[0] = new Image();
  imgArray[1] = new Image();
  imgArray[0].src = "images/folder_closed.gif";
  imgArray[1].src = "images/folder_open.gif";
</script>

  Misconceptions

Because the code is invoked in the HEAD, the preloading snippet is executed before the page load, right? Well, almost. The execution starts but it may not finish before the page load. It depends on the size of the preloaded files, on the connection speed and on the type and configuration of the browser. In case of two small pictures, like in the above example, the preload occurs on time. This way the "onmousover" and "onmousout" events will work smoothly upon the image in the BODY:

<img alt="folder" src="images/folder_closed.gif"
  onmouseover='this.src=imgArray[1].src'
  onmouseout='this.src=imgArray[0].src' />

Is this solution always appropriate? Let's say we have 20 pictures, 25k each, in a photo album. That's half a megabyte to load. Also, assume that preload works as expected, i.e., it finishes before the page load starts. The creator of the page can assume that the pictures are there when needed. However, the visitor does not see anything until the preload is complete. If we used the above technique, the preload would take at least 10 seconds even with a fast Internet connection. 10 seconds is a critical waiting time for the visitors. Many of them will not watch an empty window that long and they leave the page before the preload is complete.

Most tutorials advise to preload all pictures before loading the page. A more intelligent approach is to preload only those pictures that should be seen first, and load the rest later. This can be an asynchronous process. While the visitor watches the present page, a timed process can preload the files that are necessary for the next state of the page. The next state can be the result of a scroll-down or of a click on a button, and we should preload the forthcoming picture(s) before these events but after the load of the page. Our case with the photo album is simple, and we don't have to take care of an additional timing of the preloading process. The preload will be triggered by a user intervention or by another, automated asynchronous process. The Next function that sets up the asynchronous process is discussed in the previous article of this series.

The Been There Done That Photo Album preloads only one picture at a time, namely, the one that would be displayed after the most likely actions, the click on the Next button. Since the slide show uses the same function to automatically change the picture, this preloading technique works during the slide show, as well. The preload function, of course, is located in the HEAD, but its execution happens whenever a new image is set:

function preload(albumNo, picNo) {
  if (isNull(imageArray[albumNo][picNo])) {//not preloaded yet
    imageArray[albumNo][picNo] = new Image();
    imageArray[albumNo][picNo].src = 'image file name ...';
  }
}

function setImage(albumNo,picNo){
//Displays the currently asked image
...
//Preloads the next image
  picNo++;
  if (picNo<numberOfPictures) preload(albumNo, picNo);
}//end function setImage

The complete code is available from the BTDT web site.

  Summary

In case of large downloadable files, the developer cannot assume that the files will be at the client's computer when needed. Both premature preload and negligation of preload can cause problems. One should not rely on default settings and features and must take care of the sequence of processes.


Read the previous articles:

AJAX In Action (1): Making The Fastest Photo Album

AJAX In Action (2): Fixing The Broken Bookmark

AJAX In Action (3): Slide Show With Asynchronous Calls.

Read more articles on Web design and development.