Issue with image load recursive chain on slow network/mobile - javascript

So basically I have a page with a few sections. Each sections contains 5-30 image icons that are fairly small in size but large enough that I want to manipulate the load order of them.
I'm using a library called collagePlus which allows me to give it a list of elements which it will collage into a nice image grid. The idea here is to start at the first section of images, load the images, display the grid, then move on to the next section of images all the way to the end. Once we reach the end I pass a callback which initializes a gallery library I am using called fancybox which simply makes all the images interactive when clicked(but does not modify the icons state/styles).
var fancyCollage = new function() { /* A mixed usage of fancybox.js and collagePlus.js */
var collageOpts = {
'targetHeight': 200,
'fadeSpeed': 2000,
'allowPartialLastRow': true
};
// This is just for the case that the browser window is resized
var resizeTimer = null;
$(window).bind('resize', function() {
resetCollage(); // resize all collages
});
// Here we apply the actual CollagePlus plugin
var collage = function(elems) {
if (!elems)
elems = $('.Collage');
elems.removeWhitespace().collagePlus(collageOpts);
};
var resetCollage = function(elems) {
// hide all the images until we resize them
$('.Collage .Image_Wrapper').css("opacity", 0);
// set a timer to re-apply the plugin
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
collage(elems);
}, 200);
};
var setFancyBox = function() {
$(".covers").fancybox({/*options*/});
};
this.init = function(opts) {
if (opts != null) {
if (opts.height) {
collageOpts.targetHeight = opts.height;
}
}
$(document).ready(function() {
// some recursive functional funk
// basically goes through each section then each image in each section and loads the image and recurses onto the next image or section
function loadImage(images, imgIndex, sections, sectIndex, callback) {
if (sectIndex == sections.length) {
return callback();
}
if (imgIndex == images.length) {
var c = sections.eq(sectIndex);
collage(c);
images = sections.eq(sectIndex + 1).find("img.preload");
return loadImage(images, 0, sections, sectIndex + 1, callback);
}
var src = images.eq(imgIndex).data("src");
var img = new Image();
img.onload = img.onerror = function() {
images[imgIndex].src = src; // once the image is loaded set the UI element's source
loadImage(images, imgIndex + 1, sections, sectIndex, callback)
};
img.src = src; // load the image in the background
}
var firstImgList = $(".Collage").eq(0).find("img.preload");
loadImage(firstImgList, 0, $(".Collage"), 0, setFancyBox);
});
}
}
From my galleries I then call the init function.
It seems like my recursive chain being triggered by img.onload or img.onerror is not working properly if the images take a while to load(on slow networks or mobile). I'm not sure what I'm missing here so if anyone can chip in that would be great!
If it isn't clear what is going wrong from the code I posted you can see a live example here: https://www.yuvalboss.com/albums/olympic-traverse-august-2017
It works quite well on my desktop, but on my Nexus 5x it does not work and seems like the finally few collage calls are not happening. I've spent too long on this now so opening this up to see if I can get some help. Thanks everyone!

Whooooo I figured it out!
Was getting this issue which I'm still unsure about what it means
[Violation] Forced reflow while executing JavaScript took 43ms
Moved this into the callback that happens only once all images are loaded
$(window).bind('resize', function() {
resetCollage(); // resize all collages
});
For some reason it was getting called early even if the browser never resized causing collage to get called when no elements existed yet.
If anyone has any informative input as to why I was getting this js violation would be great to know so I can make a better fix but for now this works :):):)

Related

Image source not changing with JavaScript

