I have created a list of Image object. Every Image's onload function is the same:
function tracker() {
++loadedImage;
if (loadedImage === total) ..
}
images[i].src = urls[i];
This function tries to see if all the images are loaded, if so, call another function. However, several sources are illegal (maybe not existing) so the onload function will never be called for those images. I want to increment loadedImage even if the object is given an illegal source. However can I know if an Image is given an illegal source?
Thank you.
Images also have an error event. You can hook it too and count failed loads from here.
If you plan to support IE8-, be aware that load events won't fire if the image happens to be already present in the browser's cache.
Depending on what you want to achieve (and when), window.onload might be an alternative. It fires, when the document and all resources are loaded
<img src="http://lorempixel.com/100/100"/>
<img src="http://lorempixel.com/a/b"/>
<img src="http://placehold.it/200x50"/>
window.onload = function(evt) {
console.log('window.onload');
}
var imgs = document.getElementsByTagName('img');
for (var i = 0; i < imgs.length; ++i) {
var img = imgs[i];
img.onload = function(evt) {
console.log('src=' + this.src + ', ready');
}
img.onerror = function(evt) {
console.log('src=' + this.src + ', error');
}
}
JSFiddle
Related
I have this script which should show the text "Loading..." while images are loading, then change the text to "loaded" when all images are loaded. I added a button to load new images to make sure that it works for dynamically loaded images as well.
This works perfectly in Chrome but in Firefox the "Loading..." text never appears. I have no idea why this would be. The page begins loading and not all images are loaded so it should create the text "Loading.." but it doesn't. Then when all images are done loading the text "Loading" appears.
I just don't get why one message would appear and the other wouldn't. Especially because there are no qualifications that have to be met before creating the "Loading..." text, it should just fire automatically.
jsfiddle Example | Full Page Example
$(document).ready(function() {
var checkComplete = function() {
if($('img').filter(function() {return $('img').prop('complete');}).length == $('img').length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
$('img').on('load',function() {
checkComplete();
});
$('#button').click(function() {
$('img.a').attr('src' , 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg');
$('img.b').attr( 'src' , 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg');
checkComplete();
});
checkComplete();
});
You have several issues in the code.
First off, the checkComplete() function is not written correctly. It should be this:
var checkComplete = function() {
var imgs = $('img');
if(imgs.filter(function() {return this.complete;}).length == imgs.length) {
$('.status').text('Loaded');
} else {
$('.status').text('Loading...');
}
};
The main fix here is that the filter callback needs to refer to this.complete, not to $('img').prop('complete') because you are trying to filter a single item at a time.
Second off, you are relying on both .complete and .load working correctly AFTER you've changed the .src value. This is explicitly one of the cases where they do not work properly in all browsers.
The bulletproof way to work around this is to create a new image object for the new images, set the onload handler before you set the .src value and when both onload handlers have fired, you will know that both new images are loaded and you can replace the once you have in the DOM with the new ones.
Here is a version that works in FF:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
imgA.className = "a";
imgB.className = "b";
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a").replaceWith(imgA);
$("img.b").replaceWith(imgB);
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo: http://jsfiddle.net/jfriend00/yy7GX/
If you want to preserve the original objects, you can use the newly created objects only for preloading the new images and then change .src after they've been preloaded like this:
$(document).ready(function() {
$('#button').click(function() {
var imgA = new Image();
var imgB = new Image();
var loaded = 0;
imgA.onload = imgB.onload = function() {
++loaded;
if (loaded == 2) {
$("img.a")[0].src = imgA.src;
$("img.b")[0].src = imgB.src;
$('.status').text('Loaded');
}
}
// the part with adding now to the end of the URL here is just for testing purposes to break the cache
// remove that part for deployment
var now = new Date().getTime();
imgA.src = 'http://farm9.staticflickr.com/8545/8675107979_ee12611e6e_o.jpg?' + now;
imgB.src = 'http://farm9.staticflickr.com/8382/8677371836_651f586c99_o.jpg?' + now;
$('.status').text('Loading...');
});
});
Working demo of this version: http://jsfiddle.net/jfriend00/ChSQ5/
From the jQuery API .load method
Caveats of the load event when used with images
A common challenge developers attempt to solve using the `.load()` shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
I'm having trouble finding any good information on how to make a javascript(or jquery) progress bar WITH text that tells you the percentage.
I don't want a plug in, I just want to know how it works so that I can adapt it to what I need. How do you preload images and get a variable for the number of images that are preloaded. Also, how do you change html/css and-or call a function, based on the number of images that are loaded already?
<img> elements have an onload event that fires once the image has fully loaded. Therefore, in js you can keep track of the number of images that have loaded vs the number remaining using this event.
Images also have corresponding onerror and onabort events that fire when the image fails to load or the download have been aborted (by the user pressing the 'x' button). You also need to keep track of them along with the onload event to keep track of image loading properly.
Additional answer:
A simple example in pure js:
var img_to_load = [ '/img/1.jpg', '/img/2.jpg' ];
var loaded_images = 0;
for (var i=0; i<img_to_load.length; i++) {
var img = document.createElement('img');
img.src = img_to_load[i];
img.style.display = 'hidden'; // don't display preloaded images
img.onload = function () {
loaded_images ++;
if (loaded_images == img_to_load.length) {
alert('done loading images');
}
else {
alert((100*loaded_images/img_to_load.length) + '% loaded');
}
}
document.body.appendChild(img);
}
The example above doesn't handle onerror or onabort for clarity but real world code should take care of them as well.
What about using something below:
$('#btnUpload').click(function() {
var bar = document.getElementById('progBar'),
fallback = document.getElementById('downloadProgress'),
loaded = 0;
var load = function() {
loaded += 1;
bar.value = loaded;
/* The below will be visible if the progress tag is not supported */
$(fallback).empty().append("HTML5 progress tag not supported: ");
$('#progUpdate').empty().append(loaded + "% loaded");
if (loaded == 100) {
clearInterval(beginLoad);
$('#progUpdate').empty().append("Upload Complete");
console.log('Load was performed.');
}
};
var beginLoad = setInterval(function() {
load();
}, 50);
});
JSFIDDLE
You might also want to try HTML5 progress element:
<section>
<p>Progress: <progress id="p" max=100><span>0</span>%</progress></p>
<script>
var progressBar = document.getElementById('p');
function updateProgress(newValue) {
progressBar.value = newValue;
progressBar.getElementsByTagName('span')[0].textContent = newValue;
} </script>
</section>
http://www.html5tutorial.info/html5-progress.php
Maybe a silly question but here goes anyway.
Example: Let's say I have one non-looping animated GIF and I have two img elements.
<img src="" id="slot1" />
<img src="" id="slot2" />
So I use a little javascript to change the source of the slot1.
function changE(x)
{var image=document.getElementById (x);
image.src="animated.gif";
}
someButtonGotClicked=changE('slot1');
that works fine. Gif plays from start to end but if I then change the src of slot2 to the same gif:
changE('slot2');
slot1 resets it's gif back to the start to sync with slot2 starting it's gif.
Now i'm aware I could copy the gif and have 2 seperate files to use and I know about sprite sheets but I'm curious If I can use one copy of a gif and have it used multiple times on a page without all instances of the gif being restarted everytime another img element recieves the same file as it's src?
Hope that wasn't confusing. Thanks.
Once an image is loaded in memory, all other objects that request that image, using the same URL, get a reference to the image from the browser cache. This avoids loading the same image multiple times. In the case of a gif, one of the meta data to be kept track of is the current frame, which is stored not at the <img> dom element, but rather at the browser level in the structure it uses to store that gif.
On load, that frame index is reset. So, while the browser is processing the gif loop, a second image loading sets the current frame index to the beginning, hence both images synchronize.
This is a browser implementation, and it seems most browsers follow this implementation. One advantage to this is that if you have thousands of little gifs (from the same URL) in one page, the browser would do a lot less computation to render them, because it would only change one frame index, not thousands.
Edit:
To fix your code you'd have to basically add something random at the end of your image.
function changE(x)
{var image=document.getElementById (x);
image.src="animated.gif?" + Math.random();
}
So the browser thinks this is a different image (i.e. different URL).
Try using a solution like so:
<img src="" id="slot1" class="animate" />
<img src="" id="slot2" class="animate" />
(function(doc, w) {
var changE, getElementsByClassName;
changE = function(img) {
img.src = "animated.gif";
};
getElementsByClassName = function(node, classname) {
if (node.getElementsByClassName) { // use native implementation if available
return node.getElementsByClassName(classname);
} else {
return (function getElementsByClass(searchClass, node) {
if (node == null)
node = doc;
var classElements = [],
els = node.getElementsByTagName("*"),
elsLen = els.length,
pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)"), i, j;
for (i = 0, j = 0; i < elsLen; i++) {
if (pattern.test(els[i].className)) {
classElements[j] = els[i];
j++;
}
}
return classElements;
})(classname, node);
}
};
w.onload = function() {
var imgs, i = 0, l;
imgs = getElementsByClassName(doc, 'animate');
l = imgs.length;
for (i; i < l; i++) {
imgs[i].onclick = function(e) { changE(this); };
}
};
})(document, window);
This will set a clicke event for each image with the calss name animate and the click event will only effect the specific image clicked.
I would like to do an animation with the images, but only when they are all loaded.
When I start the animation with images still loading, it looks bad.
The images are dynamically added to the dom by parsing a json requested by javascript on $(document).ready.
After the images are loaded I would like to get a callback.
my core code is like this:
$('.gallery-block').each(function () {
$.ajax({
url: $(this).data("url"),
dataType: 'json',
outerthis: this,
success: function (json) {
$(this.outerthis).data("json", json);
var i = json.length;
while (i--) {
var preload = new Image();
preload.src = json[i];
}
$(this.outerthis).data("loaded", "true");
}
});
});
As you can see I'm trying to preload the images, but I don't know how fickle they are this way. They are not attached to the dom or anything.
The other problem is that I wnat the line
$(this.outerthis).data("loaded", "true");
only to be executed when the images are really preloaded.
Should I iterate a variable on every image's callback?
And probably this.outerthis is a bad design pattern, but I'm new to javascript.
The images don't need to be connected to the DOM to be loaded. What you can do is hook up an onload event to each image (before you set the URL), which will acts as your callback. Just count up the number of responses to onload and you can figure out when they're all loaded.
Something like this (untested):
var i, image,
preload = [], loaded = 0,
images = ["a", "b"], count = images.length;
for(i = 0; i < count; i++) {
image = new Image();
image.onload = onImageLoaded;
image.src = images[i] + ".jpg";
preload[i] = image;
}
function onImageLoaded() {
loaded++;
if(loaded === count) {
alert("done");
}
}
I have a Javascript Map kit, and it use Image object to load images. Every time I move/resize the map, it will load many images. But I want to know how I can fire a function when all images are loaded after I do a move/resize action.
Is there a way that I can create a listener like "allimages.onload=function(){}" ?
Or is there any workaround?
You would have to maintain a count of how many images were yet to load, and call the final function when the outstanding image count reached 0. eg.
function nofunction() {}
function loadImages(srcs, callback) {
var images= [];
var imagen= srcs.length;
for (var i= 0; i<srcs.length; i++) {
var image= new Image();
image.onload=image.onerror= function() {
imagen--;
if (imagen==0) {
this.onload=this.onerror= nofunction;
callback();
}
};
image.src= srcs[i];
images.push(image);
}
return images;
}
...
var images= loadImages(['/img/1.gif', '/img/2.gif'], function() {
alert('yay');
});
for (var i= 0; i<images.length; i++)
mapelement.appendChild(images[i]);
A while ago I have written a blog post about detecting the load of a single image. You can use the example to build your function that checks if all images are loaded. I hope it will help.