6

I would like to make a css transition on an element which has display: none set. Consider the following code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>CSS Transition From Hidden</title>

        <style type="text/css">
            div {
                -webkit-transition-property: all;
                -webkit-transition-duration: 2s;
            }       

            div.hidden {
                display: none;
                opacity: 0;
            }    

            div.from {
                opacity: 0;
            }
        </style>     

        <script type="text/javascript">
            function loaded() {
                var e = document.getElementById("foo");
                e.className = "from";
                window.webkitRequestAnimationFrame(function(t) {
                    e.className = null;
                });
            }
        </script>
    </head>
    <body onload="loaded()">
        <div id="foo" class="hidden">
            My test div
        </div>
    </body>
</html>

I would like to go from class="div.hidden" to class="", i.e. from display: none; opacity: 0; to display: block; opacity: 1; However, in chrome (at least) an object that has display: none does not animate. The element goes to the end state immediately.

My work around for this is to first set the element to display: block; opacity: 0; and then do the transition in the next frame (using requestAnimationFrame(). It is kind of awkward, and I can't find any explanation for this behavior in the spec. I know that I could use the visibility-attribute, but I don't want to use it because I don't want to layout the hidden element.

So, the questions are: Is this the correct behavior or a bug? If it is the correct behavior, is there a better way to write the code? Note that I'm not asking if there are any libraries that can do it, I want to know if there is a better way to do it directly on the DOM.

Tobias Ritzau
  • 3,327
  • 2
  • 18
  • 29

6 Answers6

7

Regarding the question on if this is in the spec, there is an interesting thread on the www-style@w3.org list here. I haven't read it all but it seems as they don't start animations from none and that the transition spec needs to clarify that as well.

Update: I have asked the mail list and I got this link to the minutes of a work group meeting where it was decided that there should be no transition if the start state is display: none.

To make sure that the transition is performed you must make sure that the value of the animated property is calculated before it is set to its new target. Values are normally not calculated when display is set to none. Here is a working example:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">

    <title>Fade</title>

    <style>
      .hidden { display: none }
      .start { opacity: 0 }
      .transition { opacity: 1; -webkit-transition: opacity 1s }
    </style>
  </head>
  <body>
    <div id='div' class="hidden">Test</div>

    <script>
      var elem = document.getElementById('div');

      function ontransitionend(event) {
        elem.className = null;
        elem.removeEventListener('transitionend', ontransitionend);
      }
      elem.addEventListener('transitionend', ontransitionend);

      elem.className = 'start';
      window.getComputedStyle(elem).opacity;
      elem.className = 'transition';
    </script>
  </body>
</html>

Note that you have to access the opacity property. It is not enough to call getComputedStyle()!

Tobias Ritzau
  • 3,327
  • 2
  • 18
  • 29
2

You should not necessarily relay on transitionend callbacks.
To show with a transition on the opacity property (or others) an hidden element that initially has display:none, simply use inline style to set display:block then remove your CSS class .hidden to animate the element as you want:

CSS:

#foo {
    display: none;
    opacity: 1;
    /*optional, other ending properties...*/
    transition: opacity 1s;
}
.hidden {
    opacity: 0;
    /*optional, other starting properties...*/
}

HTML:

<div id="foo" class="hidden">
    My test div
</div>

finally, the javascript part to show the element with a transition:

var elem = document.getElementById('foo');
elem.style.display = 'block';
elem.classList.remove('hidden');
guari
  • 3,715
  • 3
  • 28
  • 25
1

There are several css properties that cannot be incremented in small steps (what is the value of display 25% of the way between none and block?), particularly those that have non numeric values. The workaround you are describing is pretty much the standard way of dealing with this. jQuery, for example, uses something similar in its fadeIn and fadeOut methods.

If you want to animate the transition from taking up no space to standard layout, you can transition the height and width properties.

Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • I'm not saying that there should be an animation on the `display` property. I'm setting `display` to block and (in this case) the `opacity` is changed. This is the way transitions work. The properties you list in `transition-property` are animated, the rest are set immediately. Why is it different for `display`? – Tobias Ritzau Nov 19 '12 at 10:04
  • No, it is not that I don't want it to take no space. It should take very little time. And the hidden elements can be quite many and complex (slide show). – Tobias Ritzau Nov 19 '12 at 10:05
  • @TobiasRitzau Do you mean opacity is set immediately? – Asad Saeeduddin Nov 19 '12 at 10:08
  • Yes, instead of a 2s fade in, opacity is set to 1 directly. – Tobias Ritzau Nov 19 '12 at 10:11
  • @TobiasRitzau If you remove the `display:none` and use exactly the same code for only opacity, does it work correctly? – Asad Saeeduddin Nov 19 '12 at 10:12
1

the display: none mean that you could reach the element but first you should make sure that element has been rendered . you can use ready function in jquery or implemente it in javascript to make sure that the element is there . you could do the above by using : javascript(use timer to increase the opacity) or jquery as below

<html>
    <head>
        <meta charset="utf-8">
        <title>CSS Transition From Hidden</title>

    <script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
        <style type="text/css">

            div.hidden {
                display: none;
                opacity: 0;
            }    

                    </style>     

        <script type="text/javascript">
$.ready = function () {

                $("#foo").fadeIn(2000);

            }
        </script>
    </head>
    <body>
        <div id="foo" class="hidden">
            My test div
        </div>
    </body>
</html>
Athamneh
  • 298
  • 1
  • 9
1

I have the same issue and i dont think it should be considered an acceptable behavior by the browsers. The problem is not that anyone is trying to animate the actual display property. Its that you cannot have an element with display: none, update the property to inline or whatever and then animate the opacity for example, not even if you put a timeout for 10 seconds after the display has been updated.

My workaround right now is putting width and height to 0, overflow to hidden, updating these values and THEN update the animation attributes. At least it behaves more similar to display none than visibility hidden.

Anjuna5
  • 39
  • 1
  • 4
0

display:none jacks up the animation. It's a shame and I think should be addressed by browsers. I've gotten around the problem before by setting a z-index to -1, which hides the element, and if absolutely positioned also removes it from the static layout. You can actually transition z-index (though it doesn't really look like animate), therefore doesn't mess up the opacity animation.

This worked for me on my application, although I eventually had to use JavaScript to go ahead and set the display property from none to block because I eventually needed to use the fading element in a overflow:auto element, which when it wasn't hidden, created scroll bars at undesired times.

Ben
  • 2,917
  • 10
  • 28
  • 47
  • Thanks for your reply. Sure, there are workarounds (like the one I posted), what I like to know is if this is according to spec (point me to where), and I'm looking for nicer ways to implement it. I guess that negative z-index works since the item is placed behind the body, or? Anyway, I don't see how that helps. The code I have posted works, but it is weird if that is the way it should be implemented :S I simply don't understand why you can not animate from "display: none" – Tobias Ritzau Jun 25 '13 at 13:21