116

I have a CSS Animation for a div that slides in after a set amount of time. What I would like is for a few divs to fill the space of the animated div that slides in, which it will then push those elements down the page.

When I attempt this at first div that slides in still takes up space even when it is not visible. If I change the div to display:none the div doesn't slide in at all.

How do I have a div not take up space until it is timed to come in (using CSS for the timing.)

I am using Animate.css for the animations.

Here is what the code looks like:

<div id="main-div" class="animated fadeInDownBig"><!-- Content --></div>

<div id="div1"><!-- Content --></div>
<div id="div2"><!-- Content --></div>
<div id="div3"><!-- Content --></div>

As the code shows I would like the main div to be hidden and the other divs show at first. Then I have the following delay set:

#main-div{
   -moz-animation-delay: 3.5s;
   -webkit-animation-delay: 3.5s;
   -o-animation-delay: 3.5s;
    animation-delay: 3.5s;
}

It is at that point that I would like the main div to push the other divs down as it comes in.

How do I do this?

Note: I have considered using jQuery to do this, however I prefer using strictly CSS as it is smoother and the timing is a bit better controlled.

EDIT

I have attempted what Duopixel suggested but either I mis-understood and am not doing this correctly or it doesn't work. Here is the code:

HTML

<div id="main-div" class="animated fadeInDownBig"><!-- Content --></div>

CSS

#main-image{
    height: 0;
    overflow: hidden;
   -moz-animation-delay: 3.5s;
   -webkit-animation-delay: 3.5s;
   -o-animation-delay: 3.5s;
    animation-delay: 3.5s;
}
#main-image.fadeInDownBig{
    height: 375px;
}
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
L84
  • 45,514
  • 58
  • 177
  • 257
  • Setting the `display: none` will terminate any running animation applied to the element and its descendants. If an element has a `display` of `none`, updating `display` to a value other than `none` will start all animations applied to the element by the `animation-name` property, as well as all animations applied to descendants with `display` other than `none`. https://drafts.csswg.org/css-animations/#animations – rflw Mar 02 '22 at 06:56

9 Answers9

98

CSS (or jQuery, for that matter) can't animate between display: none; and display: block;. Worse yet: it can't animate between height: 0 and height: auto. So you need to hard code the height (if you can't hard code the values then you need to use javascript, but this is an entirely different question);

#main-image{
    height: 0;
    overflow: hidden;
    background: red;
   -prefix-animation: slide 1s ease 3.5s forwards;
}

@-prefix-keyframes slide {
  from {height: 0;}
  to {height: 300px;}
}

You mention that you're using Animate.css, which I'm not familiar with, so this is a vanilla CSS.

You can see a demo here: http://jsfiddle.net/duopixel/qD5XX/

