Image loading time - javascript

When I click on my website sometimes you can visually see the images slowly loading into the place. I don't really like this and if possible would like to prevent it from happening.
From reading round it seems preloading images is the solution I'm looking for? Let me know if that's correct or if there is a better way.
On this forum I see lots of answers to preloading images and below is a code I think works but I want to change it slightly:
var preloadImages = [ img/1.jpg,img/2.jpg];
for(var i = 0 ; i < preloadImages.length; i++) {
new Image().src = preloadImages[i];
}
I think this code above works but it requires me to type in every image source into an array. On my website there are lots of images and I will probably continue to add more. So is there a way to push all the image sources into the array without actually typing them in. So as I add more images the preloading take care of itself.

If you wanted to go down the route you already have you could search the entire DOM for tags first and then add them to the array.
Perhaps something along these lines:
images = []
$(body).find('img').each ->
imgSrc = $(this).attr('src')
image.push imgSrc
i = 0
while i < preloadImages.length
(new Image).src = preloadImages[i]
i++
That will need jQuery and probably some tweaking. I can't imagine this being the best approach however as you still need to wait for the DOM, then jQuery to load and then run through the entire DOM with the function(s).
If the issue is more that the content is moving around you can (and probably should) prevent this by giving the tag itself a height and width if you know it in advance.
Additionally it's always worth optimizing your images (either by hand or on build/compile using something like Gulp.js) if you haven't already. It's incredible how much you can reduce load times by simply by using the optimal image (in terms of size and format).

You can call below function after page load
function preloadImages(){
$('img').each(function(){
new Image().src = this.src;
});
}
You can use this function to set multiple after-page-load functions
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = function(){
func();
};
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
};
}
}
then add your function like this
addLoadEvent(preloadImages);
But I don't think you can really pre-load images like this. To really pre-load images, you have to start loading those images as soon as your DOM starts to render on the client-side. And that means loading them in the head section. But if you do that, because you don't have your html just yet, you will not be able to run the above function, because it needs the DOM to be fully downloaded. You have to hardcode your image sources into javascript like you already did, and put your function as the first thing in the head section.

Related

Why are my images holding up the page load, and how can I defer it/ make it asynchronous or lazy?

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)

Avoid wrong interpretation of source with document.createElement for dynamic sources

I have script that I would like visitors on my website to run when they load a web page. It looks like this:
window.onload = function(){
var pxl=document.createElement('img');
pxl.setAttribute('src', 'http://localhost:8080/getTrackingPixel')
document.body.appendChild(pxl);
}
Most of the times the source returns an image and it works fine. However, sometimes it returns this:
<html><body style="background-color:transparent"></body></html>
And I can't really change the fact that it might sometimes not return an image. How do I change the javascript so that it can handle the html response without any errors? It might be possible for me to predict when it happens though - but I haven't managed to find a good way to request the source and return the html either.
You can achieve it by using the javascript Image object which, unlike the createElement approach, allows you to fetch the src url before inserting the img in the DOM.
The onload event of the Image object won't fire if the loaded content isn't an img.
Here it is :
window.onload = function(){
var pxl = new Image();
pxl.onload = function(){
// is IMG
document.body.appendChild(pxl);
}
pxl.onerror = function(){
// is not IMG
// Meaning in your case : <html><body style="background-color:transparent"></body></html>
}
pxl.src = 'http://localhost:8080/getTrackingPixel';
}
(Note that your code also missed the semicolon ";" line 4)

Bug in my lazyload plugin for mootools

