Adobe

decor

Web Platform Team Blog

Making the web awesome

HTML5 Image Progress Events

Suppose you want to display the download progress of high-resolution images in a medical imaging application or an HTML5 game. Currently, this is a little harder than it should be in HTML5/JavaScript.

A common approach goes like this:

  1. Download the image data by creating an XMLHttpRequest (XHR).
  2. Update a progress bar by handling the XHR’s progress event and using its “loaded” and “total” properties to compute the progress.
  3. Display the image by encoding the XHR response in base 64, appending it to a data URI, and setting an image element’s src attribute to the data URI.

You can view a sample and its source here. Note that this particular sample will not function in most current browsers since it uses the latest web standards, including the HTML5 progress element and events from the XMLHttpRequest Level 2 spec. However, the sample will function on the latest builds of WebKit.

A proposed alternative approach, which would require additional progress events on the image element, could go like this:

  1. Download and display the image by creating an image element with the src attribute set to the image URI (as usual).
  2. Update a progress bar by handling the image element’s progress event and using its “loaded” and “total” properties.

In HTML, an image tag could look like this:

 
        <img id="image" 
        	 src="sample.jpg" 
        	 onloadstart="showProgressBar()" 
        	 onprogress="updateProgressBar(event)"
        	 onloadend="hideProgressBar()"/>

Similarly, in JavaScript, a developer could do this:

 
var imageElement = document.createElement("img");
imageElement.onloadstart = function () {  /* Show the progress bar. */ };
imageElement.onprogress = function (e) { /* Update the progress bar. */ };
imageElement.onload = function () { /* Load succeeded. Add the image element to the page. */ }
imageElement.onerror = function () { /* Load failed. Show a custom error message. */ }
imageElement.onloadend = function () { /* Load either either succeeded or failed. Either way, hide the progress bar. */ };
imageElement.src = "sample.jpg"; /* Start loading the image. */

In both cases, the function to update the progress bar could look like this:

 
            function updateProgressBar(e)
            {
                if (e.lengthComputable)
                    progressBar.value = e.loaded / e.total * 100;
                ...
            }

You can view a full sample and its source here. This sample would require image progress events in order to run.

This approach has some clear advantages in simplicity and performance:

  • No need to maintain multiple XHR objects for concurrent image downloads.
  • No need to write a base 64 encoding function in JavaScript or use the currently unstandardized window.btoa() method.
  • No need to take the performance hit from base 64 encoding large images.
  • No need to provide the image MIME type in a data URI string.
  • No need for a cross domain image server to provide special headers so the XHR can download the image file.

However, the alternative approach is not yet possible on the web because the HTML image element lacks progress events. The Web Platform team at Adobe is proposing adding image progress events to HTML5 spec and implementing them in browsers. There is a WebKit bug tracking a prototype implementation.

Currently, the image element has the following events:

  • load
  • abort
  • error

We propose adding:

  • loadstart
  • progress
  • loadend

The semantics of the new events are similar to the ones defined in the XMLHttpRequest Level 2 spec.

What do you think?

