Bytes received and bytes total of images via Javascript/jQuery
Asked Answered
T

1

6

I'm working on a Javascript/jQuery powered image preloader, and have hit a bit of a snag. While as of currently it provides the progress based on loaded_images / total_images, this is not very accurate given a page could have a thousand 1kB images, and a single 1MB image.

I'm looking for a way to incorporate filesize into the progress calculations. Now, I've looked into some (cross browser compatible) tricks at capturing the filesize of a given image, and it seems that Ajax requests for Content-Length were the most reliable (in terms of accuracy) like so:

var imageSizeTotal = 0;
var ajaxReqest = $.ajax({
    type: 'HEAD',
    url: 'path/to/image',
    success: function(message){
        imageSizeTotal += parseInt(ajaxRequest.getResponseHeader('Content-Length'));
    }
});

Now, I find this method to be quite useful, as I can provide a status message of Initializing while the necessary requests are taking place. However my issue now is two-fold:

  1. Is there any way possible to capture the bytes loaded of a given image object, perhaps using setInterval() to periodically check? Otherwise, I'm sort of back at the issue of the progress indicator hanging on large files.
  2. How can I force the actual progress calculator, etc., portion of the script to wait until the necessary Ajax requests are completed (displaying Initializing or whatever), so it can go ahead with the loading?

Also, here's the script I currently use, which again, calculates progress based on the number of images, regardless of filesize or bytes received.

var preloaderTotal = 0;
var preloaderLoaded = 0;
var preloaderCurrent = null;

$('#preloaderCurtain')
    .bind('preloaderStart', function(){
        $(this)
            .show();
        $('*')
            .filter(function(e){
                if($(this).css('background-image') != 'none'){
                    preloaderTotal++;
                    return true;
                }
            })
            .each(function(index){
                preloaderCurrent = new Image();
                preloaderCurrent.src = $(this).css('background-image').slice(5, -2);
                preloaderCurrent.onload = function(e){
                    preloaderLoaded++;
                    if(preloaderLoaded == preloaderTotal - 1){
                        $('#preloaderCurtain')
                            .trigger('preloaderComplete')
                    }
                    $('#preloaderCurtain')
                        .trigger('preloaderProgress')
                };
            });
    })
    .bind('preloaderComplete', function(){
        $(this)
            .fadeOut(500)
        startAnimation();
    })
    .bind('preloaderProgress', function(e){
        $('#preloaderProgress')
            .css('opacity', 0.25 + (preloaderLoaded / preloaderTotal))
            .text(Math.floor((preloaderLoaded / preloaderTotal) * 100) + '%');
    })
    .trigger('preloaderStart');

Hopefully I'll be able to turn this into a plugin, once I work the bugs out of it.

Tetzel answered 16/1, 2011 at 22:23 Comment(10)
I do not know of any method to get partially loaded image sizeBahrain
I can't imagine making this work without some server-side help - specifically, something to supply a table mapping image name (URL) to image size. Now, even with that, you won't be able to know how much of each image is loaded, but your "load" handlers on the Image objects can at least know when they're done, and tick off the size.Vining
@mplugjan; Thanks, I notice your history with Javascript, and it's unfortunate you don't know of a way. I've already accepted that it's likely impossible, but the second part of my question is still certainly something I would like some help with.Tetzel
@Pointy; That's what I'm figuring I'll have to do. It'll give a more accurate progress indication, but the indicator will still hang on large images, which was something I was hoping to avoid.Tetzel
A surprising amount of progress indicators are "fake". They don't really indicate the true progress. You can keep the progress moving by estimating. The goal is keeping the user occupied, not keeping them informed of perfect progress percentage.Dyal
Hmm - you have the size. Can yoi load a few smaller images and see the approximate load time and apply that to the larger images - that guesstimate could be good enough for a progress bar - you could re-calculate the bps for each image received... I am only thinking aloud here...Bahrain
@chris; True, however as a test of my abilities and to provide as accurate a progress indication as possible, I'd like to exhaust all options in my attempt to make this work. @mplungjan; I like that idea, that was another consideration I made, ordering the array of images to load by their filesize (ascending) and then by fudging with the math I could provide a somewhat accurate indication when it hits the larger images. My test case here is a single page with ~150 images, ranging in size from ~1kB to ~1.5MB.Tetzel
Any thoughts on my second issue though, ensuring that the size requests complete? I'm thinking setInterval() based checks on the completion of the filesize array. When full, begin the preload.Tetzel
Seems to me that by the time you have the Content-Length via the success handler, all of the image data is already loaded. You need that information before the image loads...Undecided
@Hemlock; A header request doesn't pull down the content though, it only grabs the meta data, right? If I have 20 images on a given page, averaging 250kB, at 5MB hopefully 20 header requests would be faster than the actual data transmission...Tetzel
L
1

It looks like a similar question was asked and answered here:

XmlHttpRequest.responseText while loading (readyState==3) in Chrome

and here:

Comet Jetty/Tomcat, having some browser issues with Firefox and Chrome

Basically - .responseText.length for Firefox and iPhone, .responseBody.length for IE, WebSockets for Chrome.

The second thread suggests bayeux/dojo encapsulate all this for you into a higher-level API so you don't have to write it yourself.

Latifundium answered 18/1, 2011 at 19:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.