Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS transition on an initially hidden elemement

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.

like image 868
Tobias Ritzau Avatar asked Nov 19 '12 09:11

Tobias Ritzau


2 Answers

Regarding the question on if this is in the spec, there is an interesting thread on the [email protected] 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()!

like image 65
Tobias Ritzau Avatar answered Sep 24 '22 23:09

Tobias Ritzau


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');
like image 37
guari Avatar answered Sep 23 '22 23:09

guari