Reputation: 3507
I'm using the following code to insert some HTML into a div, and to preload any images that might be contained in that HTML (the html var's data is actually fetched from an AJAX request in the real code). This is to prevent the browser from loading the fetched HTML's images upon showing the div (using the slideDown event) - because this results in the effect's fluidity being broken as it loads image mid-transition. I suppose I could use an interlaced JPEG so that the dimensions of the image are known almost immediately, but obviously it'd be nice to get a cleaner method worked out. :P
var html = '<img src="images/test.jpg" alt="test" />';
$('div.content').hide().html(html);
$('div.content img').each(function(){
var img = new Image();
img.src = $(this).attr('src');
$(this).attr('src', img.src);
});
$('div.content').slideDown('normal');
I'm using the Image object and its subsequent assigning as per the advice given here, but unfortunately the image still isn't cached by the browser using this method, because the sildeDown() effect is still interrupted as the image loads.
Any help or alternative methods? Many thanks.
Progress! Turns out the browser was caching the image, I just wasn't giving it time to do so (it just needed a second to load with an alert() or setInterval()). Now introducing what is probably the messiest code ever - I am using an infinite loop to create that pause.
The new method extends the old code above by binding a function (that adds each image's src to an array) to that image's successful load event. It then gets stuck in an infinite loop as it waits until all the images have loaded and therefore appeared in the array. This seems to work as a way to synchronously pre-load images - but a problem remains; the while() loop for some reason cycles infinitely even once all the images are loaded, unless I add an alert() to pause it for a moment.
The new code:
var html = '<img src="images/test.jpg" alt="test" />';
$('div.content').hide().html(html);
// define usr variables object
$.usrvar = {};
// array of loaded images' urls
$.usrvar.images = [];
// boolean for whether this content has images (and if we should check they are all loaded later)
$.usrvar.hasimages = false;
// foreach of any images inside the content
$('div.content img').each(function(){
// if we're here then this content has images..
$.usrvar.hasimages = true;
// set this image's src to a var
var src = $(this).attr('src');
// add this image to our images array once it has finished loading
$(this).load(function(){
$.usrvar.images.push(src);
});
// create a new image
var img = new Image();
// set our new image's src
img.src = src;
});
// avoid this code if we don't have images in the content
if ($.usrvar.hasimages != false) {
// no images are yet loaded
$.usrvar.imagesloaded = false;
// repeatedly cycle (while() loop) through all images in content (each() loop)
while ($.usrvar.imagesloaded != true) {
$('div.content img').each(function(){
// get this loop's image src
var src = $(this).attr('src');
// if this src is in our images array, it must have finished loading
if ($.usrvar.images.indexOf(src) != -1) {
// set imagesloaded to trueai
$.usrvar.imagesloaded = true;
} else {
// without the pause caused by this alert(), this loop becomes infinite?!
alert('pause');
// this image is not yet loaded, so set var to false to initiate another loop
// (ignores whether imagesloaded has been set to true by another image, because ALL
// need to be loaded
$.usrvar.imagesloaded = false;
}
});
}
}
$('div.content').slideDown('normal');
Upvotes: 2
Views: 1214
Reputation:
This is what I use. As you can see by my points, I'm no pro, but I found this somewhere and it works great for me and seems much simpler than everything posted. Maybe I missed a requirement though. :)
var myImgs = ['images/nav/img1.png', 'images/nav/img2.png', 'images/nav/img3.png', 'images/nav/img4.png', 'images/expand.png', 'images/collapse.png'];
function preload(imgs) {
var img;
for (var i = 0, len = imgs.length; i < len; ++i) {
img = new Image();
img.src = imgs[i];
}
}
preload(myImgs);
Upvotes: 0
Reputation: 4248
I was playing with this and I created a slightly different solution. Instead of pushing images onto an array when they are loaded, you push them all onto an array in the loop, then in the load event you remove them from the array and call a 'finished' function. It checks if the images array is empty, and if it is then it clears up and shows the content.
var html = '< animg src="images/test.jpg" alt="test" />'; // not allowed to post images...
$('div.content').hide().html(html);
// preload images
// define usr variables object
$.usrvar = {};
// array of loaded images' urls
$.usrvar.images = [];
// initially no images
$.usrvar.hasimages = false;
$('div.content img').each(function() {
// if we're here then this content has images..
$.usrvar.hasimages = true;
// set this image's src to a var
var src = this.src;
// add this image to our images array
$.usrvar.images.push(src);
// callback when image has finished loading
$(this).load(function(){
var index = $.usrvar.images.indexOf(src);
$.usrvar.images.splice(index,1);
finish_loading();
});
// create a new image
var img = new Image();
// set our new image's src
img.src = src;
});
if(!$.usrvar.hasimages) finish_loading();
function finish_loading() {
if($.usrvar.hasimages) {
if($.usrvar.images.length > 0) return;
}
$('div.content').slideDown('normal');
}
Edit: Looking at Julien's post, his method is better. My method works in a similar way but like the original solution keeps track of images by an array of srcs rather than just a count (which is more efficient).
Edit 2: well I thought it was a better solution, but it seems it doesnt work for me. Maybe something to do with the load event getting called too close to each other. Sometimes it will work but sometimes it will hang when loading images, and the image counter never reaches zero. I've gone back to the method in my post above.
Edit 3: It appears it was the setTimeout that was causing the problem.
Upvotes: 0
Reputation: 11436
I made the following solution but it hasn't been tested, so you're warned ;)
// HTML (any formatting possible)
// no src for the images: it is provided in alt which is of the form url::actualAlt
var html = "<p><img alt='images/test.jpg::test' /><br />Some Text<br /><img alt='images/test2.jpg::test2' /></p>";
$(document).ready(function() {
// Reference to the content div (faster)
var divContent = $("div.content");
// Hide the div, put the HTML
divContent.hide().html(html);
// Webkit browsers sometimes do not parse immediately
// The setTimeout(function,1) gives them time to do so
setTimeout(function() {
// Get the images
var images = $("img",divContent);
// Find the number of images for synchronization purpose
var counter = images.length;
// Synchronizer
// will show the div when all images have been loaded
function imageLoaded() {
if (--counter<=0) $('div.content').slideDown('normal');
}
// Loading loop
// For each image in divContent
$.each(images,function() {
// Get the url & alt info from the alt attribute
var tmp = $(this).attr("alt").split("::");
// Set the alt attribute to its actual value
$(this).attr("alt",tmp[1]);
// Wire the onload & onerror handlers
this.onload = this.onerror = imageLoaded;
// Set the image src
this.src = tmp[0];
});
},1);
});
Upvotes: 1
Reputation: 10251
Create an interval/timeout and let it check your compterGenerated css-height, if it's autosized it'll begin from 0 and end to 100 (for example). But in Safari it loads the height before the image, so it'll propably not work in all browsers...
Upvotes: 0