javascripthtmldom-eventsonloadonload-event

No page rendering while img onload-event in Javascript


I have got a vanilla Javascript that sets an img's src attribute to a data-uri. In the function assigned to the onload event, I try to show the progress of some preparations on the page that might take a while (multiple canvas elements are generated and a few settings are set).

This is the function, that should render a progress bar:

let progress = document.querySelector('.progress');

function showProgress(message, percent) {
    console.log(message + ' (' + (percent * 100).toFixed(0) + '%)');
    progress.querySelector('.progress--message').innerHTML = message;
    progress.querySelector('.progress--bar-container--bar').style.width = (percent * 100).toFixed(0) + '%';
}

The code is run like this:

img.onload = function() {
    showProgress('Preparing image...' .1);

    // Some preparations...

    showProgress('Preparing tools...', .2);

    // etc...

    showProgress('Done...', 1);
}

The console logging works as intended, but the rendering of the DOM elements stops until the end of the function and displays the "done" state after everything is ready.

Is there any render-blocking in the onload event handling and can it be circumvented? Or am I missing something? The code itself does not have any effect on the outcome. Even the first line in the function does have this strange behaviour.


Solution

  • I came back to the matter after working on some other projects and solved my problem using Promises like this:

    showProgress("Loading image...", 0.0) // Reset progress bar
    
    const img = document.createElement("img") // Get image element
    
    const promise = new Promise((resolve, reject) => { // Instantiate a Promise
      img.onload = resolve // Set onload callback to resolve (this triggers the chain)
      img.onerror = reject
    
      img.src = "/path/to/image.jpg" // Set src attribute to start loading image
    })
    .then(() => { // When promise is resolved: Start step 2
      showProgress("Preparing image...", 0.3)
      // ...
    })
    .then(() => { // When promise is resolved: Start step 3
      showProgress("Preparing tools...", 0.6)
      // ...
    })
    .then(() => { // When promise is resolved: Finish up
      showProgress("Ready...", 1)
      // ...
    })
    

    With Promises you can put asynchronous tasks in an order while still maintaining readability.

    Further reading: Promises on MDN