31 Comments

  1. January 14, 2012 at 12:50 am, Aldonio said:

    Its a great idea, specially for mobile websites

  2. January 15, 2012 at 2:58 am, Rob snow said:

    This would be amazing for my job at the UCLA Digital Library!!!!

  3. January 18, 2012 at 7:00 pm, Julian Kussman said:

    Is there a way to add these events for anything with a src attribute? Is that loading not all handled by the same code?

    • January 19, 2012 at 5:28 pm, Max Vujovic said:

      This sounds feasible and definitely has some good use cases (e.g. monitoring script download progress for a large web app). Loading is a fairly complex process, so each potential element would have to be looked at individually. In WebKit, there is loading code that is shared between elements and there is also a good amount of element-specific loading code. We’re thinking the image element is the best place to start, and extending these events to other elements can be a logical next step. Thanks for the question!

  4. January 18, 2012 at 7:32 pm, Raymond Camden said:

    Good idea. Big +1.

  5. January 19, 2012 at 5:03 pm, Ric said:

    This would be very useful and as @julian said, should be applied to anything with a src attribute.

  6. January 19, 2012 at 5:07 pm, Adam Bankin said:

    Yep, great idea.

  7. January 19, 2012 at 5:52 pm, FremyCompany said:

    Very great idea. However, adding it only to Images is something I find confusing. I hope we can define a clear set of properties that apply to all downloadable resources.

    For exemple, the model you propose play badly with an interlaced JPEG image : you can start to display the image even when it’s not fully downloaded and that fact isn’t visible with your current proposal.

    The onreadystatechange event of the XHR element provides such information (1=inited, 2=opened, 3=responseText is non-empty, 4=responseText is complete)

    The VIDEO element also has something like that (current frame is still downloading, current frame is downloaded but next is still downloading, current frame is downloaded and at the current rate of download, the video can be played to end without interruption, the video is fully downloaded).

    I think we should probably define a new and reusable “loadState” that would be used for all kind of downloadable objects and provide some generic information about load status.

    The following load states ‘status’ comes to my mind: error(-1) uninitialized(0) still emtpy(1) receive started but not interactive(2) receive started, partially interactive (3) receive started, interactive(4), load ended(5). Each “downloaded item” should then have to define what specific readyState match which specific loadState.status according to the spec.

    Additionnaly, a loadState object would have the “length”, “loaded” and “loadRate” properties (length can map to -1 if it’s unknown).

    An “onloadstatechange” event would then be risen each time the UA find that a significant change has happened (some run would be enforced like when the image go to the 5 or -1 load status for the first time).

    Such a model would be reusable and has been ‘requested’ in the www-style mailing list to allow to introduce new ‘:loading’ and ‘:error’ pseudo-classes to be used on replaced content like VIDEO and IMG depending on load status.

    I was going to propose the model to the HTML WG soon, but since this proposal comes in, I throw my idea here as preview ;-)

    • January 20, 2012 at 10:08 pm, Max Vujovic said:

      > Very great idea. However, adding it only to Images is something I find confusing.

      Thanks! Images seem like a good starting point for exposing progress events, and we have our eye on extending this approach to other elements (e.g. script) in the future. Julian also asked a similar question above, which has a response.

      > I hope we can define a clear set of properties that apply to all downloadable resources.

      The approach we’ve taken with these events is consistent with XHR and the HTML5 media elements. The events we’re proposing on the img element are semantically identical to the loadstart, progress, and loadend events defined in XMLHttpRequest Level 2 spec. HTML5 media elements like video and audio also define the loadstart and progress events. I’m not sure if that’s exactly what you meant by “properties”.

      > For exemple, the model you propose play badly with an interlaced JPEG image : you can start to display the image even when it’s not fully downloaded and that fact isn’t visible with your current proposal.

      Our current proposal does not define when the developer has to display the image. It’s up to him or her. In the case you mention, a developer wants to display the JPEG image only after it’s fully downloaded. The developer could take two approaches:

      (1) Create the img element in JavaScript, but do not add it to the page until the load or loadend event fires (depending on the behavior the developer wants in the error case).
      (2) Declare the img element as an HTML tag with an initial style of “display: none;”. On the load or loadend event, update the img element’s style to “display: inline;” (for example).

      > I think we should probably define a new and reusable “loadState” that would be used for all kind of downloadable objects and provide some generic information about load status.

      I think your proposal goes beyond the scope of progress events, enabling different types of use cases. In suggesting it to the HTML WG, creating some examples of the use cases your proposal enables, especially where current capabilities fall short, would be helpful.

      Thanks for the comments!

  8. January 19, 2012 at 8:20 pm, Janey B. Smith said:

    Thanks man! great ideas and good to see what can be done! thanks

  9. January 26, 2012 at 7:59 pm, Jon Beebe said:

    This is a must-have feature. I can think of so many uses in my gallery scripts…

  10. January 27, 2012 at 10:11 am, subzey said:

    There’s a typo: window.bota() window.btoa()

    • January 27, 2012 at 5:24 pm, Max Vujovic said:

      Thanks!

    • June 23, 2012 at 3:45 am, Robert00001 said:

      I also get mixed up with bota instead of btoa.

  11. January 28, 2012 at 5:54 am, Elliot J Geno said:

    You could have a flash fallback communicating through ExternalInterface to JavaScript on browsers that are not up to snuff yet are capable of playing flash. Kind of like how Pandora, GrooveShark, and Sonify use Flash instead of the audio tag whereever they can. Its a good stop gap until these things become more prevalent.

  12. January 30, 2012 at 10:36 am, Mihai Corlan said:

    +1

  13. February 02, 2012 at 7:57 pm, Weekly Badass JS Roundup #6 - Just another My blog Sites site - technologynewssites said:

    […] proposes events for monitoring the downloading of Image resources which could be quite useful in many […]

  14. February 04, 2012 at 3:11 am, Kevin G said:

    I think this makes a lot of sense, especially because of the image.crossOrigin attribute.

  15. April 24, 2012 at 9:42 pm, Moldován Eduárd said:

    I see a great idea building up here.
    Also, I love that devs from adove are getting strongly into html5/css3.

  16. June 05, 2012 at 8:05 am, Dries Bultynck said:

    Not sure if this is the proper solution for images. When you’re using a responsive webdesign (like mine), you probably want to serve a set of pictures per device to max speed. Great idea thought. I’m still thinking about the best solution for my blog.

    • June 05, 2012 at 4:39 pm, Max Vujovic said:

      Thanks for the comment, Dries.

      > Not sure if this is the proper solution for images. When you’re using a responsive webdesign (like mine), you probably want to serve a set of pictures per device to max speed.

      What problem are you trying to solve? Are you trying to choose which images to send to a device, or are you trying to display the download progress of a set of images? Or something else? :)

      > Great idea thought.

      Thanks!

      > I’m still thinking about the best solution for my blog.

      Nice design! It responds well.

      • June 06, 2012 at 11:49 am, Dries Bultynck said:

        Tnx on the design part :) hired an excellent designer to do the design. Getting it all together in WP was a challenge ;)

        On the progressbar. I’m not a fan of progressbars because the actual cause of the latency is because the image corresponding to the device of the user (without taking connection in count here) is mostly too large. Lets say for a smartphone. Progressive scaling is an option but I read (can’t find the link anymore) an article about showing other sizes of images corresponding to the device can be done with jquery without make tons of images (so just 1 or 2, maybe 3 images to fetch the gross of the devices -> Desktop, tablet & mobile).

        So.. i’m thinking about this for my own blog. Loading images takes too long, sometimes. No use of serving +1000px images to a smartphone, no?

        • June 06, 2012 at 4:54 pm, Max Vujovic said:

          > I’m not a fan of progressbars because the actual cause of the latency is because the image corresponding to the device of the user (without taking connection in count here) is mostly too large.

          > No use of serving +1000px images to a smartphone, no?

          Absolutely. The best progress bar is the one we never need to show :)

          Thankfully, built-in support for responsive images is coming to browsers (in one form or another). See:
          http://www.alistapart.com/articles/responsive-images-and-web-standards-at-the-turning-point/

          But for now, you’re probably going to have to use a framework for it.

  17. July 09, 2012 at 6:17 am, Mikhail Davydov said:

    Great idea!

    I think that all resource “tags” (script, link, audio, video…) should have loadstart, progress, loadend events. They are resources too.

  18. August 19, 2012 at 8:01 am, Jafar said:

    I agree that all tags need this option.

  19. October 08, 2012 at 5:09 am, barra di preload immagini - AlterVista said:

    […] Leggi questo articolo: http://blogs.adobe.com/webplatform/2…ogress-events/ […]

  20. December 05, 2012 at 10:53 am, arabic keyboard said:

    Its a great idea, specially for mobile websites

  21. January 30, 2013 at 12:15 pm, Mike said:

    I found this page while searching Google, hoping to find this already existed.

  22. May 06, 2013 at 3:16 am, Javi Agenjo said:

    So one year and a half later and we still dont have a clean solution for this problem? I need to load 4K images so base64 encoding is out of the question.

    I hope somebody finds a good solution

  23. May 09, 2013 at 9:04 pm, jpap said:

    Can you rely on the fact that the XMLHttpRequest, once downloaded, is in the local browser cache, then simply use a new Image() object with src attribute set to the same URL, thus avoiding the expensive base64 data URL generation?

    jpap