Lazy Load function broken - javascript

I have this lazy loading function. On execution it first loads a small version of the image and later loads the full version. I needed this to quickly give the user a working slideshow before starting to load the really big images.
It all worked before I added in the last if statement:
if (!$(this).hasClass('lazyLoaded'))
And the mutations to the class after loading each image:
$(this).addClass('lazyLoaded');
I want to check if the images had been loaded before. I need this, because else on a next execution, the script would replace the full sized images with the smaller images and then later reloading the large images.
When I add this extra if statement and the 2 .addClass operations, the script stops working. I have no clue why, can you help me?
A weird side-effect I also noticed is the counting of the image length was 1 too high.
html:
<img src="img/3x2.gif" data-src="img/photo/event.jpg" alt="" />
<img src="img/3x2.gif" data-src="img/photo/wedding.jpg" alt="" />
<img src="img/3x2.gif" data-src="img/photo/portrait.jpg" alt="" />
jQuery:
$.fn.lazyLoad = function(){
if($(this).is('img[data-src]')) {
var lazy = $(this);
} else {
var lazy = $(this).find('img[data-src]');
};
var lazyLength = $(lazy).length;
console.log('lazyLength = ' + lazyLength);
var lazyNumber = 0
$(lazy).each(function(){
if (!$(this).hasClass('lazyLoaded')) {
if (loadTime > 300) {
var src = $(this).data('src');
var srcPath = src.slice(0, -4);
var srcExt = src.slice(-4);
var srcNew = srcPath + '_s' + srcExt;
$(this).one('load', function(){
lazyNumber++;
console.log('lazyNumber = ' + lazyNumber);
if (lazyNumber >= lazyLength) {
console.log('all small images have been loaded');
console.log('setting Timeout for 500ms');
setTimeout(function() {
console.log('beginning loading of large images...');
$(lazy).each(function(){
$(this).one('load', function(){
console.log('large images loaded');
$(this).addClass('lazyLoaded');
}).attr('src', $(this).data('src'));
});
}, 500);
};
}).attr('src', srcNew);
console.log('small image loaded');
} else {
$(this).one('load', function(){
$(this).addClass('lazyLoaded');
console.log('large images loaded immediately');
}).attr('src', $(this).data('src'));
};
};
});
};

I think the error was created by adding the if statement within the .each loop. When I thought about it, it was not the most efficient way either.
I just have to test the images for both having a data-src attribute and not being loaded already (thus having the class '.lazyLoaded', before passing them to the var lazy array.
So I still add the '.lazyLoaded' class on load of the large image
$(this).one('load', function(){
console.log('large images loaded');
$(this).addClass('lazyLoaded');
}).attr('src', $(this).data('src'));
And in the beginning of the function I added .not('.lazyLoaded')
if($(this).is('img[data-src]')) {
var lazy = $(this).not('.lazyLoaded');
} else {
var lazy = $(this).find('img[data-src]').not('.lazyLoaded');
};
Just posting the entire working code, maybe it is of use to someone with a similar problem:
$.fn.lazyLoad = function(){
if($(this).is('img[data-src]')) {
var lazy = $(this).not('.lazyLoaded');
} else {
var lazy = $(this).find('img[data-src]').not('.lazyLoaded');
};
var lazyLength = $(lazy).length;
console.log('lazyLength = ' + lazyLength);
var lazyNumber = 0
console.log('lazyNumber = ' + lazyNumber);
$(lazy).each(function(){
if (loadTime > 300) {
var src = $(this).data('src');
var srcPath = src.slice(0, -4);
var srcExt = src.slice(-4);
var srcNew = srcPath + '_s' + srcExt;
$(this).one('load', function(){
lazyNumber++;
console.log('lazyNumber = ' + lazyNumber);
if (lazyNumber >= lazyLength) {
console.log('all small images have been loaded');
console.log('setting Timeout for 500ms');
setTimeout(function() {
console.log('beginning loading of large imags...');
$(lazy).each(function(){
$(this).one('load', function(){
console.log('large images loaded');
$(this).addClass('lazyLoaded');
}).attr('src', $(this).data('src'));
});
}, 500);
};
}).attr('src', srcNew);
console.log('small image loaded');
} else {
$(this).one('load', function(){
console.log('large images loaded immediately');
$(this).addClass('lazyLoaded');
}).attr('src', $(this).data('src'));
};
});
};
$('.lazy').lazyLoad();

Related

Reload multiple images using jQuery? How to optimize?

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);