I want to implement a plug-in serial download pictures in MooTools. Let's say there are pictures with the img tag inside a div with the class imageswrapper. Need to consistently download each image after it loads the next and so on until all the images are not loaded.
window.addEvent('domready', function(){
// get all images in div with class 'imageswrapper'
var imagesArray = $$('.imageswrapper img');
var tempProperty = '';
// hide them and set them to the attribute 'data-src' to cancel the background download
for (var i=0; i<imagesArray.length; i++) {
tempProperty = imagesArray[i].getProperty('src');
imagesArray[i].removeProperty('src');
imagesArray[i].setProperty('data-src', tempProperty);
}
tempProperty = '';
var iterator = 0;
// select the block in which we will inject Pictures
var injDiv = $$('div.imageswrapper');
// recursive function that executes itself after a new image is loaded
function imgBomber() {
// exit conditions of the recursion
if (iterator > (imagesArray.length-1)) {
return false;
}
tempProperty = imagesArray[iterator].getProperty('data-src');
imagesArray[iterator].removeProperty('data-src');
imagesArray[iterator].setProperty('src', tempProperty);
imagesArray[iterator].addEvent('load', function() {
imagesArray[iterator].inject(injDiv);
iterator++;
imgBomber();
});
} ;
imgBomber();
});
There are several issues I can see here. You have not actually said what the issue is so... this is more of a code review / ideas for you until you post the actual problems with it (or a jsfiddle with it)
you run this code in domready where the browser may have already initiated the download of the images based upon the src property. you will be better off sending data-src from server directly before you even start
Probably biggest problem is: var injDiv = $$('div.imageswrapper'); will return a COLLECTION - so [<div.imageswrapper></div>, ..] - which cannot take an inject since the target can be multiple dom nodes. use var injDiv = document.getElement('div.imageswrapper'); instead.
there are issues with the load events and the .addEvent('load') for cross-browser. they need to be cleaned up after execution as in IE < 9, it will fire load every time an animated gif loops, for example. also, you don't have onerror and onabort handlers, which means your loader will stop at a 404 or any other unexpected response.
you should not use data-src to store the data, it's slow. MooTools has Element storage - use el.store('src', oldSource) and el.retrieve('src') and el.eliminate('src'). much faster.
you expose the iterator to the upper scope.
use mootools api - use .set() and .get() and not .getProperty() and .setProperty()
for (var i) iterators are unsafe to use for async operations. control flow of the app will continue to run and different operations may reference the wrong iterator index. looking at your code, this shouldn't be the case but you should use the mootools .each(fn(item, index), scope) from Elements / Array method.
Anyway, your problem has already been solved on several layers.
Eg, I wrote pre-loader - a framework agnostic image loader plugin that can download an array of images either in parallel or pipelined (like you are trying to) with onProgress etc events - see http://jsfiddle.net/dimitar/mFQm6/ - see the screenshots at the bottom of the readme.md:
MooTools solves this also (without the wait on previous image) via Asset.js - http://mootools.net/docs/more/Utilities/Assets#Asset:Asset-image and Asset.images for multiple. see the source for inspiration - https://github.com/mootools/mootools-more/blob/master/Source/Utilities/Assets.js
Here's an example doing this via my pre-loader class: http://jsfiddle.net/dimitar/JhpsH/
(function(){
var imagesToLoad = [],
imgDiv = document.getElement('div.injecthere');
$$('.imageswrapper img').each(function(el){
imagesToLoad.push(el.get('src'));
el.erase('src');
});
new preLoader(imagesToLoad, {
pipeline: true, // sequential loading like yours
onProgress: function(img, imageEl, index){
imgDiv.adopt(imageEl);
}
});
}());

Load list of images in sequential order

