Reputation: 2657
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
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
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