Tobiq
Tobiq

Reputation: 2657

CSS fade not working in Chrome

I wrote this function in javascript:

function unhide(el, dur = 300, display = "block") {
    el.style.display = display;
    el.style.transitionDuration = dur + "ms";
    el.style.opacity = 1;
}

For some reason, this isn't working in Chrome.

I remember having a problem like this, due to the browser not applying Javascript-written CSS atomically.

So, I tried putting a break point after the display was changed:

function unhide(el, dur = 300, display = "block") {
    el.style.display = display;
●   el.style.transitionDuration = dur + "ms";
    el.style.opacity = 1;
}

This fixed the problem.

This same issue occurs in FireFox, but not Microsoft edge.

How can I fix this?

Is there a way to force the browser to apply the display, and then the opacity?


I just thought about using a setTimeout (for setting the opacity, because, again, the display is not atomically applied), however, that seems like a messy/inefficient hack.

Upvotes: 4

Views: 1065

Answers (2)

Tobiq
Tobiq

Reputation: 2657

As theorised, its been noted that the browser groups JS-applied CSS alterations together.


Is there a way to force the browser to apply the display, and then the opacity?`

Yes. Add the line:

el.offsetWidth;

between the two CSS alterations.

This forces the browser to re-render the pages CSS.

var el = document.getElementById("hidden");
unhide(el);

function unhide(el, dur = 300, display = "block") {
    el.style.display = display;
    el.style.transitionDuration = dur + "ms";
    el.offsetWidth;
    el.style.opacity = 1;
}
<div id="hidden" style="display: none; opacity: 0;">Unhidden</div>

Upvotes: 2

Freeman Lambda
Freeman Lambda

Reputation: 3655

Minimal implementation with a requestAnimationFrame

function unhide(el, dur = 1000, display = "block") {
    console.log("opacity when display none: ", getComputedStyle(mrHyde, null).opacity);
    el.style.display = display;
    console.log("opacity when display block: ", getComputedStyle(mrHyde, null).opacity)
    el.style.transitionDuration = dur + "ms";
    requestAnimationFrame(() => el.style.opacity = 1);
}

const mrHyde = document.getElementById("unhideMe");
unhide(mrHyde);
#unhideMe {
  display: none;
  opacity: 0;
}
<div id="unhideMe">Mr. Hyde</div>

EDIT I replaced setTimeout with requestAnimationFrame. The end result is the same but I feel requestAnimationFrame is more suited for this purpose.

EDIT 2 This behavior is actually already reported as a bug. The very last comment on the thread raises a hypothesis that I also was speculating on. Perhaps the browser "discards" the numeric value of opacity when the element is in display: none. That would imply that setting the opacity: 1 does not trigger a css transition, because transitions only work on well defined style property values. On the very next event loop, the browser will have a defined opacity value for the element, because it is by now in display: block.

I took a small step into proving this hypothesis by logging the computed style value of the element when it is in display: none and right after it switches to display: block. Both values are actually a well defined numeric 0, so this goes against the hypothesis.

PS: http://caniuse.com/#feat=requestanimationframe Coverage for requestAnimationFrame seems to be fairly acceptable as of 2017, including IE > 9 versions.

Upvotes: 2

Related Questions