alex
alex

Reputation: 490413

How should I handle a missing image in my plugin?

I have written a jQuery plugin called waitForImages. It basically gives the ability to run callbacks when images have loaded.

I recently received a feature request to somehow make the code notify when there is an error downloading an image.

Could the plugin call a function when an image is not found?

I thought about adding an extra argument that could be checked if the image was downloaded successfully or not.

image.onerror = function() {
     eachCallback.call(img.element, allImgsLoaded, allImgsLength, true);
}

Then the person could check the last argument to see if it is true, and if so, there was an error loading the image.

This idea smells to me though. What if I want to add an extra argument there later? Should the same callback be called for successful and failure of the image download?

I then thought about throwing an exception if the image is not found.

throw new Error(img.src ' + does not exist');

This however is not as flexible to catch, you'd need to call the plugin like so...

try {
   $('body').waitForImages();
} catch (exception) {
    // Which image didn't load?
    alert(exception.message);
}

What would be the best way to handle if an image didn't load in my plugin?

Upvotes: 4

Views: 621

Answers (5)

lsuarez
lsuarez

Reputation: 4992

Either way this has pointed out that you should probably bind your function on line 103 to both error and load, and should probably accept the event as the argument to that function. Otherwise you will never raise a flag that the browser is done with its image processing. This could be as simple as calling $(image).bind("load error", function(e) { allImagesLoaded++; ... }). Internally within the function you can pass off to error handling options by inspecting e.type === "error" like raising a flag that there was a failure and the error callback needs to be executed once all images are finished.

You already have handling for an options object. The best thing to do would be to allow for the user to make callbacks on error events by adding additional options within that object. This would be consistent with the approach of other common plugins and base jQuery functions. I'd suggest considering the callback model for jQuery.ajax(). Allow users to specify callbacks for success which execute when all images have successfully loaded, error which allows a different execution path when any error event is encountered, and complete which executes once all images have fired their events, regardless of success or failure. You may also want to add a callback option for handling errors immediately after a single image fails, though I don't necessarily feel that fits with the scope of the plugin. Don't bloat it unless it really adds value to the functionality.

Upvotes: 0

Gijs
Gijs

Reputation: 5262

This sounds like the kind of thing you'd want to use jQuery's deferred objects for: http://api.jquery.com/category/deferred-object/ .

Basically, this lets you create an object (var x = jQuery.Deferred()). Then you can add completion, success and error handlers as you would to a jQuery.ajax object, using x.always(callback), x.done(successCallback), x.fail(errorCallback), respectively. Obviously the names of functions and/or properties to attach such handlers to your internal Deferred object are up to you.

You could pass a text status to these handlers, like jQuery.ajax, or indeed the number of completed/errored images, when you resolve the deferred object using x.resolveWith and x.rejectWith.

I'd think such functions are best suited for the overall completion/success/failure of the image loads, but you could also use the same system for each image individually. When calling your plugin, you'd keep the handlers in a local object, and create a new deferred object for each image you encounter, adding the callbacks to this new deferred object.

Upvotes: 0

kororo
kororo

Reputation: 2022

Nice plugin :)

After, I examine your code, I would place on error after this piece of code:

image.onload = function() {
    allImgsLoaded++;
    eachCallback.call(img.element, allImgsLoaded, allImgsLength);
    if (allImgsLoaded == allImgsLength) {
        finishedCallback.call(obj[0]);
        return false;
    };
};

image.onerror = function() {
    errorCallback.call(img.element);
    return false;
}

Of course, you need to introduce a new callback function like

$('selector').waitForImages(function() {

    alert('All images are loaded.');

}, function(loaded, count) {

   alert(loaded + ' of ' + count + ' images have loaded.');
   $(this).addClass('loaded');

}, function(img) {
   alert('error');
});

Hope helps :)

Upvotes: 0

Brian McKenna
Brian McKenna

Reputation: 46228

I'd add a specific callback for error conditions.

You already have this:

$('selector').waitForImages({
    finished: function() {
        ...
    },
    each: function() {
       ...
    },
    waitForAll: true
});

You should consider changing it to:

$('selector').waitForImages({
    finished: function() {
        ...
    },
    finishedError: function() {
        ...
    },
    each: function() {
       ...
    },
    eachError: function() {
       ...
    },
    waitForAll: true
});

You could also use "finishedSuccess", if given, just to keep it consistent.

I prefer this for a few reasons:

  1. It separates out error handling
  2. You can have a different set of arguments for success or failure callbacks
  3. You easily add callbacks for different errors (unlikely for this case)

Please don't throw exceptions. An uncaught exception will break JavaScript execution. Probably not something you'll want to do just because an image didn't load.

Upvotes: 7

cusspvz
cusspvz

Reputation: 5291

Try this: (DOM Element .complete var)

$('img').each(function(){
     if($(this).complete != 'true'){
          /* workarround here */
     }
})

TIP: If you found some images that aren't loading, you could replace the urls with an default image saying that 'image was not found' :)

Upvotes: 1

Related Questions