Preloading Photos

by R. Preston McAfee, May, 2023

TLDR: Successfully preloading and displaying photos requires a more complex program than you find on the web.

I've been trying to preload photos since I created my first website 28 years ago. If a website has a small number of photos, you can just download them all. But my website has over 30K photos, so that is impractical. When a user clicks to change an image, the goal is to download it and not change the displayed image until the downloaded image was available.

The reason that this is challenging is, on a slow connection, the user might click again to download another photo. Indeed, if it is taking over a second to download, the user can submit requests much faster than downloads happen. It is critical to prevent such request stacking, and get the picture downloaded before initating a second download request, and then only initiate the most recent request.

Here is javascript code to do exactly that. The code starts by a user clicking something that changes the desired picture number, pn, and this runs a program, changepix.

Changepix moves to a new picture, using a preldr, checking if image is loaded and if not, running changepix .1 sec later. There are two ways to get to picloaded, based on two paths. If the user seeks a new picture starting at changecomplete=1, it will preload and when complete, run picloaded. But if, while that picture is loading, the user clicks to get yet another picture, that one is held (pn changed) while the first loads, and runs changepix again .1 sec later, repeating until complete. The user can repeatedly change pics during this time and only the last one will actually load.

var pn="0.jpg";// pn is the identity of the desired picture
var preldr = new Image();// preldr is the file into which an image is preloaded
var changecomplete=1;// 0 means changepix is running
let time1 = new Date().getTime();// will use times to assess connection speed
let time2 = new Date().getTime();
let timecount=0;
var pnmem=pn;// pnmem is used to recall the loading file; user may initiate additional requests while this one is loaded but pnmem will be preserved.

function changepix() {
// called to initiate a change in the image; typically: pn="abc.jpg"; changepix();
// I use code here to light up an indicator that a picture is loading
    if(changecomplete==1){
        changecomplete=0; pnmem=pn;    
// only run if changepix ==1, else wait .1 sec
        preldr.src=URL[pnmem];     //set preldr to get the desired file
        time1 = new Date().getTime();     //optional, record the time this happened
        if (!preldr.complete) {
            preldr.addEventListener('load', picloaded);}
// if preldr isn't finished loading, add a listener and run picloaded when it completes
        else {picloaded();}}         //otherwise preldr is loaded (e.g. was in cache) so run it
    else {setTimeout(changepix,100)}}         // only run if changepix ==1, else wait .1 sec


function picloaded() {
    preldr.removeEventListener('load', picloaded);
   time2 = new Date().getTime();get the time pic loaded
   if (time2-time1>=1000){xyz(); timecount=0}//this has detected a slow connection, and turns off some eye candy in the site by running xyz();
   if (time2-time1<=100){
        timecount=timecount+1;
        if(timecount>4){timecount=0; zyx();}}
// here we detected a fast connection and turned the eye candy back on if load time is more than a second, turn off eye candy.    If 5 fast loads in a row, turn eye candy back on by running zyx();

// Code can go here to fill headers but use pnmem, not pn, as the current picture, because the user might have changed pn
   document.getElementById("imgdisplay").src=preldr.src;//set the current picture in <img id="imgdisplay">
// if code used to light up an indicator, turn it off now
changecomplete=1;}// picture loaded, reset changecomplete

At a high level, the user initiates a request for a picture, labelled pn, and runs the program changepix. There is a variable, changecomplete, which is 0 if a picture is still loading, and one otherwise. If changecomplete = 0, i.e. a picture is loading, the code just waits .1 sec and then runs changepix again. Otherwise, it sets changecomplete to zero (download running), stores the requested pic name pn as pnmem, preloads the picture, and then checks if it is complete, and if so, runs picloaded, and if not, starts a listener that, when the picture is complete, runs picloaded.

Picloaded removes the listener (which may not exist if the picture was in cache) and loads the preloaded picture in the relevant location. I've also added code to detect the time this took; I use > 1 sec download to indicate a slow connection and turn off some pretty but unnecessary code. If the time is consistently short (five in a row less than .1 sec) I turn that code back on.