Reputation: 1167
TL;DR A css transition on opacity does not work immediately after display change, but works with setTimeout(.., 100)
. Why?
I want to flash a message for a couple of seconds and then fade it out. Seems pretty basic, right?
Well, here's a jsfiddle, but let me explain in detail.
Say I have a message block
<div id="message" class="message">
Here be dragons
</div>
Which starts hidden but opaque
.message {
opacity: 1;
display: none;
}
Once I've prepared my message I want to show it.
document.getElementById("message").style.display = "block"
Now I want the message to fade out so I added a simple transition on opacity.
.flash {
opacity: 0;
transition: opacity 2s ease-out 1s;
}
Which I apply with the following
document.getElementById("message").classList.add("flash")
The message div is shown but it stays invisible as the opacity: 0
immediately applies. Besides, the transitionend
event is not firing, which makes me think the transition does not happen at all for some reason. Weird, right?
However, everything's fine once I add the timeout
document.getElementById("message").style.display = "block"
setTimeout(() => (document.getElementById("message").classList.add("flash")), 100)
That works but seems like a totally dirty hack. Why is it like this?
You can see this behaviour on jsfiddle with two buttons aptly named 'Working' and 'Not working';
Upvotes: 3
Views: 534
Reputation: 350310
There are two things that together cause this:
When items have display: none
the opacity
is ignored (as not relevant). And so when you apply display: block
to them they render the provided opacity
with the current value without any transition effect.
Changes you apply to the style
attribute all apply together at the moment a paint (asynchronous) happens, and so the transition
definition comes too late.
First, make sure to set the transition effect definition before the actual application of it, in the message
CSS class.
I would then suggest using height
instead of display
to get the same effect. You would need to switch the border on and off also (through its width):
document.getElementById("working").addEventListener("click" , () => {
document.getElementById("message").classList.add("flash");
})
// reset
document.getElementById("message").addEventListener("transitionend" , () => {
document.getElementById("message").classList.remove("flash")
})
.message {
border: solid 0px;
background: grey;
opacity: 1;
transition: opacity 2s ease-out 1s;
overflow: hidden;
height: 0;
}
.flash {
height: 100%;
opacity: 0;
border-width: 1px
}
<div id="message" class="message">
Here be dragons
</div>
<button id="working"> Working </button>
Upvotes: 2
Reputation: 51
Why it doesn't work is because you are applying display:block
and opactiy: 0
at the same time. When you set the attribute display
in css it ignores all transition, there has to be an event in between setting display and transitions. An alternative is using visibility:hidden
and visibility:visible
instead of display but note that this only hides the element and the element is still present in its position
Upvotes: 1