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.
Edit - 21st Sept 09
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');
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);
});
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...
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.
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);
Related
I need to place separate gifs in multiple tab sections. Because I click through tabs which reveal each gif and tabs out of another, I require Javascript to reset the gif on a event handler. The script for this section works fine. I have written a resetGif function to correspond with this. This also works for the most part. The issue I'm finding with this particular script is that the reset is caching at some point. If I perform a hard refresh in browser the resetGif script performs correctly. If I perform a regular refresh, the resetGif script, runs only once.
I am not overly experienced with Javascript, so I have been unsure what to try next.
// Function resets gif through id
function resetGif(id) {
var img = document.getElementById(id);
var imageUrl = img.src;
img.src = "";
img.src = imageUrl;
}
I need the resetGif function to run/reset gif every-time it is being called regardless of whether I have hard refreshed or normal refresh. I need it to not cache.
If you want to force a reload of the image, append a timestamp to the src so it's unique, like this:
function resetGif(id) {
var img = document.getElementById(id);
var imageUrl = img.src;
img.src = '';
img.src = imageUrl.split('?')[0] + '?' + new Date().getTime();
}
Note that, in theory, the line setting src = '' should not be needed, unless there's some odd browser behaviour I'm not aware of.
function resetGif(id) {
var img = document.getElementById(id);
var randomString = Math.random().toString().split(".")[1];
var imageUrl = img.src;
img.src = ''; // will remove image source
img.src = imageUrl.split('?')[0] + '?' + randomString;
}
By generating a random string, you can force a reload of the image. and every time it is unique so I think this error will be solved.
I've scouted many forums and blogs and questions and sites and whatnot but cannot seem to find a solution that works for me - I am trying to load images using pure javascript without halting the rest of the page to load, and without relying on third party libraries.
On the site I work on, there may be between 0 - 30 images that may load, of different resolutions, and as you may imagine, might slow down performance to a halt on slower connections (which is what I am trying to prevent now - I want the user to see info on the page and worry less about images hooting up the performance on it)
on my latest attempt:
(function () {
// jquery is unavailable here. using javascript counterpart.
var carouselDivs = document.querySelectorAll('#caruselImagesDivs div[data-url]');
var carouselIndicators = document.querySelector('.carousel-indicators');
var carouselInner = document.querySelector('.carousel-inner');
for (var i = 0; i < carouselDivs.length; i++) {
var liIndicator = document.createElement('LI');
liIndicator.dataset.target = "#property_image_gallery";
liIndicator.dataset.slideTo = i + 1;
var divItem = document.createElement('DIV');
divItem.className = "item";
var image = document.createElement('IMG');
image.dataset.src = carouselDivs[i].dataset.url;
image.classname = 'img-responsive center-block';
// for some reason I thought this might work, but it hasn't.
image.onload = function () {
image.src = image.dataset.src;
image.onload = function () { };
}
image.src = '/Images/blankbeacon.jpg';
divItem.appendChild(image);
carouselIndicators.appendChild(liIndicator);
carouselInner.appendChild(divItem);
}
})();
I tried deferring the loading of the images too (the top code section hadn't had the onload event then):
function initImg() {
var imgs = document.querySelectorAll('#property_image_gallery .carousel-inner .item img');
for (var i = 0; i < imgs.length; i++) {
var imgSource = imgs[i].dataset.src;
imgs[i].src = imgSource;
}
}
window.onload = initImg
2 hours in. no results. I am stumped. What am I missing? how can I force the browser to just move on with life and load those images later on?
At first, you may load images one after one, using recursive functions:
function addimg(img){
img.onload=function(){
addimg(nextimg) ;
img.onload=null;//kill closure -> free the js memory
}
}
Start that if the html is loaded completely:
window.onload=addimg;
(pseudocode)
You can also use a image compressor tool to make the images load faster.
http://optimizilla.com/
This is a great article that might also help you
https://varvy.com/pagespeed/defer-images.html
Few suggestions:
If the images are not in the current viewport and are taking up too much initial bandwidth then i suggest to lazy load images when the user is in (or close to) the same viewport of the images.
You can also try deferring the images like what you are doing, but ensure the script is run right before the end body tag.
I also suggest doing things like making sure images are correctly compressed and resized (you have an image there that is 225kb which isnt ideal)
I'm developing a printing tool using HTML5 canvas. By open a new window and write the page canvas as image in new window document then finally print the window document.As a test, I've tried to print the pages(greater than 100 pages) in chrome latest version (Version 46.0.2490.71) it does not print the whole page. In chrome print preview window display only partial page like if we print the 110 pages document means it display only 24 or 38 pages(it display the page randomly). But the whole pages are added in the newly created window for printing.
I used the below code for printing the pages.
var _printWindow = window.open('');
_printWindow.document.write('<html><BODY>');
_printWindow.document.write('<center>');
for (var i = 1; i <= _totalPages; i++) {
var canvas = this._printpages(i);
_printWindow.document.write('<img src="' + canvas.toDataURL() + '"style="border:1px solid;height:"' + _pageHeight + '"px;margin:0px"><br />');
}
_printWindow.document.write('</center></body></html>');
_printWindow.document.close();
_printWindow.print();
You are calling print() directly after you have finished writing the <img> tags. However, it takes time for all those images to load (asynchronously). Thus, by the time you call print(), the images have not yet finished loading and some may not have had their height determined yet, resulting in an unexpected number of pages.
To fix this, you should call print() only after the onload event has fired on all of the image elements. Here is how I solved it:
var nImages = _totalPages;
function imageLoaded() {
if (--nImages === 0) {
_printWindow.print();
}
}
// ...snip...
// replace your `for` loop with the following:
for (var i = 1; i <= _totalPages; i++) {
var img = new Image;
img.addEventListener('load', imageLoaded);
img.src = /* get the data URL from the canvas */;
// here, add in your style properties
_printWindow.document.body.appendChild(img);
}
I am loading images from remote server.
how do i make sure that all images are loaded in correct order
also how do i make sure the alert is called only when all images are loaded
the below code does not load the images in correct order and calls the alert before the images are loaded.
$(window).load(function () {
$('.fancybox').each(function () {
var hh = $(this).attr('href');
var img = new Image();
img.src = hh;
function TIMG(i) {
if (img.complete != null && img.complete == true) {
$('.mag').append('<img src="' + hh + '" class="cls" />');
} else {
setTimeout(TIMG, 1000);
}
}
setTimeout(TIMG, 1000);
});
alert(hi);
});
Based on the new information that you just want to add them in order as soon as they are ready, you can do that like this
$(document).ready(function () {
// preload all the images as fast as possible
var imgs = [];
$('.fancybox').each(function () {
// get all images preloading immediately
// so there is the best chance they are available when needed
var img = new Image();
imgs.push(img);
img.onload = function() {
addReadyImages();
}
img.className = "cls";
img.src = $(this).attr('href');
});
function addReadyImages() {
// show all images that are ready up to here
while (imgs.length && imgs[0].complete === true) {
// append the loaded image from the start of the array
$('.mag').first().append(imgs[0]);
// remove the one we just did from the start of the array
imgs.shift();
}
}
});
Working demo: http://jsfiddle.net/jfriend00/sCHws/
This algorithm works as follows:
Start preloading all the images as soon as possible.
Store the image objects in an array in the order that the .fancybox items are encountered in your page
Set an onload handler for each image so we know immediately when it's ready
In the onload handler for any image, append all images at the front of the array that are ready and then remove them from the array
P.S. I've assumed there is only one .mag item and thus we don't need to make separate copies of the image objects (much more efficient that way - rather than creating new image objects). If that is not the case, then please disclose your HTML so we can see the whole problem.
Since browsers will normally not block when downloading images,the only way to ensure that the images are loaded in the right order would be to load them serially...using a queue and then only beginning the next after the previous has completed. That is definitely not recommended though since that could easily create a severe performance hit.
Waiting for when all of the images are loaded would likely best be done by the .promise() and related methods in jQuery: http://api.jquery.com/promise/, and watching for when the images are load()ed. If you particularly want to control the display of images (which is more likely what you'd be concerned with), then they could be displayed in sequence after they are loaded.
The following Code Works in every browser accept every version of IE, can someone please help! There seems to be a problem with the cat.onload method....I have done so many tests and am at a loss...any help would be appreciated
Thanks!
var bannerCount = 0;
var bannerImgsArr = new Array();
bannerImgsArr[0] ='image1.jpg';
bannerImgsArr[1] ='image2.jpg';
bannerImgsArr[2] ='image3.jpg';
bannerImgsArr[3] ='image4.jpg';
$(document).ready(function() {
preloadBannerImages();
});
function preloadBannerImages(){
//create new image object
var cat = new Image();
//give Image src attribute the image file name
cat.src =bannerImgsArr[bannerCount];
//if the counter represents the last image in the current gallery enter here:
if(bannerCount == 3){
//When the image has been loaded the following function is exicuted
cat.onload = function() {
//Removes the LOADING message hard coded into the html page;
document.getElementById("bigbox").removeChild(document.getElementById("loading"));
}
}else{
//when current image is finished loading increments the img index and
//calls the loading function again for the next image in the gallery
bannerCount++;
cat.onload =preloadBannerImages;
}
}
You are defining the onload handler for the image after setting the src property. Do the one before the other.
If that doesn't solve your problem, you really need to elaborate what exactly doesn't work.
Don't rely on the onload-event of images, it may not fire if the image was loaded before and comes from the cache
[edit]
The function most likely ends and calls itself again before the image is downloaded, so the image isn't cached. Load the images into a persistent store (e.g. array) that hangs around until they are all downlaoded and cached.
[/edit]
You don't have to wait for the DOM to be ready to load images:
var bannerCount = 0;
var bannerImgsArr = ['image1.jpg','image2.jpg','image3.jpg','image4.jpg'];
var imageArray = [];
for (var i=0, iLen=bannerImgsArr.length; i<iLen; i++) {
imageArray[i] = new Image();
imageArray[i].src = bannerImgsArr[i];
}
Now when the DOM is ready, you can use one of the preloaded images.
...
cat.src = imageArr[bannerCount].src;
...