Reputation: 490413
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
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
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
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
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:
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
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