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:
- Download the image data by creating an XMLHttpRequest (XHR).
- Update a progress bar by handling the XHR’s progress event and using its “loaded” and “total” properties to compute the progress.
- 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:
- Download and display the image by creating an image element with the src attribute set to the image URI (as usual).
- 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?
Its a great idea, specially for mobile websites
This would be amazing for my job at the UCLA Digital Library!!!!
Is there a way to add these events for anything with a src attribute? Is that loading not all handled by the same code?
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!
Good idea. Big +1.
This would be very useful and as @julian said, should be applied to anything with a src attribute.
Yep, great idea.
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
> 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!
Thanks man! great ideas and good to see what can be done! thanks
This is a must-have feature. I can think of so many uses in my gallery scripts…
There’s a typo:
window.bota()window.btoa()Thanks!
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.
+1
Pingback: Weekly Badass JS Roundup #6 - Just another My blog Sites site - technologynewssites
I think this makes a lot of sense, especially because of the image.crossOrigin attribute.