methodofaction
  • 70,885
  • 21
  • 151
  • 164
  • Thanks for the help, it is not working or I do not have it correct. See my edit to my question. – L84 Oct 24 '12 at 04:34
  • Sorry, I was thinking about CSS transitions when I wrote that (it is much more simple by the way), I've updated the code for the animation syntax – methodofaction Oct 24 '12 at 05:07
  • Beautiful! Works like a charm. Thanks! You should check out [Animate.css](http://daneden.me/animate/) a very very useful tool for animations. – L84 Oct 24 '12 at 06:28
  • I can not hard code the height, what would it be the best way to do it using javascript? – Raphael Isidro May 14 '13 at 22:06
  • @RaphaelIsidro depending on the problem sometimes I save the height and *then* hide the element, or I put it in a placeholder with `opacity: 0; height auto` and then destroy the placeholder. Here is an example http://jsfiddle.net/duopixel/SUZ8r/ – methodofaction May 15 '13 at 01:59
  • 8
    `if you can't hard code the values then you need to use javascript`. That's wrong. You can animate the max-height property, with a value larger than you expect the box to be and it works just fine. – ssc-hrep3 Oct 06 '16 at 14:28
48

There are a few answers already, but here is my solution:

I use opacity: 0 and visibility: hidden. To make sure that visibility is set before the animation, we have to set the right delays.

I use http://lesshat.com to simplify the demo, for use without this just add the browser prefixes.

(e.g. -webkit-transition-duration: 0, 200ms;)

.fadeInOut {
    .transition-duration(0, 200ms);
    .transition-property(visibility, opacity);
    .transition-delay(0);

    &.hidden {
        visibility: hidden;
        .opacity(0);
        .transition-duration(200ms, 0);
        .transition-property(opacity, visibility);
        .transition-delay(0, 200ms);
    }
}

So as soon as you add the class hidden to your element, it will fade out.

Fabian Vogelsteller
  • 1,025
  • 9
  • 13
  • 9
    WOW! I used this like: `.default { opacity: 0; visibility: hidden; top: -10px; transition-duration: 200ms, 200ms, 0; transition-property: opacity, top, visibility; transition-delay: 0, 0, 200ms; }` -- `.hidden { opacity: 1; visibility: visible; top: 0px; transition-duration: 200ms, 200ms, 0; transition-property: opacity, top, visibility; transition-delay: 200ms, 200ms, 0; }` to create a "drop" effect, worked really well! love it @frozeman , thanks!! – simey.me Aug 18 '13 at 18:18
  • 8
    okay you have hidden the element, but as the accepted answer says, you have not animated from display: none; and display: block; so your element is still taking up space on the page – svnm May 05 '15 at 04:22
  • 2
    animating `display: none` is not possible. – Fabian Vogelsteller Aug 09 '15 at 18:34
  • When you hide an element with opacity or visibility, it is still there, so if you have for example links inside, they will still be clickable. To avoid this, you can use `pointer-events: none;`. This disables any interaction with the mouse cursor. – pol_databender Oct 29 '15 at 13:35
  • 7
    `visibility: hidden` take care of that, you don't need `pointer-events: none;` – Edu Ruiz Dec 09 '15 at 20:23
28

I had the same problem, because as soon as display: x; is in animation, it won't animate.

I ended up in creating custom keyframes, first changing the display value then the other values. May give a better solution.

Or, instead of using display: none; use position: absolute; visibility: hidden; It should work.

Jawa
  • 2,336
  • 6
  • 34
  • 39
tobspr
  • 8,200
  • 5
  • 33
  • 46
20

You can manage to have a pure CSS implementation with max-height

#main-image{
    max-height: 0;
    overflow: hidden;
    background: red;
   -prefix-animation: slide 1s ease 3.5s forwards;
}

@keyframes slide {
  from {max-height: 0;}
  to {max-height: 500px;}
}

You might have to also set padding, margin and border to 0, or simply padding-top, padding-bottom, margin-top and margin-bottom.

I updated the demo of Duopixel here : http://jsfiddle.net/qD5XX/231/

Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
oliverpool
  • 1,624
  • 13
  • 30
5

The following will get you to animate an element when

  1. Giving it a Display - None
  2. Giving it a Display - Block

CSS

.MyClass {
       opacity: 0;
       display:none;
       transition: opacity 0.5s linear;
       -webkit-transition: opacity 0.5s linear;
       -moz-transition: opacity 0.5s linear;
       -o-transition: opacity 0.5s linear;
       -ms-transition: opacity 0.5s linear;
 }

JavaScript

function GetThisHidden(){
    $(".MyClass").css("opacity", "0").on('transitionend webkitTransitionEnd oTransitionEnd otransitionend', HideTheElementAfterAnimation);
}

function GetThisDisplayed(){
    $(".MyClass").css("display", "block").css("opacity", "1").unbind("transitionend webkitTransitionEnd oTransitionEnd otransitionend");
}

function HideTheElementAfterAnimation(){
    $(".MyClass").css("display", "none");
}
Mahesh
  • 3,727
  • 1
  • 39
  • 49
  • In GetThisDisplayed I needed to separate `.css("display", "block")` and `.css("opacity", "1")`. Also I needed to place ``.css("opacity", "1")`` in a setTimeout (with zero delay). See https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop if interested about the setTimeout – Julien Jan 27 '22 at 06:20
2

When animating height (from 0 to auto), using transform: scaleY(0); is another useful approach to hide the element, instead of display: none;:

.section {
  overflow: hidden;
  transition: transform 0.3s ease-out;
  height: auto;
  transform: scaleY(1);
  transform-origin: top;

  &.hidden {
    transform: scaleY(0);
  }
}
Jesper N
  • 2,115
  • 4
  • 23
  • 32
1

How do I have a div not take up space until it is timed to come in (using CSS for the timing.)

Here is my solution to the same problem.