I'm working on a website which uses ExpressionEngine to create a list of images with img1, img2, img3 etc as the ID and creates an array with their sources imgAddresses[1], imgAddresses[2], imgAddresses[3] etc.
I'm attempting to create a function which loads the first image, then (when the first is completely loaded), load the second, third etc. The following is what I have so far:
function loadImage(counter) {
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload=loadImage(0);
It works when refreshing the page, but not when accessing the page via the URL. As far as I can tell from research, this is because the onload event is not fired when a cached image is loaded, and refreshing the page clears the cache, whereas accessing the page via the URL does not.
Research suggests that assigning the src of the image after declaring the onload event would get around this, but it does not seem to have solved it in this case. I was thinking that this may be because the onload event is recursive in this case.
Does anyone have any ideas on how to make sure the browser is loaded a fresh copy of the image, rather than a cached version? Or whether there is a better way to write this function? Thanks for any help!
EDIT: One solution that I have found is to change the img source assignment to:
i.src = imgAddresses[counter] + '?' + new Date().getTime();
This forces the user to load a fresh copy each time, which I guess is not so much a solution, but a workaround
The only thing I can say is that you are not attaching the document.onload handler correctly. I cannot tell if it will fix your issue because image.onload is not reliable in all browsers, however the onload should be set to a function reference and that's not what you are doing.
Instead, you should have something like:
function loadImage(counter) {
//initialize the counter to 0 if no counter was passed
counter = counter || 0;
var i = document.getElementById("img"+counter);
if(counter==imgAddresses.length) { return; }
i.onload = function(){
loadImage(counter+1)
};
i.src = imgAddresses[counter];
}
document.onload = loadImage; //instead of loadImage(0);
You can tell how the browser will manage the cached resources
Take a look to HTML5 cache approach:
HTML5: The cache manifest file
This way you can avoid the browser cache for the specified resources.

How does the javascript preloading work?

I don't want to know a way to preload images, I found much on the net, but I want to know how it works.
How is javascript able to preload images?
I mean, I tried a snippet from here, and even if it works, it doesn't seem to preload images.
When I check firebug, I can see that the image is loaded twice, once while the preloading, another time when displaying it!
To improve this code I'd like to know how it works.
Here is what i do:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(){
$('<img/>')[0].src = this;
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
then i do something like that:
preloader = function() {
preload(myImages);
}
$(document).ready(preloader);
Here is how i display/add the image :
$("li.works").click(function() {
$("#viewer").children().empty();
$('#viewer').children().append('<img src=\'images/ref/'+this.firstChild.id+'.jpg\' alt="'+this.firstChild.id+'" \/>')
$("#viewer").children().fadeIn();
Your basic Javascript preloader does this:
var image = new Image();
image.src = '/path/to/the/image.jpg';
The way it works is simply by creating a new Image object and setting the src of it, the browser is going to go grab the image. We're not adding this particular image to the browser, but when the time comes to show the image in the page via whatever method we have setup, the browser will already have it in its cache and will not go fetch it again. I can't really tell you why whatever you have isn't working this way without looking at the code, though.
One interesting gotcha that is discussed in this question is what happens when you have an array of images and try preloading them all by using the same Image object:
var images = ['image1.jpg','image2.jpg'];
var image = new Image();
for(var x = 0; x < images.length; x++) {
image.src = images[x];
}
This will only preload the last image as the rest will not have time to preload before the loop comes around again to change the source of the object. View an example of this. You should be able to instantly see the second image once you click on the button, but the first one will have to load as it didn't get a chance to preload when you try to view it.
As such, the proper way to do many at once would be:
var images = ['image1.jpg','image2.jpg'];
for(var x = 0; x < images.length; x++) {
var image = new Image();
image.src = images[x];
}
Javascript preloading works by taking advantage of the caching mechanism used by browsers.
The basic idea is that once a resource is downloaded, it is stored for a period of time locally on the client machine so that the browser doesn't have to retrieve the resource again from across the net, the next time it is required for display/use by the browser.
Your code is probably working just fine and you're just misinterpeting what Fire Bug is displaying.
To test this theory just hit www.google.com with a clean cache. I.e. clear your download history first.
The first time through everything will likely have a status of 200 OK. Meaning your browser requested the resource and the server sent it. If you look at the bottom on the Fire Bug window it will says how big the page was say 195Kb and how much of that was pulled from cache. In this case 0Kb.
Then reload the same page without clearing your cache, and you will still see the same number of requests in FireBug.
The reason for this is simple enough. The page hasn't changed and still needs all the same resources it needed before.
What is different is that for the majority of these requests the server returned a 304 Not Modified Status, so the browser checked it's cache to see if it already had the resource stored locally, which in this case it did from the previous page load. So the browser just pulled the resource from the local cache.
If you look at the bottom of the Fire Bug window you will see that page size is still the same (195Kb) but that the majority of it, in my case 188Kb, was pulled locally from cache.
So the cache did work and the second time i hit Google I saved 188Kb of download.
I'm sure you will find the same thing with preloading your images. The request is still made but if the server returns a status of 304 then you will see that the image is in fact just pulled from local cache and not the net.
So with caching, the advantage is NOT that you kill off all future resource requests, i.e. a Uri lookup is still made to the net but rather that if possible the browser will pull from the local cache to satisify the need for the content, rather than run around the net looking for it.
You may be confused by the concept of "preloading". If you have a bunch of images in your HTML with <img src="...">, they cannot be preloaded with Javascript, they just load with the page.
Preloading images with Javascript is about loading images not already in the document source, then displaying them later. They are loaded after the page has rendered for the first time. They are preloaded in order to eliminate/minimize loading time when it comes to making them appear, for example when changing an image on mouse rollover.
For most applications, it is usually better practice to use "CSS sprites" as a form of preloading, in lieu of Javascript. SO should have a ton of questions on this.
It just involves making a new DOM image object and setting the src attribute. Nothing clever and AFAIK, it has always worked for me.
Is it possible the second "load" firebug is showing you is it loading it from cache?
The index on the loop is only looking
at the first image. Change it to use
the index:
function preload(arrayOfImages) {
$(arrayOfImages).each(function(i){ // Note the argument
$('<img/>')[i].src = this; // Note the i
//(new Image()).src = this;
alert(this +' && ' + i++);
});
}
Edit: In retrospect, this was wrong and I can see you're trying to create image elements. I don't understand why the index is there at all, there need not be an index. I think the function should look like this:
function preload(arrayOfImages) {
$(arrayOfImages).each(function () {
$('<img/>').attr('src', this);
});
}
And to instantiate it, why not just do this:
$(function () { // Equivalent to $(document).ready()
preload(myImages);
});
JavaScript image preloading works because when a DOM element that contains an image is created, the image is downloaded and cached. Even if another request is made when the image is actually rendered from the HTML, the server will send back a 304 (not changed), and the browser will simply load the image from its cache.
Paolo suggests using the following notation to create an image object:
var image = new Image();
While this will work, the DOM-compliant way of doing this is:
var image = document.createElement('img');
image.setAttribute('src', 'path/to/image.jpg');
Which is the way it is being done in the script, except it's using jQuery's HTML string literal syntax to do it. Additionally, most modern browsers offer compatibility with the Image() constructor by simply calling DOM-standard methods. For example, if you open up the Google Chrome JavaScript console and type Image, this is what you'll get:
function Image() {
return document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
}
Chrome merely uses the native DOM methods to create an image element.

Categories