JS custom caching function?

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.

Preloading website images

I have an image preloading script which loads all the images with a loading bar
Once all the images are loaded it will then show the entire website.
Problem
The percentage stops at 80%.
See here for example: nanomitetech.com/projects/stillmens/preloading.html
JS
(function ($) {
var imgList = [];
$.extend({
preload: function (imgArr, option) {
var setting = $.extend({
init: function (loaded, total) {},
loaded: function (img, loaded, total) {},
loaded_all: function (loaded, total) {}
}, option);
var total = imgArr.length;
var loaded = 0;
setting.init(0, total);
for (var i in imgArr) {
imgList.push($("<img />")
.attr("src", imgArr[i])
.load(function () {
loaded++;
setting.loaded(this, loaded, total);
if (loaded == total) {
setting.loaded_all(loaded, total);
}
}));
}
}
});
})(jQuery);
One of your images in your array has the wrong src:
http://nanomitetech.com/projects/stillmens/images/benefits.png.png
Should be:
http://nanomitetech.com/projects/stillmens/images/benefits.png
This precludes the image from loading, so your load event listener doesn't get called for this broken image, and your loaded counter doesn't increment.
4/5 successful image loads = 80%

Image Preloader Getting Stuck in IE8

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();
};
});
};
};
};

Load Image and Fade In, but in sequential way

I'm trying to load my images and fade in. Its working. But lets suppose that I have 4 images. Right now, my script its working by loading the last image, not the first. How can I make my js load the first image, and just start loading the second after the first has been loaded?
Here's my code:
$(function(){
$("a").click(function(){
$("#load").load('load.html', function() {
$('#load img').hide();
alert($("#load img").length);
$('#load img').each( function(){
$(this).on('load', function () {
$(this).fadeIn();
});
});
});
return false;
});
});
See http://jsfiddle.net/5uNGR/15/
Try something where you are not trying to perform a fadeIn on each image as they load, but test the ok-ness of the next image in your animation queue each time ANY load event happens, then on the animation callback, see if the next one is ready. Here is a script that should work.
BTW, see http://www.sajithmr.me/javascript-check-an-image-is-loaded-or-not for more on image readiness testing.
$(function () {
// util fn to test if image loaded properly
var isImgLoadOk = function (img) {
if (!img.complete) { return false; }
if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) {
return false;
}
return true;
};
$("a").on('click', function () {
$("#load").load('load.html', function () {
var imgs = $('#load img').hide(), //create image array and hide (chaining)
animIndex = 0, //position in the animation queue
isFading = false; //track state of whether an animation is in prog
// this is a load handler AND is called in the fadeIn callback if
// not at last image in the collection
var fadeNextImg = function () {
// make sure a load event didn't happen in the middle of an animation
if (isFading) return;
// last image in animation queue?
var isLast = animIndex == imgs.length - 1;
// get the raw image out of the collection and test if it is loaded happily
var targetImage = imgs[animIndex];
if (isImgLoadOk(targetImage)) {
// jQuery-up the htmlImage
targetImage = $(targetImage);
// increment the queue
animIndex++;
// set animation state - this will ignore load events
isFading = true;
// fade in the img
targetImage.fadeIn('slow', function () {
isFading = false;
// test to see if next image is ready to load by
// recursively calling the parent fn
if (!isLast) fadeNextImg();
});
}
};
imgs.on('load', fadeNextImg);
});
return false;
});
});
Ron's solution is a little over-engineered IMO. If your intention is indeed to load all of the images before the animation starts, then you could do something similar to the following:
$(function(){
$("a").click(function(){
$("#load").load('load.html', function() {
$('#load img').hide(); // I would recommend using css to hide the image instead
alert($("#load img").length);
$("#load img").each(function(i, image) {
setTimeout(function() { // For each image, set increasingly large timeout before fadeIn
$(this).fadeIn();
}, 2000 * i);
});
});
return false;
});
});

Categories