4

Like the title said. I have this code: https://jsfiddle.net/fwo9ym1o/

//javascript
    var container = document.querySelector("#container");

    container.style.display = "block";

    //this is not working
    //container.style.opacity = 1;


    //this is working
    setTimeout(function() {
       container.style.opacity = 1;
    }, 0);

/css
    .container {
        height: 200px;
        width: 200px;
        background-color: salmon;
        display: none;
        border-radius: 5px;
        opacity: 0;
        transition: opacity 2s ease-in-out;
    }

//html
    <div id="container" class="container"></div>

So, I've changed the container.style.display = "block"; then applied container.style.opacity = 1; and the transition is not happening.

It works if I run everything in a new thread.

NOTE: I can't use visibility. It has to be display:none

Doua Beri
  • 10,612
  • 18
  • 89
  • 138

4 Answers4

6

It's because of the way styles are figured out. Style changes are expensive so they are effectively saved up until they are needed (a recalc check like .offsetHeight is called or the next frame needs to be drawn).

The following code should work. It includes an explanation of what (I think) is going on:

container.style.display = "block";
// container is actually still invisible
// current style hasn't been calculated

container.offsetHeight;
// this forces a style recalc
// so the current style for the container is figured out.
// without this recalc, this element effectively has no style,
// and so when you transition from its current style (null) to a different one (opacity: 1), it just snaps to the new value.

container.style.opacity = 1;
// this triggers the transition.
// because the style was recalced before the transition was triggered,
// it will transition _from_ those values.

jsFiddle

Whothehellisthat
  • 2,072
  • 1
  • 14
  • 14
  • thanks, Already found something similar using getComputedStyle(container).opacity; – Doua Beri Sep 17 '16 at 16:02
  • Oh. That just gives you the latest style. I guess it would do the same thing, but I would guess it'd be more expensive. Up to you. – Whothehellisthat Sep 17 '16 at 16:03
  • @Whothehellisthat This answer got me out of trouble too. In my case, I triggered a CSS transition by setting an attribute. The sequence became, briefly, `container.style.display='block'; container.offsetHeight; container.setAttribute(someting,'')`. Thanks. – Manngo Dec 29 '16 at 22:17
  • Glad I could help! – Whothehellisthat Jan 01 '17 at 10:18
  • What would be the jQuery equivalent for this? I've tried `$('#my_element').offsetHeight;` but it doesn't seem to fix the issue – clayRay May 11 '21 at 05:17
3

May I suggest you use animation instead, it is much more appropriate than force a redraw.

var container = document.querySelector("#container");
container.classList.add('animateme');
.container {
  display: none;
  height: 150px;
  width: 150px;
  background-color: red;
}

.animateme {
  display: block;
  animation: animate 2s linear;
}

@keyframes animate {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
<div id="container" class="container"></div>
Asons
  • 84,923
  • 12
  • 110
  • 165
1

Despite the marked 'right' answer I vote for the answer from LGSon above due to it being not a workaround but using natural CSS capability.

Plus it gives a cleaner code in JS due to the ease of toggling a class (container.classList.toggle('animateme')) and separation of concerns (JS does not manipulate CSS properties directly).

However I experienced the animation animate getting reset at the end to its zero keyframe i.e. to opacity: 0;

To make it stay I added animation-fill-mode: forwards; to .animateme selector like this (taken from this stackoverflow answer)

.animateme {
  display: block;
  animation: animate 2s linear;
  animation-fill-mode: forwards;
}
Community
  • 1
  • 1
Valentine Shi
  • 6,604
  • 4
  • 46
  • 46
0

Try this:

var container = document.querySelector("#container");
container.style.opacity = 1;

<div id="container" class="container"></div>

.container {
height: 200px;
width: 200px;
background-color: salmon;
display: block;
border-radius: 5px;
opacity: 0;
transition: opacity 2s ease-in-out;
}

JSFiddle

bbonch
  • 534
  • 1
  • 5
  • 12