Eric Dubé
Eric Dubé

Reputation: 482

jQuery .on('load') is not working as expected

I'm trying test for when an image is loaded so I can change the opacity and fade it in, and I want to do this to all images with the .hphoto class.

This is how I'm attempting it with jQuery:

function handle_photoloads() {
    $(".hphoto").each(function (i,obj) {
        console.log("Yo");
        if ($(obj).complete) {
            console.log("1");
            $(obj).css('opacity','1');
        } else {
            console.log("incomplete");
            $(obj).on('load',function () {
                console.log("2");
                $(obj).css('opacity','1');
            });
        }
    });
}

$(document).ready(function() {
    handle_photoloads();
});

The outputs I get are "Yo" and "incomplete", and I get one of each output for the amount of .hphoto objects I have, so something is working. Why is the load handler not being triggered as expected?

Upvotes: 0

Views: 93

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074008

This line is a problem:

if ($(obj).complete) {

You're looking for a complete property on the jQuery object, not the DOM element it wraps. Since obj refers to the DOM element, the simplest thing is:

if (obj.complete) {              // Where `obj` refers to the DOM element

Otherwise, use prop, but you wouldn't normally use it when you're wrapping something you know is an element:

if ($(obj).prop("complete")) {   // Where `obj` refers to the DOM element

However, note that checking for the complete flag and then adding a handler has a small chance of missing the event. JavaScript in browsers has only one UI thread, but the browser is not single-threaded. This is a perfectly valid sequence of events:

  1. The JavaScript interpreter evaluates $(obj).prop("complete") and gets back the value false.

  2. The browser's image loading code, running on a different thread, sees that it has finished loading that image and checks to see if there are any event handlers registered for the load event; not seeing any, it doesn't add any handler callback for when the UI thread is next able to process the pending calls queue.

  3. The JavaScript interpreter hooks the load event on the image.

The odds are very low, but it can happen.

If you're worried about it, hook the handler first, then check complete and, if it's set, call your handler and unregister it. In a rare converse of the above, you might get two calls rather than one (because the event occurred after you hooked the handler but before you checked complete), but "sometimes two" is better than "sometimes zero." :-)

jQuery's subtly-named one function can be useful in this context:

function handle_photoloads() {
    $(".hphoto").each(function (i,obj) {
        var photo = $(obj);            // Or = $(this), which would be more common
        console.log("Yo");
        photo.one("load", function() { // Note `one`, not `on`
            photo.css('opacity','1');
        });
        if (photo.prop("complete")) {
            photo.trigger("load");
        }
    });
}

one removes the handler the first time it's called. So we only get one callback, regardless of timing.

(In the above, instead of if (photo.prop("complete")), you could do if (photo[0].complete))

Upvotes: 3

Related Questions