Moreover I have an onclick on the last frame loading another slideshow, and it must not be clickable until the last frame is visible.

Basically my solution is to keep the div 1 pixel high using a scale(0.001), zooming it when I need it. If you don't like the zoom effect you can restore the opacity to 1 after zooming the slide.

#Slide_TheEnd {

    -webkit-animation-delay: 240s;
    animation-delay: 240s;

    -moz-animation-timing-function: linear;
    -webkit-animation-timing-function: linear;
    animation-timing-function: linear;

    -moz-animation-duration: 20s;
    -webkit-animation-duration: 20s;
    animation-duration: 20s;

    -moz-animation-name: Slide_TheEnd;
    -webkit-animation-name: Slide_TheEnd;
    animation-name: Slide_TheEnd;

    -moz-animation-iteration-count: 1;
    -webkit-animation-iteration-count: 1;
    animation-iteration-count: 1;

    -moz-animation-direction: normal;
    -webkit-animation-direction: normal;
    animation-direction: normal;

    -moz-animation-fill-mode: forwards;
    -webkit-animation-fill-mode: forwards;
    animation-fill-mode: forwards;

    transform: scale(0.001);
    background: #cf0;
    text-align: center;
    font-size: 10vh;
    opacity: 0;
}

@-moz-keyframes Slide_TheEnd {
    0% { opacity: 0;  transform: scale(0.001); }
    10% { opacity: 1; transform: scale(1); }
    95% { opacity: 1; transform: scale(1); }
    100% { opacity: 0;  transform: scale(0.001); }
}

Other keyframes are removed for the sake of bytes. Please disregard the odd coding, it is made by a php script picking values from an array and str_replacing a template: I'm too lazy to retype everything for every proprietary prefix on a 100+ divs slideshow.

Marco Bernardini
  • 695
  • 6
  • 17
1

In 2023, Using CSS and Inline JS (Vanilla, no jQuery, etc...)

CSS transition can be used instead of writing an animation keyframe sequence using this:

General Technique:

  • Use 2 classes on the same parent <div>
  • Start the transition directly after the display: block; mutation:
    • setTimeout() with 1ms delay or
    • requestAnimationFrame()
  • Set display: none; after transition:
    • setTimeout() with transition-duration via getComputedStyle()

HTML Outline with Inline JS

<style>
  .root { 
    opacity: 0; /* etc... */
    transition: opacity 0.3s;
  }
  .root.d-none { display: none; }
  .root.show { opacity: 1; }
</style>
...
<button id="show" onclick="
  const ar = getElementById('ani-root'); 
  ar.classList.remove('d-none');
  setTimeout(() => ar.classList.add('show'),1);"
  > Show </button>
...
<button id="hide" onclick="
  const ar = getElementById('ani-root');
  const timeout = 1000 * parseFloat(
    getComputedStyle(ar).getPropertyValue('transition-duration')
  );
  ar.classList.remove('show');
  setTimeout(() => ar.classList.add('d-none'),timeout);"
  > Hide </button>
...
<div id="ani-root" class="root d-none">
  ...
</div>
...

Explanation

By using setTimeout() with 1ms delay or requestAnimationFrame() to add .show, the block is 1st added to the DOM and able to be painted with 0 opacity (etc...) for the start of the transition, allowing the transition to occur in step 2, triggered by .show.

requestAnimationFrame() worked equally in Chrome and Edge for the setTimeout(), but not in Firefox; setTimeout() with a 1ms delay which feels like a hack, works in all 3 and mirrors display: none;.

I've only tested using current browsers (2023) but expect that the setTimeout() hack should work for many older browsers, given you change to the older JS syntax instead of const and arrow (=>) functions.

TAOcode
  • 136
  • 6
0

I have the same problem and solved putting everything bellow a div with position:relative and added position: absolute, top:0, left:0 to every child div. In your case it will be like:

    <div id="upper" style="position: relative">
        <div id="main-div" class="animated fadeInDownBig" style="position: absolute; left:0; top:0;"><!-- Content --></div>

        <div id="div1" style="position: absolute; left:0; top:0;""><!-- Content --></div>
        <div id="div2" style="position: absolute; left:0; top:0;""><!-- Content --></div>
        <div id="div3" style="position: absolute; left:0; top:0;""><!-- Content --></div>
    </div>