I've searched high and low for a solution to this problem, extremely grateful to anyone who can shed some light.
Below is a simplified version of a function used to preload 25 images. It works fantastically well in IE9-10 and in Chrome.
In IE8 it gets partway through loading the images (e.g. 10/25) then just stops. If I then refresh the browser it will continue to load images, taking ~3 refreshes to complete. This should not be necessary!
On the odd occasion it does succeed in loading all images in one go. I tried to avoid using .load() as I've read it can be problematic in IE yet here I am just the same! I also tried using different images to rule out the image format as the cause.
I'm looking for either a fix to the below code or a better solution if someone has one.
Many thanks.
function loadAllImages() {
var imgArray = [ //image SRCs stored in array
'imgsrc01.jpg',
'imgsrc02.jpg',
'imgsrc03.jpg',
...
'imgsrc25.jpg'];
var noFinalImg = imgArray.length - 1;
var i = -1;
var waitTime = 10000;
if (imgArray.length === 0) {
return;
} else {
loadImage();
};
function loadImage() {
i++;
var url = imgArray[i];
var img = new Image();
var timer;
img.src = url;
img.id = "thumb" + i;
// image loaded
if (img.complete || img.readyState === 4) {
//Call image loaded function
console.log(i + "/" + noFinalImg);
//equivalent to loop next
if (i !== noFinalImg) {
loadImage();
};
// image not loaded
} else {
// handle 404 using setTimeout set at waitTime
timer = setTimeout(function () {
if (i !== noFinalImg) {
loadImage();
};
$(img).unbind('error load onreadystate');
}, waitTime);
$(img).bind('error load onreadystatechange', function (e) {
clearTimeout(timer);
if (e.type !== 'error') {
//Call image loaded function
console.log(i + "/" + noFinalImg);
};
if (i !== noFinalImg) {
loadImage();
};
});
};
};
};
Related
I have an update of multiple images using jQuery:
window.onload = function () {
setInterval(function () {
$(".image").each(function (index) {
base_url = $(this).attr('src').split('?rand=')[0];
address = base_url + '?rand=' + Math.random();
$(this).attr("src", address);
});
}, 10000);
};
I'm trying to improve it:
function update() {
setInterval(function () {
$(".cameragrid_").find("img").each(function (index) {
d = new Date();
var src = $(this)[0].src;
var state = $(this).context.readyState;
if (state == 'complete'); {
$(this).attr("src", src + d.getTime());
}
});
console.log('click');
}, 10000);
}
$(window).ready(function () {
$('.cameragrid_').hide();
});
$(window).load(function () {
$('.cameragrid_').show();
update();
});
I wanted to reduce the time from 10 to 3 seconds, but when I reduce this time, do not update all the images, my algorithm and the rest is not updated.
.
Is there any way to optimize it to run within 3 seconds ?
instead of having one timer for many images maybe you could try to have many timers, one for each image or at least a small batch of images.
window.onload = function () {
$(".image").each(function (index) {
setInterval(function () {
base_url = $(this).attr('src').split('?rand=')[0];
address = base_url + '?rand=' + Math.random();
$(this).attr("src", address);
}, 3000);
});
};
I've not tested this code but you can get the idea, maybe you'll have to add a $.proxy() somewhere to fix the this context.
testing if the image is displayed should be easier this way: https://www.customd.com/articles/13/checking-if-an-element-is-visible-on-screen-using-jquery
Hey have you tried https://tinypng.com/ this compress the image format, this will increase some loading time of image more over you can also work on making the sprite of 10 images into 1 in this way loading only one file and then indexing the sprite.
Never personally tried image-min but this is also worth looking.
Believe sprite could solve your problem in shot.
I ended that way:
setInterval(function(){
$( ".image" ).each(function( index ) {
var img = new Image();
img.src = $(this).attr('src');
if(img.complete){
base_url = $(this).attr('src').split('?rand=')[0];
address = base_url + '?rand=' + Math.random();
$(this).attr("src", address);
}
});
},500);
I am trying to create a function that will recursively try to reload an image until it either is successful, or a maximum amount of attempts is reached. I have created this function, but it doesn't work (is it due to the fact that the reference to the image has changed?):
function reload (image, URL, maxAttempts)
{
image.onerror = image.onabort = null;
if (maxAttempts > 0)
{
var newImg = new Image ();
newImg.src = URL;
newImg.onerror = image.onabort = reload (image, URL, maxAttempts - 1);
newImg.onload = function () {
newImg.onerror = newImg.onabort = null;
image = newImg;
}
}
else
{
alert ("Error loading image " + URL); /* DEBUG */
}
}
Which is used in the following manner:
var globalTestImage = new Image ();
reload (globalTestImage, "testIMG.jpg", 4);
Rather than it attempting to load "testIMG.jpg" four times, and waiting in between attempts, it instead tries to load it twice, and regardless of whether it was successful the second time around it will display the error message.
What am I doing there? More precisely, why is it acting the way it is, rather than retrying to load the image 4 times?
(function ($) {
var retries = 5; //<--retries
$( document).ready(function(){
$('img').one('error', function() {
var $image = $(this);
$image.attr('alt', 'Still didn\'t load');
if (typeof $image !== 'undefined') {
if (typeof $image.attr('src') !== 'undefined') {
$image.attr('src', retryToLoadImage($image));
}
}
});
});
function retryToLoadImage($img) {
var $newImg = $('<img>');
var $src = ($img.attr('src')) || '';
$newImg.attr('src', $src);
$newImg.one('error', function() {
window.setTimeout(function(){
if (retries > 0) {
retries--;
retryToLoadImage($newImg);
}
}, 1000); //<-retry interval
});
$newImg.one('load', function() {
return $newImg.attr('src');
});
}
})(jQuery);
Some code I wrote for the same case a while ago. Hope it helps you!
In the end I solve this issue in a simple (if inelegant) way:
try
{
canvas.getContext("2d").drawImage (testImage, 0, 0);
backgroundLoaded = true;
}
catch (err)
{
testImage = new Image ();
testImage.src = "placeholder.jpg";
}
The idea is that if an image failed to load, it will fail when rendering it on the canvas, producing an error. When such an error happens, we can create a new image and try again.
I'd like to initially load a "Caching" function for my Chrome app, then run the actual code, like so:
function preloader(){
hideDOM();
loadSpinner();
for(var i=0;i<x;i++)
caching (loading resources)...
}
function startAPP(){
showDOM();
stopSpinner();
loadAPP();
}
So what I need is the preloader first, then when the "for" loop ends, load the startAPP.
Here is a proof of concept that I cobbled together: (there might be bugs)
function preload_resources(callback) {
var images = [
"http://domain/img1.jpg",
"http://domain/img2.jpg",
"http://domain/img3.jpg"
];
// count the number of completed load events
var count = 0;
function done() {
count += 1;
if (count === images.length) { // all images loaded
callback();
}
}
// make a hidden div;
var div = document.createElement("div");
div.style.display = "none";
document.body.appendChild(div);
// add images to DOM
images.forEach(function (image_url) {
var img = document.createElement("img");
img.src = image_url;
// attach load event
img.addEventListener("load", done);
div.appendChild(img);
});
}
hideDOM();
loadSpinner();
preload_resources(function () {
showDOM();
stopSpinner();
loadAPP();
});
It loads images, in theory you can load anything you want.
i am still new to all the Javascript stuffs and i have to be honest that i have not yet experimented anything concerning my question.
I would like to know if there is a way or a plugin with jQuery to preloaded multiples images and call a function when the images are loaded?
You can use the load event for images, but they have a number of cross browser issues (it depends on what platforms you're targeting).
var allImgs = $("#container img").filter(function() { return ! this.complete; });
var allImgsLength = allImgs.length;
allImgs.on("load", function() {
if (--allImgsLength) {
// Images have loaded.
}
});
If you don't mind including a plugin, there is waitForImages, (disclaimer: written by me) which handles this relatively nicely. :)
You'd use it like...
$("#container").waitForImages(function() {
// Images have loaded.
});
If you don't want to use jQuery at all, you can adapt the first example. If you're only targeting modern browsers...
var allImgs = [].filter.call(document.querySelectorAll("#container img"),
function(img) {
return ! img.complete;
});
var allImgsLength = allImgs.length;
[].forEach.call(allImgs, function(img) {
img.addEventListener(function() {
if (--allImgsLength) {
// Images have loaded.
}
});
});
If you had to support old browsers...
var allImgs = document.getElementById("container").getElementsByTagName("img");
var allImgsLength = allImgs.length;
var i;
var eventCallback = function() {
if (--allImgsLength) {
// Images have loaded.
}
};
for (i = 0; i < allImgsLength; i++) {
if (allImgs[i].complete) {
allImgsLength--;
}
if (allImgs[i].addEventListener) {
allImgs[i].addEventListener("load", eventCallback);
} elseif (allImgs[i].attachEvent) {
allImgs[i].attachEvent("onload", eventCallback);
} else {
allImgs[i].onload = eventCallback;
}
}
How do I move this image http://svastara.info/.s/img/icon/download1.png in the front of Download now?
Should look something like this: image Download Now
var CountdownTimer = function( id, count, imgurl ) { this.construct(id, count, imgurl); }
CountdownTimer.prototype = {
construct: function(id,count,imgurl) {
this.id = id;
this.object = document.getElementById(id);
this.count = count;
this.interval = null;
this.counted = false;
this.img = new Image(); // preload
this.img.src = imgurl;
this.img.border = "0";
(function(obj) {
obj.object.onclick = function() {
return obj.onclick();
};
})(this);
},
tick: function() {
this.count--;
this.render();
if(this.count == 0){
clearInterval(this.interval);
this.interval = null;
this.object.appendChild(this.img);
}
},
onclick: function() {
if(!this.counted) {
this.counted = true;
this.render();
(function(obj) {
obj.interval = setInterval(function() {
obj.tick();
},1000);
})(this);
return false;
} else if(this.count == 0)
return true;
else
return false;
},
render: function() {
if(this.count > 0)
this.object.innerHTML = "Download (" + this.count + " second" + (this.count == 1 ? "" : "s") + ")";
else
this.object.innerHTML = "Download Now";
}
};
window.onload = function() {
var c = new CountdownTimer("delayed",3,"http://svastara.info/.s/img/icon/download1.png");
};
<div>
<a id="delayed" class="stop" href="http://www.epiclosers.com/">Download (30sec)</a>
</div>
Have a look at the insertBefore method, the existing text should be a child node of the anchor tag.
Having said that, I'm beginning to wonder why people are doing the fancy thing here... You're not unique, I see this sort of thing all the time. The code can be simplified by allowing HTML and CSS to help you. Put the image in the document, set the display to none, and turn it on when you need it. Also, the text after download can be in a span that is also updated as you require. Then whole thing can be managed with a fraction of the code. Final thought on the simplification, you can just disable the link until you're ready to allow.
Also, using a simple debugger in the client, I can change the count to 0 on the fly and bypass the logic altogether. Or, even easier, I can just turn off javascript and click the link. In other words, make sure you're enforcing it through other means that aren't in the client. It's always a bad idea to rely on the client to enforce policy, so back it up on the server side. You may be doing that, so please don't be offended by the comment.