diff --git a/README.markdown b/README.markdown index 7b060aa..930ef5c 100644 --- a/README.markdown +++ b/README.markdown @@ -4,32 +4,90 @@ http://desandro.github.com/imagesloaded/ A jQuery plugin that triggers a callback after all the selected/child images have been loaded. Because you can't do `.load()` on cached images. -## Basic usage + +## Calling ```js -$('#my-container').imagesLoaded( function( $images, $proper, $broken ) { - // callback provides three arguments: - // $images: the jQuery object with all images - // $proper: the jQuery object with properly loaded images - // $broken: the jQuery object with broken images - // `this` is a jQuery object of container - console.log( $images.length + ' images total have been loaded in ' + this ); - console.log( $proper.length + ' properly loaded images' ); - console.log( $broken.length + ' broken images' ); +$(selector).imagesLoaded( [ callback ] ); +``` + +### selector + +ImagesLoaded can be called on an elements with images within them, images directly, or a combination of both. + +###### *Example* + +```js +// Calling on an element that may contain images +$('#content').imagesLoaded(fn); + +// Calling on image elements directly +$('img').imagesLoaded(fn); + +// Combination of both +$('#content, #gallery > img').imagesLoaded(fn); +``` + +### [ callback ] + +Callback argument can be a function, or an object map with deferred methods. + +#### callback:function + +Pass a function to be called when all images has finished with loading. This is the simplest way how to use imagesLoaded. + +##### *this* + +When executed, the callback function scope (`this`) is a jQuery object with element or set of elements on which the imagesLoaded has been called (`$(selector)`). + +##### arguments + +Callback function receives 3 arguments: + ++ **$images:** `Object` jQuery object with all images ++ **$proper:** `Object` jQuery object with properly loaded images ++ **$broken:** `Object` jQuery object with broken images + +###### *Example* + +```js +$('#my-container, .article img').imagesLoaded( function( $images, $proper, $broken ) { + console.log( $images.length + ' images total have been loaded' ); + console.log( $proper.length + ' properly loaded images' ); + console.log( $broken.length + ' broken images' ); }); ``` -You can call `imagesLoaded` on a set of images as well. +#### callback:object + +To bind deferred methods before the determination process starts, pass an object with map of given method callbacks instead of a callback function. +You can read more about these methods and their behavior in [Deferred section](#deferred) below. + +###### *Example* ```js -$('.article img').imagesLoaded( myFunction ); +$(selector).imagesLoaded({ + done: function ($images) {}, + fail: function ($images, $proper, $broken) {}, + always: function () {}, + progress: function (isBroken, $images, $proper, $broken) {} +}); ``` +If you are passing object with deferred methods, but you still want to use the simple callback functionality, use the `callback` property, like so: + +```js +$(selector).imagesLoaded({ + // ... deferred methods ... + callback: function ($images, $proper, $broken) {} +}); +``` + + ## Deferred As of v1.2.0, `imagesLoaded` returns jQuery deferred object. - ### Behaviour **Resolved**: deferred is *resolved* when all images have been properly loaded @@ -40,57 +98,70 @@ As of v1.2.0, `imagesLoaded` returns jQuery deferred object. ### Usage ```js -var dfd = $('#my-container').imagesLoaded(); // save a deferred object +// Save a deferred object with postponed determination process +var dfd = $('#my-container').imagesLoaded(); // Always dfd.always( function(){ - console.log( 'all images has finished with loading, do some stuff...' ); + console.log( 'all images has finished with loading, do some stuff...' ); }); // Resolved dfd.done( function( $images ){ - // callback provides one argument: - // $images: the jQuery object with all images - console.log( 'deferred is resolved with ' + $images.length + ' properly loaded images' ); + // callback provides one argument: + // $images: the jQuery object with all images + console.log( 'deferred is resolved with ' + $images.length + ' properly loaded images' ); }); // Rejected dfd.fail( function( $images, $proper, $broken ){ - // callback provides three arguments: - // $images: the jQuery object with all images - // $proper: the jQuery object with properly loaded images - // $broken: the jQuery object with broken images - console.log( 'deferred is rejected with ' + $broken.length + ' out of ' + $images.length + ' images broken' ); + // callback provides three arguments: + // $images: the jQuery object with all images + // $proper: the jQuery object with properly loaded images + // $broken: the jQuery object with broken images + console.log( 'deferred is rejected with ' + $broken.length + ' out of ' + $images.length + ' images broken' ); }); // Notified dfd.progress( function( isBroken, $images, $proper, $broken ){ - // function scope (this) is a jQuery object with image that has just finished loading - // callback provides four arguments: - // isBroken: boolean value of whether the loaded image (this) is broken - // $images: jQuery object with all images in set - // $proper: jQuery object with properly loaded images so far - // $broken: jQuery object with broken images so far - console.log( 'Loading progress: ' + ( $proper.length + $broken.length ) + ' out of ' + $images.length ); + // function scope (this) is a jQuery object with image that has just finished loading + // callback provides four arguments: + // isBroken: boolean value of whether the loaded image (this) is broken + // $images: jQuery object with all images in set + // $proper: jQuery object with properly loaded images so far + // $broken: jQuery object with broken images so far + console.log( 'Loading progress: ' + ( $proper.length + $broken.length ) + ' out of ' + $images.length ); }); ``` +**Important!**: progress method should be always registered with `callback:object` argument, which ensures that it will be registered before the determination process starts. +Otherwise you might run into an issue where by the time you register the progress method all images have been loaded, and there is nothing to report anymore. + ### Requirements -Deferred is being used only when present, so having older versions of jQuery doesn't break the plugin, just removes the functionality. -For using any Deferred method, you need jQuery **v1.5** and higher. -For using Deferred progress method, you need jQuery **v1.7** and higher. -For availability of other Deferred methods, read the [jQuery Deferred object documentation](http://api.jquery.com/category/deferred-object/). ++ Deferred is being used only when present, so having older versions of jQuery doesn't break the plugin, just removes the functionality. ++ For using any Deferred method, you need jQuery **v1.5** and higher. ++ For using Deferred progress method, you need jQuery **v1.7** and higher. ++ For availability of other Deferred methods, read the [jQuery Deferred object documentation](http://api.jquery.com/category/deferred-object/). ## Behavior notes +### Determination process + +Every browser is handling image elements differently. We are trying to check for state in image attributes when possible & reliable, but in some browsers (especially older ones) +we have to reset `img.src` attribute to re-trigger load/error events. That is the only possible way how to check for an image status, +as well as differentiate between proper and broken images. We are resetting the src with blank image data-uri to bypass webkit log warning (thx doug jones). + +Fortunately, since v2.0.0 we are in a state where imagesLoaded is both cross-browser reliable and fast, with only a few minor [known issues](#known-issues). + ### Caching -The state of all once checked images is cached, so the calls repeated on the same images don't have to go through a determining process for each image again. +The state of all once checked images is cached, so the calls repeated on the same images don't have to go through a determination process for each image again. Determining might be slow in older browsers in which we have to reset `src`, and also might introduce image flickering. -Image state is stored in `$.data` associated to that particular image DOM element. That means that everything is stored per page load, -so you don't have to worry that temporarily unavailable images will be considered as broken on a next page load as well. This is just for a multiple calls within one page load. +Image state is stored in `$.data` associated to that particular image DOM element and its `src` path (changing `src` of an image resets its cache). +That means that everything is stored per page load, so you don't have to worry that temporarily unavailable images will be considered as broken on a next page load as well. +Caching is just for a multiple calls within one page load. If, however, you need it from some reason, you can remove this data from an image with: @@ -98,18 +169,11 @@ If, however, you need it from some reason, you can remove this data from an imag $.removeData( img, 'imagesLoaded' ); ``` -### Image flickering - -In IE (particularly in older versions) you might see images flicker as plugin has to refresh all `src` attributes to catch event types. That is the only known -way how to check for loading status of both proper and broken images in IE browsers, as these dinosaurs don't bother changing any image property once an image has -been recognized as broken. The only thing they do is send an `error` event. Without refreshing `src` and catching it, it would be impossible to differentiate -between broken images and proper images that are still loading. - -Thankfully, this flickering is invisible most of the time. - ## Known issues -+ unreliable differentiation between proper and broken images in Opera versions lower than 11 (market share ~0.1%), but callback still fires ++ Unreliable differentiation between proper and broken images in Opera versions lower than 11 (market share ~0.1%), but callback still fires. ++ In IE (particularly in older versions) you might see images flicker as plugin has to refresh all `src` attributes to catch event types. + Thankfully, this flickering is invisible most of the time. ## Contribute @@ -120,4 +184,3 @@ It ain't easy knowing when images have loaded. [Every browser has its own little + [**View contributors**](https://github.com/desandro/imagesloaded/contributors) + [ajp](http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f) + Oren Solomianik - diff --git a/index.html b/index.html index 0ee5d13..0e5dbd7 100644 --- a/index.html +++ b/index.html @@ -69,17 +69,17 @@
// Call imagesLoaded and position images
-holder.imagesLoaded(function( $images, $proper, $broken ){
+$holder.imagesLoaded(function( $images, $proper, $broken ){
- var $container = this,
- x = 1;
+ var $container = this,
+ x = 1;
- $images.each( function() {
- var $this = $(this).css({ left: x });
- x += $this.width() + 1;
- });
+ $images.each( function() {
+ var $this = $(this).css({ left: x });
+ x += $this.width() + 1;
+ });
- $container.width(x);
+ $container.width(x);
});
@@ -143,37 +143,40 @@ holder.imagesLoaded(function( $images, $proper, $broken ){
-// Call imagesLoaded with callback and save the deferred object
-var dfd = $("#holder").imagesLoaded(function( $images, $proper, $broken ){
+// Call imagesLoaded with multiple callbacks, and save the deferred object
+var dfd = $holder.imagesLoaded({
+ callback: function($images, $proper, $broken){
- totalLabel.text( $images.length );
- properLabel.text( $proper.length );
- brokenLabel.text( $broken.length );
+ $totalLabel.text( $images.length );
+ $properLabel.text( $proper.length );
+ $brokenLabel.text( $broken.length );
-});
+ },
+ progress: function (isBroken, $images, $proper, $broken) {
-// Deferred magic
-dfd.progress(function( isBroken, $images, $proper, $broken ){
+ var loadingSpan = this.siblings('.loading');
- var loadingSpan = this.siblings('.loading');
+ if( isBroken ){
+ loadingSpan.removeClass('loading').addClass('broken');
+ } else {
+ loadingSpan.fadeOut(200, function(){ $(this).remove(); });
+ }
- if( isBroken ){
- loadingSpan.removeClass('loading').addClass('broken')
- } else {
- loadingSpan.fadeOut(200, function(){ $(this).remove(); });
- }
+ $progressBar.css({ width: Math.round( ( ( $proper.length + $broken.length ) * 100 ) / $images.length ) + '%' });
- progressBar.css({ width: Math.round( ( ( $proper.length + $broken.length ) * 100 ) / $images.length ) + '%' });
+ }
+ });
-}).always(function(){
+// Subsequent deferred method registration (not to be used with progress method)
+dfd.always(function(){
- var dfdState = dfd.state();
+ var dfdState = dfd.state();
- dfdLabel.addClass( 'label-' + ( dfdState === 'resolved' ? 'success' : 'important' ) ).text( dfdState );
+ $dfdLabel.addClass( 'label-' + ( dfdState === 'resolved' ? 'success' : 'important' ) ).text( dfdState );
- progress.hide();
- statusBar.show();
- progressBar.css({ width: 0 });
+ $progress.hide();
+ $statusBar.show();
+ $progressBar.css({ width: 0 });
});
diff --git a/jquery.imagesloaded.js b/jquery.imagesloaded.js
index a488974..e879492 100644
--- a/jquery.imagesloaded.js
+++ b/jquery.imagesloaded.js
@@ -1,5 +1,5 @@
/*!
- * jQuery imagesLoaded plugin v2.0.1
+ * jQuery imagesLoaded plugin v2.1.0
* http://github.com/desandro/imagesloaded
*
* MIT License. by Paul Irish et al.
@@ -23,6 +23,17 @@ $.fn.imagesLoaded = function( callback ) {
proper = [],
broken = [];
+ // Register deferred callbacks
+ if ($.isPlainObject(callback)) {
+ $.each(callback, function (key, value) {
+ if (key === 'callback') {
+ callback = value;
+ } else if (deferred) {
+ deferred[key](value);
+ }
+ });
+ }
+
function doneLoading() {
var $proper = $(proper),
$broken = $(broken);
diff --git a/js/main.js b/js/main.js
index 91c3a9f..bfc70bb 100644
--- a/js/main.js
+++ b/js/main.js
@@ -7,14 +7,14 @@ jQuery(function($){
(function(){
// Variables
- var simple = $('#simple'),
- holder = simple.find('.holder'),
- controlbar = simple.find('.controlbar');
+ var $simple = $('#simple'),
+ $holder = $simple.find('.holder'),
+ $controlbar = $simple.find('.controlbar');
- function runTest( holder ){
+ function runTest( $holder ){
// Call imagesLoaded and position images
- holder.imagesLoaded(function( $images, $proper, $broken ){
+ $holder.imagesLoaded(function( $images, $proper, $broken ){
var $container = this,
x = 1;
@@ -31,7 +31,7 @@ jQuery(function($){
}
// Controls
- controlbar.find('[data-action]').click(function(){
+ $controlbar.find('[data-action]').click(function(){
var el = $(this),
action = el.data('action');
@@ -40,7 +40,7 @@ jQuery(function($){
case 'run':
//Empty holder and apped images
- holder.empty().append(
+ $holder.empty().append(
'
' +
'
' +
'
' +
@@ -48,15 +48,15 @@ jQuery(function($){
'
' +
'
'
);
- runTest( holder );
+ runTest( $holder );
break;
case 'empty':
- holder.empty().width('auto');
+ $holder.empty().width('auto');
break;
case 'clone':
- var clone = holder.clone().addClass('clone').append('').insertAfter( holder );
+ var clone = $holder.clone().addClass('clone').append('').insertAfter( $holder );
runTest( clone );
break;
@@ -65,7 +65,7 @@ jQuery(function($){
});
// Click-delete holders
- simple.on('click', '.clone', function(){
+ $simple.on('click', '.clone', function(){
$(this).fadeOut( 200, function(){
@@ -85,16 +85,16 @@ jQuery(function($){
(function(){
// Variables
- var advanced = $('#advanced'),
- holder = advanced.find('.holder'),
- statusBar = advanced.find('.status'),
- totalLabel = statusBar.find('.totalcount'),
- properLabel = statusBar.find('.propercount'),
- brokenLabel = statusBar.find('.brokencount'),
- dfdLabel = statusBar.find('.dfdstatus'),
- progress = advanced.find('.progress'),
- progressBar = progress.find('.bar'),
- controlbar = advanced.find('.controlbar'),
+ var $advanced = $('#advanced'),
+ $holder = $advanced.find('.holder'),
+ $statusBar = $advanced.find('.status'),
+ $totalLabel = $statusBar.find('.totalcount'),
+ $properLabel = $statusBar.find('.propercount'),
+ $brokenLabel = $statusBar.find('.brokencount'),
+ $dfdLabel = $statusBar.find('.dfdstatus'),
+ $progress = $advanced.find('.progress'),
+ $progressBar = $progress.find('.bar'),
+ $controlbar = $advanced.find('.controlbar'),
broken_urls = [
'missing.jpg',
'absent.png',
@@ -109,49 +109,49 @@ jQuery(function($){
function checkImages(){
// Reset status & progress bar
- holder.children().removeClass();
- dfdLabel.removeClass('label-success label-important');
- progressBar.css({ width: 0 });
- statusBar.hide();
- progress.show();
+ $holder.children().removeClass();
+ $dfdLabel.removeClass('label-success label-important');
+ $progressBar.css({ width: 0 });
+ $statusBar.hide();
+ $progress.show();
- // Call imagesLoaded with callback, defer the determination, and save the deferred object
- var dfd = holder.imagesLoaded(function( $images, $proper, $broken ){
+ // Call imagesLoaded with multiple callbacks, and save the deferred object
+ var dfd = $holder.imagesLoaded({
+ callback: function($images, $proper, $broken){
- totalLabel.text( $images.length );
- properLabel.text( $proper.length );
- brokenLabel.text( $broken.length );
+ $totalLabel.text( $images.length );
+ $properLabel.text( $proper.length );
+ $brokenLabel.text( $broken.length );
- }, true);
+ },
+ progress: function (isBroken, $images, $proper, $broken) {
- // Deferred magic
- dfd.progress(function( isBroken, $images, $proper, $broken ){
+ var loadingSpan = this.siblings('.loading');
- var loadingSpan = this.siblings('.loading');
+ if( isBroken ){
+ loadingSpan.removeClass('loading').addClass('broken');
+ } else {
+ loadingSpan.fadeOut(200, function(){ $(this).remove(); });
+ }
- if( isBroken ){
- loadingSpan.removeClass('loading').addClass('broken');
- } else {
- loadingSpan.fadeOut(200, function(){ $(this).remove(); });
- }
+ $progressBar.css({ width: Math.round( ( ( $proper.length + $broken.length ) * 100 ) / $images.length ) + '%' });
- progressBar.css({ width: Math.round( ( ( $proper.length + $broken.length ) * 100 ) / $images.length ) + '%' });
+ }
+ });
- }).always(function(){
+ // Subsequent deferred method registration (not to be used with progress method)
+ dfd.always(function(){
var dfdState = dfd.state();
- dfdLabel.addClass( 'label-' + ( dfdState === 'resolved' ? 'success' : 'important' ) ).text( dfdState );
+ $dfdLabel.addClass( 'label-' + ( dfdState === 'resolved' ? 'success' : 'important' ) ).text( dfdState );
- progress.hide();
- statusBar.show();
- progressBar.css({ width: 0 });
+ $progress.hide();
+ $statusBar.show();
+ $progressBar.css({ width: 0 });
});
- // Start the determination process
- dfd.start();
-
}
// Add images to holder; 20% of images will be broken by default
@@ -161,7 +161,7 @@ jQuery(function($){
brokenPercentile = 10;
}
- holder.loremImages( 600, 800, { count: count, randomWidth: 100, itemBuilder: function( i, url ){
+ $holder.loremImages( 600, 800, { count: count, randomWidth: 100, itemBuilder: function( i, url ){
url = Math.random()*100 < brokenPercentile ? broken_urls[Math.floor( Math.random() * broken_urls.length )]+'?'+Math.round( Math.random()*1000 ) : url;
@@ -174,7 +174,7 @@ jQuery(function($){
}
// Controls
- controlbar.find('[data-action]').click(function(){
+ $controlbar.find('[data-action]').click(function(){
var el = $(this),
action = el.data('action'),
@@ -187,7 +187,7 @@ jQuery(function($){
break;
case 'removeImages':
- holder.children().slice(-count).remove();
+ $holder.children().slice(-count).remove();
checkImages();
break;
@@ -196,7 +196,7 @@ jQuery(function($){
});
// Click-delete images
- holder.on('click', 'li', function(){
+ $holder.on('click', 'li', function(){
$(this).fadeOut( 200, function(){
diff --git a/js/vendor/plugins.js b/js/vendor/plugins.js
index ff9cc4b..a7014e1 100644
--- a/js/vendor/plugins.js
+++ b/js/vendor/plugins.js
@@ -1,10 +1,10 @@
/*! Prism | http://prismjs.com */
-(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={languages:{insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);Object.prototype.toString.call(e)==="[object Object]"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent.trim();if(!f)return;f=f.replace(/&/g,"&").replace(//g,">").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data));l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r){return n.stringify(t.tokenize(e,r))},tokenize:function(e,n){var r=t.Token,i=[e],s=n.rest;if(s){for(var o in s)n[o]=s[o];delete n.rest}e:for(var o in n){if(!n.hasOwnProperty(o)||!n[o])continue;var u=n[o],a=u.inside,f=!!u.lookbehind||0;u=u.pattern||u;for(var l=0;l