I have a request calling up a bunch of images like so:
<a href='www.domain1.com'><img src='../image/img1.png' onerror='imgError(this);'/></a>
<a href='www.domain2.com'><img src='../image/img2.png' onerror='imgError(this);'/></a>
The problem is when the call is made some of the images (~20%) are not ready yet. They need another second.
So in js or jquery what I would like to do is on error get the images that failed, wait 1 second, then try to load those failed images again. If they fail on the 2nd try -- oh well, Im okay with that. But Im not doing this correctly... Should I not be calling a timeout inside of another method in js?
function imgError(image) {
image.onerror = "";
image.src = image;
setTimeout(function () {
return true;
}, 1000);
}
Add a cache breaker.
function imgError(image) {
image.onerror = null;
setTimeout(function (){
image.src += '?' + +new Date;
}, 1000);
}
(This assumes your image URL doesn't already have a query string, per the example. If it does, a little more work is obviously required.)
Related
To make sure a document is ready before doing stuff, i do the following :
(function() {
var interval = window.setInterval(function() {
if("complete" === document.readyState) {
window.clearInterval(interval);
// Some stuff
}
}, 10);
})();
If somewhere in my code i create an image from JavaScript like this :
var image = new Image();
image.onload = function() {
// Some other stuff
};
image.src = 'some_url';
Will the check I perform on document.readyState also wait for "image" to be loaded, or will it just wait for the images present in the HTML code, and only those, to be loaded ?
Thanks in advance.
You don't need your setInterval.
From the MDN :
The load event fires at the end of the document loading process. At
this point, all of the objects in the document are in the DOM, and all
the images and sub-frames have finished loading.
You can simply do this for the statically included images :
window.onload = function() {
// Some stuff
};
As this doesn't take into account the images you create later, you may do this :
window.onload = function() {
var image = new Image();
image.onload = function(){
// Some stuff
};
image.src = 'some_url';
};
In jquery document.ready() function is call when entire html page is ready or we can say bind (in technical terms).
You should try with increasing Interval time. or include image load callback for performing the stuff.
Having read other people's questions I thought
window.onload=...
would answer my question. I have tried this but it executes the code the instant the page loads (not after the images load).
If it makes any difference the images are coming from a CDN and are not relative.
Anyone know a solution? (I'm not using jQuery)
Want a one-liner?
Promise.all(Array.from(document.images).filter(img => !img.complete).map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))).then(() => {
console.log('images finished loading');
});
Pretty backwards-compatible, works even in Firefox 52 and Chrome 49 (Windows XP era). Not in IE11, though.
Replace document.images with e.g. document.querySelectorAll(...) if you want to narrow the image list.
It uses onload and onerror for brevity. This might conflict with other code on the page if these handlers of the img elements are also set elsewhere (unlikely, but anyway). If you're not sure that your page doesn't use them and want to be safe, replace the part img.onload = img.onerror = resolve; with a lengthier one: img.addEventListener('load', resolve); img.addEventListener('error', resolve);.
It also doesn't test whether all images have loaded successfully (that there are no broken images). If you need this, here's some more advanced code:
Promise.all(Array.from(document.images).map(img => {
if (img.complete)
return Promise.resolve(img.naturalHeight !== 0);
return new Promise(resolve => {
img.addEventListener('load', () => resolve(true));
img.addEventListener('error', () => resolve(false));
});
})).then(results => {
if (results.every(res => res))
console.log('all images loaded successfully');
else
console.log('some images failed to load, all finished loading');
});
It waits until all images are either loaded or failed to load.
If you want to fail early, with the first broken image:
Promise.all(Array.from(document.images).map(img => {
if (img.complete)
if (img.naturalHeight !== 0)
return Promise.resolve();
else
return Promise.reject(img);
return new Promise((resolve, reject) => {
img.addEventListener('load', resolve);
img.addEventListener('error', () => reject(img));
});
})).then(() => {
console.log('all images loaded successfully');
}, badImg => {
console.log('some image failed to load, others may still be loading');
console.log('first broken image:', badImg);
});
Two latest code blocks use naturalHeight to detect broken images among the already loaded ones. This method generally works, but has some drawbacks: it is said to not work when the image URL is set via CSS content property and when the image is an SVG that doesn't have its dimensions specified. If this is the case, you'll have to refactor your code so that you set up the event handlers before the images begin to load. This can be done by specifying onload and onerror right in the HTML or by creating the img elements in the JavaScript. Another way would be to set src as data-src in the HTML and perform img.src = img.dataset.src after attaching the handlers.
Here is a quick hack for modern browsers:
var imgs = document.images,
len = imgs.length,
counter = 0;
[].forEach.call( imgs, function( img ) {
if(img.complete)
incrementCounter();
else
img.addEventListener( 'load', incrementCounter, false );
} );
function incrementCounter() {
counter++;
if ( counter === len ) {
console.log( 'All images loaded!' );
}
}
Once all the images are loaded, your console will show "All images loaded!".
What this code does:
Load all the images in a variable from the document
Loop through these images
Add a listener for the "load" event on each of these images to run the incrementCounter function
The incrementCounter will increment the counter
If the counter has reached the length of images, that means they're all loaded
Having this code in a cross-browser way wouldn't be so hard, it's just cleaner like this.
Promise Pattern will solve this problem in a best possible manner i have reffered to when.js a open source library to solve the problem of all image loading
function loadImage (src) {
var deferred = when.defer(),
img = document.createElement('img');
img.onload = function () {
deferred.resolve(img);
};
img.onerror = function () {
deferred.reject(new Error('Image not found: ' + src));
};
img.src = src;
// Return only the promise, so that the caller cannot
// resolve, reject, or otherwise muck with the original deferred.
return deferred.promise;
}
function loadImages(srcs) {
// srcs = array of image src urls
// Array to hold deferred for each image being loaded
var deferreds = [];
// Call loadImage for each src, and push the returned deferred
// onto the deferreds array
for(var i = 0, len = srcs.length; i < len; i++) {
deferreds.push(loadImage(srcs[i]));
// NOTE: We could push only the promise, but since this array never
// leaves the loadImages function, it's ok to push the whole
// deferred. No one can gain access to them.
// However, if this array were exposed (e.g. via return value),
// it would be better to push only the promise.
}
// Return a new promise that will resolve only when all the
// promises in deferreds have resolved.
// NOTE: when.all returns only a promise, not a deferred, so
// this is safe to expose to the caller.
return when.all(deferreds);
}
loadImages(imageSrcArray).then(
function gotEm(imageArray) {
doFancyStuffWithImages(imageArray);
return imageArray.length;
},
function doh(err) {
handleError(err);
}
).then(
function shout (count) {
// This will happen after gotEm() and count is the value
// returned by gotEm()
alert('see my new ' + count + ' images?');
}
);
Using window.onload will not work because it fires once the page is loaded, however images are not included in this definition of loaded.
The general solution to this is the ImagesLoaded jQuery plugin.
If you're keen on not using jQuery at all, you could at least try converting this plugin into pure Javascript. At 93 significant lines of code and with good commenting, it shouldn't be a hard task to accomplish.
You can have the onload event on the image that can callback a function that does the processing... Regarding how to handle if all images are loaded, I am not sure if any of the following mechanisms will work:
have a function that counts the number of images for which onload is called, if this is equal to the total number of images on your page then do your necessary processing.
<title>Pre Loading...</title>
</head>
<style type="text/css" media="screen"> html, body{ margin:0;
padding:0; overflow:auto; }
#loading{ position:fixed; width:100%; height:100%; position:absolute; z-index:1; ackground:white url(loader.gif) no-repeat center; }**
</style>
<script> function loaded(){
document.getElementById("loading").style.visibility = "hidden"; }
</script>
<body onload="loaded();"> <div id="loading"></div>
<img id="img" src="avatar8.jpg" title="AVATAR" alt="Picture of Avatar
movie" />
</body>
I was looking for something like this, if you won't mind using setInterval this code is easy and straightforward. In my case I'm okay to use setInterval because it will run maybe 4-5 times.
const interval = setInterval(() => {
const allImagesLoaded = [...document.querySelectorAll('img')]
.map(x => x.complete)
.indexOf(false) === -1;
if (allImagesLoaded) {
window.print();
clearInterval(interval);
}
}, 500);
I was about to suggest the same thing Baz1nga said.
Also, another possible option that's maybe not as foolproof but easier to maintain is to pick the most important/biggest image and attach an onload event to only that one.
the advantage here is that there's less code to change if you later add more images to your page.
This works great:
$(function() {
$(window).bind("load", function() {
// code here
});
});
I'm doing something like this ->
$("<img>").attr("src", "path/to/image.jpg").load(function(){
$("#thediv").append(this);
});
so i can append the new image when the image is completely loaded
I have a timer to call this function with new parameters every 6 seconds.... but I want to call a function to load a new set of images but I have this old set running in background ( asynchronous ) and i need to stop that load so the image won't append beacuse i don't want that image and I'm trying to load a whole new different set of images... I was thinking on something like :
function loadNewSet(){
window.stop(); // i don't quite know if this exist or works but
//should stop all ajax request taking place at that time
loadImages(); // call some function to load the new set
}
/*//*///*///*///*///*///*/
To be more specific:
I have a div called thediv where I'll be placing my images
then I have set of images or an array
set1 = ['img1','img2','img3']
and
set2 = ['img4','img5','img6']
and i have thetimer set on the
$(document).ready(function(){
thetimer=setTimeout("nextSlide()",6000);
});
then i have nextSlide()
function nextSlide(){
//this is where i load the next image on the active set (set 1 or set 2)
img.load(function(){
thetimer=setTimeout("nextSlide()",6000); // and then i set the timer again
});
}
but when the nextSlide() function is called it'd be like idle while the new image is loading and after it loads it'd the things it's supposed to do on load() but what it while loading i call this function loadNewSet()
function loadNewSet(set){
clearTimeout(thetimer);
//this is wheere i want to stop any image loading called by any other function
stopAllImagesLoading();//example
//then change activeset
activeSet=set; // set1 or set2
nextSlide();
}
Don't quite know if I'm doing this the right way and may be I can't put down the procces here the way I'm doing it.
Thanks for your responses.
You can spawn up "worker processes" by using setTimeout.
var proc;
somefunc();
function somefunc() {
//do stuff
proc = setTimeout(somefunc, 6000);
}
//...
clearTimeout(proc); //Cancel the looping somefunc from executing.
What if instead of appending, you do something like....
<div id="myDiv">
<img id="myImg" style="display: none">
</div>
...
function loadImg(url){
$("#myImg").attr("src", url).load(function(){
if(url == $(this).attr(url)){
// The currently requested image has loaded. Show it.
$(this).show();
}
else{
// Finished loading an image that we don't need anymore. Do nothing.
}
});
}
So if you called:
loadImg("a.jpg");
loadImg("b.jpg");
a.jpg might or might not finish first, but it doesn't matter because once a.jpg finishes, nothing happens.
Try using a global variable to turn on or off the timer. For instance (pseudo-code only):
var gKeepRunning = true; //KEY TO ANSWER HERE
myRepeatingImageLoader(); //this is the method that will repeat
function myRepeatingImageLoader(){
$("<img>").attr("src", "path/to/image.jpg").load(function(){
$("#thediv").append(this);
});
if( gKeepRunning == true ) //KEY TO ANSWER HERE - WILL STOP WHEN SET TO FALSE
var junk = setTimeout('myRepeatingImageLoader();', 6000); //Repeat again
}
function loadNewSet(){
gKeepRunning == false; //KEY TO ANSWER HERE
loadImages();
}
Found the anwser here:
Javascript: Cancel/Stop Image Requests
if(window.stop !== undefined)
{
window.stop();
}
else if(document.execCommand !== undefined)
{
document.execCommand("Stop", false);
}
Using the following script to add an event listener, basically it says "Hide the #curtain div (whole page) until .bgImage is downloaded, then wait 1500ms and fade everything in"
My question is this - sometimes due to server lag or glitchy triggering of .bind("load") the page is not fading in. How can I add some kind of timeOut to the effect so that it triggers after X miliseconds if the .bind("load) event is not triggered?
$(document).ready(function(){
// #curtain DIV begins hidden then fades in after .bgImage (curtain) is loaded - prevents "ribbon" loading effect in Chrome
$('#curtain').hide();
$(".bgImage").bind("load", function () {$('#curtain').delay(1500).fadeIn(); });
});
$(".bgImage").bind("load", function () {
setTimeout(function() {
$('#curtain').delay(1500).fadeIn();
}, 500);
});
if you want to add a delay to the fadeIn animation this will help. cheers
What you could do is this:
var url = $('.bgImage').attr('src');
var img = new Image();
img.onload = function() {
$('#curtain').delay(1500).fadeIn();
};
img.src = url;
In my experience, as long as you set up the "onload" property of an "Image" object before you set the "src", the handler will always run.
edit — if you wanted to be sure that the thing would eventually fade in, then you could do something like this:
var allDone = false;
var url = $('.bgImage').attr('src');
var img = new Image();
img.onload = function() {
if (!allDone) {
$('#curtain').delay(1500).fadeIn();
allDone = true;
}
};
setTimeout(img.onload, 5000); // show the hidden stuff after 5 seconds, image or no image
img.src = url;
You can use something like this:
var timeout;
$(".bgImage").bind("load", function(){
clearTimeout(timeout);
// do something here
});
timeout = setTimeout(function(){
$(".bgImage").unbind("load");
// do something else instead
}, 10000);
and maybe also handle errors:
$(".bgImage").bind("error", function(){
// do something else here as well
});
UPDATE: I added code to cancel your timeout when the load does happen. Those two functions has to be able to cancel out each other.
Try adding this to your css:
#curtain {
display:none;
}
and using this in your document read():
$(.bgImage).load(function() {
$('#curtain').fadeIn(3000);
});
I'm currently working on a page that loads several images sequentially using setTimeout and onLoad. Basically when an image finishes loading, onLoad is triggered, which starts another image loading via a setTimeout call. Unfortunately, if an image load is interrupted for some reason, such as a subsequent ajax call, onload is never called and any images left to be loaded are not loaded. Is there any way in javascript to detect this situation? I've attempted to hook into onError and onAbort (on the image tag) and neither of these seem to be called.
queuePhotos: function(options)
{
this.init();
this.photo_urls = options.photo_urls;
this.photo_size = options.size
this.max_width = options['max_width'];
this.max_height = options['max_height'];
this.containers = options['containers'];
yd = YAHOO.util.Dom;
photo_tags = yd.getElementsByClassName('image_stub', 'div');
for( i in photo_tags )
{
//create new image
photo = new Image();
this.queue.push( { tag: photo_tags[i], photo: photo } );
}
setTimeout(photoLoader.prepareNextPhoto, 1);
},
prepareNextPhoto: function()
{
photo_tag_and_image = photoLoader.queue.shift();
if(photo_tag_and_image)
{
YAHOO.util.Event.addListener(photo_tag_and_image.photo, "load", photoLoader.appendPhoto, photo_tag_and_image, photoLoader);
photo_tag_and_image.photo.src = photoLoader.photo_urls[photo_tag_and_image.tag.id];
}
},
An AJAX call shouldn't cancel the loading of images. There could be something else going on here...
I don't think that you can readily detect the load failure of an image. You could fire off a timer with a certain timeout threshold to deem an image as failed if your timeout timer expires before you get the load event from the image.
You can call onload and onerror
myImage.onload = function(){ alert("loaded"); };
myImage.onerror = function(){ alert("whoops"); };
Eric