Please answer this question, as I am struggling a lot with it.
I am trying to change image source on mouse over. I am able to do it, but image is not displaying on page.
I am trying to change image source to cross domain URL. I can see that in DOM image source is changing but on page its not.
I have tried all solutions mentioned in LINK, but none of them is working.
Please let me solution to problem.
NOTE:
I can see in network tab image is taking some time to download (about 1 sec).
It is an intermediate issue, sometime image is loading and sometimes its not
CODE:
document.getElementsByTagName('img')[0].addEventListener('mouseover', function()
{
document.getElementsByTagName('img')[0].setAttribute('src', 'url/of/the/image');
});
have you tried loading images before everything else?
function initImages(){
var imC = 0;
var imN = 0;
for ( var i in Images ) imN++;
for(var i in Images){
var t=Images[i];
Images[i]=new Image();
Images[i].src=t;
Images[i].onload = function (){
imC++;
if(imC == imN){
console.log("Load Completed");
preloaded = 1;
}
}
}
}
and
var Images = {
one image: "path/to/1.png",
....
}
then
if( preloaded == 1 ){
start_your_page();
}
Here the code that will remove the img tag and replace it with a new one:
document.getElementsByTagName('img')[0].addEventListener('mouseover', function() {
var parent = document.getElementsByTagName('img')[0].parentElement;
parent.removeChild(document.getElementsByTagName('img')[0]);
var new_img = document.createElement("img");
new_img.src = "https://upload.wikimedia.org/wikipedia/commons/6/69/600x400_kastra.jpg";
parent.appendChild(new_img);
});
<img src="https://www.w3schools.com/w3images/fjords.jpg">
I resolved the issue using code:
function displayImage() {
let image = new image();
image.src="source/of/image/returned/from/service";
image.addEventListener('load', function () {
document.getElementsByTagName('img')[0].src = image.src;
},false);
}
Here in code, I am attaching load event to image, source of image will be changed after image is loaded.

Preloading images to play animation

I have to preload approximately 900 images to play the required animation based on the user selection. But before that, I have to check, which images I have to preload. I have approximately 5400 images, which I have extracted from the videos using ffmpeg. And based on the user selection I have to preload the required images. I am also showing how much percentage of preloading is completed.
Below is the code I am using to preload 900 the images based on the user selection.
function preloadImages(ImagesURL, callback) {
var img_i;
var img_j;
var loaded = 0;
for (img_i = 0, img_j = ImagesURL.length; img_i < img_j; img_i++) {
(function (img, src) {
img.onload = function () {
updateProgress(); // to show how much is completed
if (++loaded == ImagesURL.length && callback) {
callback();
}
};
img.onerror = function () {
showCustomMessage("Error occurred while loading files, please refresh the page and try again.");
};
img.onabort = function () {};
img.src = src;
} (new Image(), ImagesURL[img_i]));
}
}
Now I want to know is there any better method other than this? Or should I use AJAX instead of that, because this function is running absolutely fine on the desktop's but the same method is eating up lot of resources while running on the iPad or handheld devices.

jQuery or Javascript check if image loaded

