Can onload event fire right after setting image src attribute? - javascript

What console log can be after this code will be executed?
var img = new Image;
img.onload = function() { console.log('B'); };
img.src = 'image.jpg';
for (var i=0;i<100000;i++) {
console.log('A');
}
I know, that most likely it will be A...AB.
But can it be A...B...A or BA...A? For example, if image.jpg is very small file and connection is very fast.

It can be any of them. You don't know how long it takes to load the image. The image could even be cached, minimizing the loading time.

Related

Using Callback in image loading (Sequential execution)

I am playing with image loading and stuck on sequentially executing the code
function processImage(callback) {
console.log("Start");
callback('http://lorempixel.com/400/200/');
console.log("Finish");
}
function loadImage(url) {
console.log('Load Method')
var img = new Image();
img.src = url;
img.onload = function (event) {
console.log('Image Loaded')
}
}
processImage(loadImage);
For the above code, I want the output as Start -> Load Method -> Image Loaded -> Finish
Instead, I am getting Start -> Load Method -> Finish -> Image Loaded
I am having trouble using callback method I guess.
Any help appreciated.
img.onload is setting an event listener - so when the event occurs, it'll call the function. So, unless your image loads in about a millionth of second (the amount of time I guess it takes JS to go to the next line), the console.log("Finish") will happen first.
When the image is fully loaded, the function which is set to .onload is called. As the image loads after JavaScript has processed the next line, this one will happen first. If you really want to indicate the start and finish, put your finish console.log after the image loaded console.log.
img.onload = function (event) {
console.log('Image Loaded')
console.log('Finish)
}

Wait for image loading to complete in JavaScript

I'm loading images with JavaScript. Something like this:
images[0]=new Image();
images[0].onload=function(){loaded++;console.log(loaded)};
images[0].src="assets/img/image.png";
When I look at the log, I see that all the images are loaded nicely, since the value of the "loaded" variable increases with each loaded image.
However I would like to stop any further action to be executed until this amount reaches it's maximum, so right after setting up the images, I place a while cycle.
while(loaded<11){
document.getElementById("test").innerHTML="Loading "+loaded+"/11";
console.log(loaded);
}
//Some code here which should only run after everything has been loaded
//In other words: when the statement in the while cycle becomes false
However my browser simply crashes, since the while seems to be stuck in an infinite loop. When I check the log, I see that "0" was written 1000 times, and after that, the numbers from 1 to 11 (which implies that the images in fact gets loaded, but the while does not care about it, and crashes faster than it could happen).
I believe that the method I'm trying to use here is not the right approach to solve this problem.
How can I put everything on hold until every asset which is needed for the site is loaded?
Using promises and async functions, there is a nice way to wait until all the images are loaded (no callbacks, no loaded image counting):
async function loadImages(imageUrlArray) {
const promiseArray = []; // create an array for promises
const imageArray = []; // array for the images
for (let imageUrl of imageUrlArray) {
promiseArray.push(new Promise(resolve => {
const img = new Image();
// if you don't need to do anything when the image loads,
// then you can just write img.onload = resolve;
img.onload = function() {
// do stuff with the image if necessary
// resolve the promise, indicating that the image has been loaded
resolve();
};
img.src = imageUrl;
imageArray.push(img);
}));
}
await Promise.all(promiseArray); // wait for all the images to be loaded
console.log("all images loaded");
return imageArray;
}
Or you can wait for a single image to load:
async function loadImage(imageUrl) {
let img;
const imageLoadPromise = new Promise(resolve => {
img = new Image();
img.onload = resolve;
img.src = imageUrl;
});
await imageLoadPromise;
console.log("image loaded");
return img;
}
You can use it like this (using promise chaining):
loadImages(myImages).then(images => {
// the loaded images are in the images array
})
Or inside an async function:
const images = await loadImages(myImages);
Personally I hate using while()... I think the easiest way to do it, is using event listeners.
var img = new Image;
img.addEventListener("load", function () {
//Img loaded
});
img.src= e.target.result;
Javascript is single threaded. This means that if you add an event listener that listener will not run until the current execution has completed. Hence if you start a loop that relies on a event to end it it will never happen as the event will never fire because the current execution is preventing it from running. Also the events are place on the call stack asynchronously, thus if your execution is slower than the rate of event firing (placing a call on the call stack) you also risk the a page crash. This is a common mistake when using setInterval when the interval is set to less time than the code takes to execute. NEVER USE setInterval.
Just remember Javascript can not do two things at once.
The best way to deal with resource monitored loading is to use setTimeout.
var allLoaded = false;
var imgCount = 0; // this counts the loaded images
// list of images to load
const imageURLS =["a.jpg","b.jpg","c.jpg","d.jpg","e.jpg"];
// array of images
var images = [];
const onImageLoad = function(){ imgCount += 1; } // onload event
// loads an image an puts it on the image array
const loadImage = function(url){
images.push(new Image());
images[images.length-1].src = url
images[images.length-1].onload = onImageLoad;
}
const waitForLoaded = function(){
if(imgCount === images.length){
allLoaded = true; // flag that the image have loaded
}else{
// display the progress here
...
setTimeout(waitForLoaded,100); // try again in 100ms
}
}
// create the images and set the URLS
imageURLS.forEach(loadImage);
setTimeout(waitForLoaded,100); // monitor the image loading

Why confirm() function loads before in Chrome?

I would like to know why in Chrome, this line:
var quer = confirm("Deseja esta poltrona?");
loads before the previous line:
imagens[i].src = "img/poltrona_selecionada.jpg";
On Firefox, te function works perfectly in the sequence.
This is my function:
function selecionarPoltrona() {
var imagens = document.getElementsByTagName("img");
for (var i=0; i<poltronas.length; i++) {
if (poltronas[i]) {
imagens[i].src = "img/poltrona_selecionada.jpg";
var quer = confirm("Deseja esta poltrona?");
if (quer) {
break;
} else {
imagens[i].src = "img/poltrona_disponivel.jpg";
}
}
}
}
Thank You
Welcome to the Image object, and <img> element: it is not synchronous. When you tell an image what its source is, the rest of your code gets to keep running. It does not wait for the image binding to succeed, the image URL to be looked up, the data the be transfered, the bytes to be decoded, all before you see the image. This all happens while the rest of your code runs. However, confirm (and alert, and prompt, and all those terrible functions) don't: they block the thread. So this happens:
You set the image .src attribute, and the browser schedules this for off-thread handling. Your code continues
confirm gets triggered, and everything stops. Nothing will happen in your tab until the confirm is deal with.
you click through the confirm. Javascript runs again, and the browser's image source binding runs again.
after X amount of time, your image is done downloading, parsing, and finally renders.
If you want some code to run after the image finishes all its work, you do this:
...
img.onload = function() {
// the code you need to have run after the image finishes loading.
}
img.onerror = function() {
// whatever we need to do if the image cannot load.
}
img.src = "..." // trigger the image loading attempt
Or, using modern rather than 1998 JS:
...
img.addEventListener("load", function(evt) {
// the code you need to have run after the image finishes loading.
});
img.addEventListeners("error", function(evt) {
// whatever we need to do if the image cannot load.
});
img.src = "..." // trigger the image loading attempt

data:image/jpeg;base64 how to get its width and height in pixels

How do you get the width and height in pixels image which its src is in data:image/jpeg;base64 ?
Didn't succeed using setting img.src to it and then calling width().
var img = jQuery("<img src="+oFREvent.target.result+">");
img.width() // = 0
This will work :
var img = new Image();
img.onload = function() {
alert(this.width);
}
img.src = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAA.......";
FIDDLE
I made sure the base64 was valid by converting an image here, and the onload function should come before setting the source of the image.
You have to wait for the image to be loaded (or complete, if cached). This will result in an asynchronous callback operation:
// pure JS:
var img = new Image();
img.src = myDataString;
if (img.complete) { // was cached
alert('img-width: '+img.width);
}
else { // wait for decoding
img.onload = function() {
alert('img-width: '+img.width);
}
}
Note: I once had the same problem with a project using jQuery. jQuery doesn't provide an access to the image directly enough just after you have created it. It seems, it's not possible to be done, but in pure JS. But you could try a timeout-loop and wait for the img-width to have a value (and catch any loading/decoding errors).
[Edit, see also comments] Why this works (why there is no race-condition):
JS is single-threaded, meaning there's only one part of code executed at a given time. Any events will be queued until the current scope is exited and will only fire then. Thanks to late-binding, any listeners will be evaluated only then (after the current scope has been executed). So the handler will be present and listening as soon as the onload-Event is fired, regardless, if the listener was setup before or after setting the src-attribute. In contrast to this, the complete-flag is set as soon as the src-attribute is set.

Is it necessary to set onload function before setting src for an image object?

I was told it is necessary to set the onload function before setting src for an image object. I've searched in SO for this.
I found this code:
var img = new Image();
img.src = 'image.jpg';
img.onload = function () {
document.body.appendChild(img);
};
But most people believe that onload should be written before src like this:
var img = new Image();
img.onload = function () {
document.body.appendChild(img);
};
img.src = 'image.jpg';
MUST it be written in this order? Are there any cases when the above code will cause an error (like if an image is too big)?
If you anyone can show me some examples, I will be very appreciate.
It doesn't have to, but if setting the src and the image loads before your handler is attached, it won't fire.
JavaScript operates asynchronously. Setting the src will cause the web browser to load the image outside the main execution flow. If onload isn't set at the time that operation completes - which could be between setting src and onload.
As soon as you assign the src a value, the image will load. If it loads before the onload is reached, your onload will not fire.
To support ALL implementations, I strongly suggest to assign the onload handler before setting the src.
It is my experience (21+ years of JS) that you MUST set onload first - especially in IE which did not even support the image object when I started with JS.
If you get issues about the cached image not firing, add +"?"+new Date().getTime() when you set the src next time to avoid cache.
Here is the example from MDN which also uses the order I have suggested
Creating an image from scratch
Another SO link image.onload not firing twice in IE7
The browser will start downloading the image asychronously as soon as you assign a src, so there is the possibility the download could complete before you attach the onload event handler and never fire the code to add the image to the DOM.
Most browser fire the load event imediatly if the image if the image is cached.
However, Internet explorer 7 won't fire it at all. That's why it's better to set the src first.
Answers above always mentioned the same problem like:
there is the possibility the download could complete before you attach the onload event handler
or
but if setting the src and the image loads before your handler is attached, it won't fire.
or bug with IE7.
First, let's ignore IE7.
Second, I don't think problem mentioned exist, for example:
function loadImg(url) {
let img = new Image()
img.src = url
let date1 = Date.now()
console.log(img.complete)
while (Date.now() - date1 < 5000) {
}
img.onload = function() {
console.log('success')
}
console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')
Normally, you will get:
false
sync first
success
Well, if you executed on another site which will use cache more than one time. You will get the result like image below:
The answer is clear now.
Well, as long as you set the onload synchronously. You will not miss the onload event.
Why would I say synchronously. For another example,
function loadImg(url) {
let img = new Image()
img.src = url
let date1 = Date.now()
console.log(img.complete)
while (Date.now() - date1 < 5000) {
}
setTimeout(function() {
img.onload = function() {
console.log('success')
}
})
console.log('sync first')
}
loadImg('https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg')
The result on another site:
The second time with cache, load event won't be triggered. And the reason is setTimeout is asynchronously.

Categories