0

I am trying to load my images with a fade in effect that will be triggered once the image is loaded. I noticed that onload works properly in Chrome, but in Firefox or Safari it is called some hundred miliseconds before the image is rendered, so any animation below that duration will be skipped.

I' have been reading other questions, as:

Here I created a snippet demonstrating the problem using Picsum heavy images —3000 x 3000—. When opening this question on Chrome and running this snippet, on load the image is faded; but when opening this same question on Firefox or Safari the image is rendered without fade in.

const imgElement = new Image();
const rootElement = document.getElementById("root");

rootElement.append(imgElement);
imgElement.className = "image";
imgElement.src = "https://picsum.photos/3000/3000";

imgElement.onload = function () {
  imgElement.classList.add("loaded");
};
.image {
  border: 2px solid gray;
  max-width: 100%;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.image.loaded {
  opacity: 1;
}
<div id="root"></div>

Any idea will be welcome!

Emille C.
  • 315
  • 2
  • 15
  • The onload [seems to work as it is](https://jsfiddle.net/dnzebLcs/) also in FF, only that fading in within 0.3 secs is maybe too fast. – Teemu Dec 30 '21 at 10:57
  • Can't see how 300ms is too fast. Please compare with Chrome. – Emille C. Dec 30 '21 at 11:04
  • Looks like Chrome is so slow, that an animation of 300 msecs takes a second or so ;D. – Teemu Dec 30 '21 at 11:17

2 Answers2

3

Yes, the load event does correspond to the time when the network resource has been fetched and when the browser has ensured it will be able to read it without issue.
That's when Firefox and Safari do fire the event, as per the specs.
Chrome currently does more work before firing this event, as they will wait until the full decoding is done.

So as you correctly guessed, Safari and Firefox will first render the <img> element empty, apply the transition on that empty image and then only render the final image.

To overcome this issue you can use a relatively recent addition to the HTML specs: HTMLImageElement#decode(). This method will return a Promise, resolving when the decoding part has been completely done.

const imgElement = new Image();
const rootElement = document.getElementById("root");

rootElement.append(imgElement);
imgElement.className = "image";
imgElement.src = "https://picsum.photos/3000/3000";

imgElement.decode().then(()=>{
  imgElement.classList.add("loaded");
});
.image {
  border: 2px solid gray;
  max-width: 100%;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.image.loaded {
  opacity: 1;
}
<div id="root"></div>

Ps: for the ones needing to support older browsers that didn't support decode() yet, there is a way to force the decoding synchronously.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
2

Ideally,

  1. You should wait for window.load event before appending the image tag to the DOM
  2. onload handler for imgElement should be set up before imgElement is loaded or appended to the DOM

With these changes and making animation duration from 0.3s to 3s (to make observation easy), the behaviour is consistent across browsers.

window.addEventListener("load", () => {
  const imgElement = new Image();
  const rootElement = document.getElementById("root");

  imgElement.onload = function () {
    imgElement.classList.add("loaded");
  };

  rootElement.append(imgElement);
  imgElement.className = "image";
  imgElement.src = "https://picsum.photos/3000/3000";
});
.image {
  border: 2px solid gray;
  max-width: 100%;
  opacity: 0;
  transition: opacity 3s ease;
}

.image.loaded {
  opacity: 1;
}
<div id="root"></div>
Tyler Durden
  • 860
  • 6
  • 17
  • Interesting: so in Firefox and Safari `onload`is triggered before `window` has been loaded? What kind of sorcery is this? Why? Makes no sense at all, right? – Emille C. Dec 30 '21 at 11:06
  • In this case **YES**. In the snippet you provided, I added console logs to `onload` for `window` and `imgElement` (without changing the order of statments) and I could see the log for img appearing before the log for window in Chrome as well as Firefox. – Tyler Durden Dec 30 '21 at 11:10
  • Thanks a lot. This was very helpful! Now I tried to apply this to React, but it is failing again, even using ` window.addEventListener("load"` to load the React script. But this is a React related issue, opening a new question for this. Thanks! – Emille C. Dec 30 '21 at 11:13