I know there is a lot of these on Stackoverflow but I haven't found one that works for me in a recent version of jquery (1.10.2).
I did try:
$(".lazy").load(function (){}
But I believe after some research using .load to detect image load is deprecated in jQuery 1.8. What I need to do is fire an image resize function once the images are loaded. I don't have control over the
HTML and at the moment I am having to add the image dimensions via jQuery by attaching an attribute (via .attr()) once page loads so that I can use lazyload js.
The problem is that I need an accurate way to hold off all my various scripts until the image has loaded properly else the functions sometimes fire before every image had loaded. I have tried using $(window).load(function (){}); however it sometimes still fires before every image had loaded.
I usually do this:
var image = new Image();
image.onload = function () {
console.info("Image loaded !");
//do something...
}
image.onerror = function () {
console.error("Cannot load image");
//do something else...
}
image.src = "/images/blah/foo.jpg";
Remember that the loading is asynchronous so you have to continue the script inside the onload and onerror events.
There's also a useful .complete property of an image object, you can use it if you have already set the .src of your <img> before attaching to it any event listeners:
var img=document.getElementById('myimg');
var func=function(){
// do your code here
// `this` refers to the img object
};
if(img.complete){
func.call(img);
}
else{
img.onload=func;
}
Reference: http://www.w3schools.com/jsref/prop_img_complete.asp
I would give the images that require this constraint a class like mustLoad where:
<img class="mustLoad" src="..." alt="" />
and then create a generic image load handler function, such as:
$('img.mustLoad').on('load',function(){
/* Fire your image resize code here */
});
Edit:
In response to your comments about deprecating .load() above, .load() was deprecated, in favor of .on('load') to reduce ambiguity between the onLoad event and Ajax loading.
In the case of waiting of loading multiple images:
var images = $("#div-with-images img");
var unloaded = images.length;
images.on('load', function(){
-- unloaded;
if (!unloaded) {
// here all images loaded, do your stuff
}
});
What I need to do is fire an image resize function once the images are loaded.
Are you sure that you need the image to be loaded? Waiting for an image to load before resizing it can cause a large jump in the page layout, especially if the images have large file sizes, such as animated GIFs.
Usually, for an image resize, you only need to know the intrinsic dimensions of the image. While there is no event to tell you this, it's easy enough to poll the images for the data. Something like this could be particularly effective:
<img src="..." data-resizeme="123" />
(function() {
var images, l, i, tmp;
if( document.querySelectorAll) {
images = [].slice.call(document.querySelectorAll("img[data-resizeme]"),0);
}
else {
tmp = document.getElementsByTagName("img");
images = [];
// browser compatibility is fun!
for( i=tmp.length-1; i>=0; i--) {
if( tmp[i].getAttribute("data-resizeme")) images.unshift(tmp[i]);
}
}
for( i=images.length-1; i>=0; i--) {
images[i].onload = resizeImage;
images[i].onerror = cancelImageResize;
}
var timer = setInterval(function() {
for( i=images.length-1; i>=0; i--) {
if( images[i].width) {
resizeImage.call(images[i]);
images[i].onload = null;
cancelImageResize.call(images[i]);
}
}
if( images.length == 0) clearInterval(timer);
},100); // adjust granularity as needed - lower number is more responsive.
function cancelImageResize() {
var i;
for( i=images.length-1; i>=0; i--) {
if( images[i] == this) {
images.splice(i,1);
break;
}
}
}
function resizeImage() {
console.log("Image "+this.src+" is "+this.width+"x"+this.height);
}
})();
Hope this helps!

JavaScript waiting until an image is fully loaded before continuing script

I've been looking around a lot of JavaScript answers but I haven't found one that really answers my problem yet. What I'm trying to do is load an image, grab the pixel data, perform an analysis, and then load another image to repeat the process.
My problem is that I can't preload all of the images because this script has to be able to work on large amounts of images and preloading could be too resource heavy. So I'm stuck trying to load a new image each time through a loop, but I'm stuck with a race condition between the image loading and the script drawing it to the canvas's context. At least I'm pretty sure that's what is happening because the script will work fine with the images precached (for example if I refresh after loading the page previously).
As you'll see there are several lines of code commented out because I'm incredibly new to JavaScript and they weren't working the way I thought they would, but I didn't want to forget about them if I needed the functionality later.
This is the snippet of code that I believe is giving rise to the problem:
EDIT: So I got my function to work after following a suggestion
function myFunction(imageURLarray) {
var canvas = document.getElementById('imagecanvas');
console.log("Canvas Grabbed");
if (!canvas || !canvas.getContext) {
return;
}
var context = canvas.getContext('2d');
if (!context || !context.putImageData) {
return;
}
window.loadedImageCount = 0;
loadImages(context, canvas.width, canvas.height, imageURLarray, 0);
}
function loadImages(context, width, height, imageURLarray, currentIndex) {
if (imageURLarray.length == 0 || imageURLarray.length == currentIndex) {
return false;
}
if (typeof currentIndex == 'undefined') {
currentIndex = 0;
}
var currentimage = new Image();
currentimage.src = imageURLarray[currentIndex];
var tempindex = currentIndex;
currentimage.onload = function(e) {
// Function code here
window.loadedImageCount++;
if (loadedImageCount == imageURLarray.length) {
// Function that happens after all images are loaded here
}
}
currentIndex++;
loadImages(context, width, height, imageURLarray, currentIndex);
return;
}
Maybe this will help:
currentimage.onload = function(e){
// code, run after image load
}
If it is necessary to wait for the image to load, the following code will load the next image (currentIndex is your "img" variable):
var loadImages = function(imageURLarray, currentIndex){
if (imageURLarray.length == 0 || imageURLarray.length == currentIndex) return false;
if (typeof currentIndex == 'undefined'){
currentIndex = 0;
}
// your top code
currentimage.onload = function(e){
// code, run after image load
loadImages(imageURLArray, currentIndex++);
}
}
Instead of a "for" loop, use for example this function:
loadImages(imageURLarray);
Maybe try this:
http://jsfiddle.net/jnt9f/
Setting onload handler before setting img src will make sure the onload event be fired even the image is cached
var $imgs = $(),i=0;
for (var img = 0; img < imageURLarray.length; img++) {
$imgs = $imgs.add('<img/>');
}
var fctn = (function fctn(i){
$imgs.eq(i).on('load',function(){
//do some stuff
//...
fctn(++i);
}).attr('src',imageURLarray[i]);
})(0);
Actually...a lot of developers are pointing here to detect when images are done loading after a jQuery event..
https://github.com/desandro/imagesloaded
If you can determine when the event triggers your images to load (for example, adding an Id or class onto the page right before your images begin to load), then you should be able to blend that in with this plug-in on github.
Good Luck!

jquery image load() and set timeout conflict in IE

As I hover a small img, I read it's larger image attribute, and create that image to display.
The problem is that I want to set Timeout before to display the image.
And while waiting for that timeout, we suppose to already have set an src to make it load early.
For some reason it never works in IE. ie, it only triggers the load even on the second time I hover the small image. I've no idea what has gone wrong with it, I had very similar animation on the other page it has been working just fine with a timeout.
Any ideas?..
$(document).ready(function(){
var nn_pp_trail=0;
$('div.nn_pp_in').hover(function(){
var limg=$(this).children('img').attr('limg');
var img=new Image();
//img.src=limg;
img.className='nn_pp_z';
img.src=limg;
var a=(function(img,par,limg){
return function(){
nn_pp_trail=window.setTimeout(showtrail,50);
$(img).one('load',(function(par){ //.attr('src',limg).
return function(){
// alert('loaded');
window.clearTimeout(nn_pp_trail);
hidetrail();
var width=this.width;
var height=this.height;
var coef=width/313;
var newHeight=height/coef;
var newHpeak=newHeight*1.7;
var nn=par.parents('.tata_psychopata').nextAll('.nn_wrap').first();
var pheight=nn.height();
var ptop=nn.position().top-2+pheight/2-1;
var pleft=nn.position().left+90+157-1;
$(this).appendTo(nn).css('left',pleft+'px').css('top',ptop+'px')
.animate({opacity:0.6},0)
.animate({opacity:1,top:'-='+newHpeak/2+'px',height:'+='+(newHpeak)+'px',left:'-=10px',width:'+=20px'},130,(function(newHeight,newHpeak){
return function(){
$(this).animate({left:'-='+(156-10)+'px',width:'+='+(313-20)+'px',height:newHeight+'px',top:'+='+(newHpeak-newHeight)/2+'px'},200,function(){});
}
})(newHeight,newHpeak)
);
}
})(par)
).each(function(){
if(this.complete || this.readyState == 4 || this.readyState == "complete" || (jQuery.browser.msie && parseInt(jQuery.browser.version) <=6))
$(this).trigger("load");}); ;
}
})(img,$(this),limg);
window.setTimeout(a,20); //if I set 0 - it loads all the time.
//if I set more than 0 timeout
//the load triggers only on the 2nd time I hover.
$(this).data('img',$(img));
},function(){
});
});
img.src=limg;
This was the problem, in IE we need not to set src as we create an image object, but first attach load event to it, and only then set attr src, and then trigger complete with an each, eg:
img.one(load, function(){}).attr('src','i.png').each(function(){ /*loaded? then it's complete*/ });
Hope someone learn on my mistakes :-)
Thank you, this helped me a lot. I had a similar situation.
As you said: First the "load"-event, then the "src" for IE.
// This was not working in IE (all other Browsers yes)
var image = new Image();
image.src = "/img/mypic.jpg";
$(image).load(function() {
//pic is loaded: now display it
});
// This was working well also in IE
var image = new Image();
imagSrc = "/img/mypic.jpg";
$(image).load(function() {
//pic is loaded: now resize and display
}).attr('src',imageSrc);

Categories

Resources