I want to know if looking at an image's height property is a reliable way to see if it has already loaded.
Let's say I've got a reference to an image, like this:
var img = document.getElementById('myImg');
If I want to run some code that relies on the image being loaded, I can put it in a callback, and run img.onload = myCallback;.
But if the image has already loaded before this code runs, then the callback won't execute.
This is one possible solution:
if (img.height && img.height > 0) {
myCallback();
}
else {
img.onload = myCallback;
}
In other words: run myCallback now if the image is already loaded, otherwise wait until it's loaded.
But I have a feeling this code might be fragile... Will it work in all cases? Is there a safer or more elegant way of doing this?
You're looking for the complete property. However, I don't think this works reliably in all Firefox versions, so it's good to add your height check as backup:
if (img.complete) {
myCallback();
} else if (img.height && img.height > 0) {
myCallback();
} else {
img.onload = myCallback;
}
Related
I want to lazy load a css file, and then have javascript modify a page after said css loaded. To that end, I'm using this approach, as illustrated here:
lazyLoadCSS = function (pathToStyleSheet, options, callback) {
var stylesheet = document.createElement('link');
stylesheet.href = pathToStyleSheet;
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
// temporarily set media to something inapplicable to ensure
// it'll fetch without blocking render
stylesheet.media = 'only x';
// set the media back when the stylesheet loads
stylesheet.onload = function() {
stylesheet.media = 'all';
// here be callbacks...
callback();
};
document.getElementsByTagName('head')[0].appendChild(stylesheet);
};
So, the idea is, javascript fetches the CSS, but smuggles it in as 'only x' so the browser doesn't think it needs to wait for the file before rendering the rest of the page, which is the whole point. This approach seems to be pretty standard and the above code does work...mostly.
The only problem is the callback. It happens after the CSS is loaded, but before the styles have been applied to the document.
If I put a timer on the callback, like so:
window.setTimeout(function(){
callback();
}, 2000);
then everything works exactly the way it should (only slow).
Question is, how can I wait not only for the CSS to load, but also to be applied before I run the callback()?
As per some comments above, this is what I've come up with and it work great (though it's not thoroughly tested):
function waitForStylesToApply(cycleNumber, canary, callback){
if(cycleNumber < 100){
window.setTimeout(function(){
var computedValue = window
.getComputedStyle(window.document.body, null)
.getPropertyValue('max-width');
if(computedValue !== canary){
if(typeof callback === 'function'){
console.log('done in ' + cycleNumber + ' cycles');
callback();
}
}
else {
waitForStylesToApply(cycleNumber++, canary, callback);
}
}, 10);
}
}
Checks on the computed style for <body> over and over until it changes. Then it does the callback. So far, the code has never had to cycle through more than once, so the wait is infinitesimal.
Even a delay of 10 is too much. Code will work with a delay of 0 and still only cycle once.
EDIT: use this function in place of simply using callback() in the example above.
I've read numerous posts and articles and tried both the JS-only onLoad method and using JQuery's .load(function() { way of going about things, but I can't find a good way to load an image quietly in the background and then do something once I know its actual width.
At this point I've got it all the way down as simple as I can get it, and it still returns "0":
bgImg = new Image();
bgImg.src = "./img/car-and-truck.gif";
bgImg.onload = imageLoaded();
function imageLoaded() {
alert(bgImg.width);
}
Use this instead:
bgImg.onload = imageLoaded;
You are calling imageLoaded immediately which you do not want to do.
The alert works, but the return is 0. How do I get it to return the correct value?
var img;
img = new Image();
img.onload = function() {
alert(img.width);
return(img.width);
};
img.src = "https://maps.gstatic.com/mapfiles/dir/bike.png";
alert(img.width);
You can't do that. onload runs once the image is loaded, which means after setting the src, onload will run later.
You need to put all code dealing with the image's width inside the onload.
You can return the value, but you have no way of capturing it.
The function you pass to onload is executed, when your image has loaded. But there is no way to retrieve the return value there. You can just call another function inside, to process the value.
I get 10 from this code in Chrome:
var img = new Image();
img.onload = function() { console.log(img.width); }
img.src = "https://maps.gstatic.com/mapfiles/dir/bike.png";
You do have to wait for the image to load before querying width (in onload).
In Firefox 8.0 on ubuntu 11.10, the onload function, draw, is called although img.complete is false. I managed to solve it somewhat with the setTimeout hack, but it's not pretty.
I tried setting the img.onload before setting img.src. Although I always get img.complete as true this way, img.width is zero, and img.src si also empty, so it doesn't work.
Any ideas how to implement this properly?
var draw=function(img,ctx,x,y)
{ if(!img.complete)
{ setTimeout(function(){draw(img,ctx,x,y);},50);
}
else
{
ctx.drawImage(img,x,y);
}
}
for(i=0;i<9;i++)
{ img=new Image();
img.src="/media/"+url[i];
img.onload=(draw)(img,ctx,tile.x*offset[i].x,tile.y*offset[i].y);
}
The complete property is buggy in Firefox. Once it's true, it's always true (even if you change the image).
I got around it by testing a new Image object.
new Image for every .complete doesn't work, either.
Try checking img.width for greater than zero instead.
function loader(img) {
var imgH = img.height;
var imgW = img.width;
console.log(imgH, imgW);
};
img = new Image();
img.src ='../images/pic1.jpeg';
img.onLoad = loader(img);
So, It is exepeted, that I'll get image's size, but I got "0 0" in console. And size of image is 500X700. What's wrong with this code?
This makes no sense:
img.onLoad = loader(img);
you want to pass the actual function to the event:
img.onload = loader;
and use this instead of img within the function.
Also you need to assign the event before changing the image's src property.
Also note that there are numerous problems with the load event on images. From the jQuery manual on load():
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Try this:
function loader(){
var imgH = this.height;
var imgW = this.width;
console.log(imgH, imgW);
}
var img = new Image();
img.onload = loader;
img.src ='../images/pic1.jpeg';
I used this way:
img.onload = function(){//here wrote everything from loader};
And it was the only working solution I have found.
I found the best way is to let the computer do the scaling first.
and declaring the onload = "some_function()" in the body.
<body onload="some_function()">
and then getting the sizing afterward in the script.
some_function(){
var image1 = document.getElementsByClassName('main')[0];
var computedStyle_image1 = window.getComputedStyle(image1);
var image_width = computedStyle_image1.getPropertyValue('width');
}
I noticed with google chrome you need to make sure you call the onload="function" within the body div otherwise the image values arn't set and it pulls in 0px for width and height.