jQuery or Javascript check if image loaded - javascript

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!

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.

Issue with image load recursive chain on slow network/mobile

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

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!

How to do a process after completion of another one in JavaScript

I want to add an image by Javascript, then calculating the html element width as
window.onload=function(){
document.getElementById('x').addEventListener('click', function(e){
var el = document.getElementById('xx');
el.innerHTML = '<img src="img.jpg" />';
var width = el.offsetWidth;
.....
}, false);
}
but since JavaScript conduct all processes simultaneously, I will get the width of the element before loading the image. How can I make sure that the image has been loaded into the content; then calculating the element width?
UPDATE: Thanks for the answers, but I think there is a misunderstanding. img src="img.jpg" /> does not exist in the DOM document. It will be added later by Javascript. Then, when trying to catch the element by Id, it is not there probably.
You can give the img an ID and do the following :-
var heavyImage = document.getElementById("my-img");//assuming your img ID is my-img
heavyImage.onload = function(){
//your code after image is fully loaded
}
window.onload=function(){
document.getElementById('x').addEventListener('click', function(e){
var el = document.getElementById('xx');
var img = new Image();//dynamically create image
img.src = "img.jpg";//set the src
img.alt = "alt";
el.appendChild(img);//append the image to the el
img.onload = function(){
var width = el.offsetWidth;
}
}, false);
}
This is untested, but if you add the image to the DOM, set an onload/load event-handler and then assign the src of the image, the event-handling should fire (once it's loaded) and allow you to find the width.
This is imperfect, though, since if the image is loaded from the browser's cache the onload/load event may not fire at all (particularly in Chromium/Chrome, I believe, though this is from memory of a bug that may, or may not, have since been fixed).
For the chrome bug you can use the following:-
var BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';//create a blank source
var tImg = document.getElementById("my-img");//get the image
var origSrc = tImg.src;//get the original src
tImg.src = BLANK;//change the img src to blank.
tImg.src = origSrc;//Change it back to original src. This will lead the chrome to load the image again.
tImg.onload= function(){
//your code after the image load
}
You can use a library called PreloadJS or you can try something like this:
//Somewhere in your document loading:
loadImage(yourImage, callbackOnComplete);
function loadImage(image, callbackOnComplete){
var self = this;
if(!image.complete)
window.content.setTimeout(
function() { self.loadImage(image, callbackOnComplete)}
,1000);
else callbackOnComplete();
}
I did this when I worked with images base64 which delay on loading.

JavaScript SwapImage with multiple images and getElementById

I wanted to implement a swapImage on my site that would swap one image on mouseover, and another image on mouseout. Essentially, with the solution I found I could also probably swap different images for mousedown and mouseup even.
I have this happening in 8 different nested tabs, all of which are on the same page.
The javascript looks like this:
<script type="text/javascript">
function swapImageFiat(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageHarley(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageHotWheels(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageVoltron(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageBenchmade(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageCrew(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageReuters(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function swapImageMarsVolta(imgID, imgSrc) {
var theImage = document.getElementById(imgID)
theImage.src = imgSrc;
}
function preLoadImages() {
for(var i = 0; i < arguments.length; i++) {
var theImage = new Image();
theImage.src = arguments[i];
}
}
</script>
And the inline code for each image would be essentially this:
<img class="diagram" src="img/fiat.gif" id="imgToSwapFiat" alt="Diagram" data-title="Fiat Diagram" onClick="swapImageFiat('imgToSwapFiat', 'img/fiat-animated.gif')" onmouseout="swapImageFiat('imgToSwapFiat', 'fiat.gif')" height="189" width="358" />
I would like to be able to use less IDs, less of the same repetitive script, and shorter inline code.
But I also want the flexibility of being able to just change the images and their IDs, rather than fussing with JQuery or other script. This is why I'm using getElementById.
Is there a more efficient way for me to write that JavaScript, and/or Inline code?
Also
Is there a way for the image to swap back to the original after the animation stops playing, rather than on mouse-out?
Use this instead of the element ID since your function eventually need the actual element object.
The script example:
function swapImageFiat(imgEle, imgSrc) {
imgEle.src = imgSrc;
}
The image HTML example:
<img src="initial.jpg" onclick="swapImageFiat(this, 'clicked.jpg')" onmouseover="swapImageFiat(this, 'hovered.jpg')" onmouseout="swapImageFiat(this, 'left.jpg')" />
Yeah, sure there is. As Jan mentions - all of your functions are identical. There's no need to duplicate them all. Also, you can remove the inline event-handler code from your html, simply attaching the mouseover/mouseout or mousedown/mouseup.
Simply add a new attribute to each image that you wish to have the effect for, then runs some js on page-load to attach the handler.
Here, I've just used (and checked for the existance of) custom attributes. If the image has them, I attach handlers. If not, it's no different to any other normal image. This should be nice an re-usable and little effort to use.
As for resetting the image once the animated gif has played, I'd probably look at Jan's first suggestion. A slight modification on the idea would be to make the last frame transparent, then simply position the animated image over (z-index higher) the static one.
When it's done, you'll see the original image again. You'll have to decide on a system for refreshing these images, should they need to be re-played. I'm not sure, perhaps setting their src to '', before setting it back to the animated.gif would cause the animation to play from the start again - you'd have to check if to decided to attempt such an approach.
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function mInit()
{
var imgList = document.getElementsByTagName('img');
var i, n = imgList.length;
for (i=0; i<n; i++)
{
if (imgList[i].hasAttribute('img2'))
imgList[i].addEventListener('click', imgClick, false);
if (imgList[i].hasAttribute('img3'))
imgList[i].addEventListener('mouseover', imgHover, false);
if (imgList[i].hasAttribute('img4'))
imgList[i].addEventListener('mouseout', imgOut, false);
}
}
// because these function are attached with addEventListener, the 'this'
// keyword refers to the image element itself. No need for IDs..
function imgClick()
{
this.src = this.getAttribute('img2');
}
function imgHover()
{
this.src = this.getAttribute('img3');
}
function imgOut()
{
this.src = this.getAttribute('img4');
}
window.addEventListener('load', mInit, false);
</script>
<style>
</style>
</head>
<body>
<img src='img/gladiators.png'> <!-- this image won't be touched, as it doesn't have img1, img2 or img3 attributes -->
<img src='img/opera.svg' img2='img/chromeEyes.svg' img3='img/rss16.png' img4='img/girl2.png'/>
</body>
</html>
Jay has a good suggestion for your functions, which all do the same thing. If you remove the names from your functions you'll see they are otherwise identical. And the function name makes no difference at all, in this case it's just the arguments you pass to the function that matters.
Is there a way for the image to swap back to the original after the
animation stops playing, rather than on mouse-out?
You could make the gifs non-looping and make the last frame of the animation the same as the original image. AFAIK there's no way to detect which frame an animated gif is on through Javascript.
If you really want it to return to the original image, you could animate the image completely through Javascript: Write a function that preloads all the images (or a spritemap) and then load them frame by frame.

Categories

Resources