'How to prevent a CSS keyframe animation from running on page load?

I have a div in which I animate the content:

#container {
  position: relative;
  width: 100px;
  height: 100px;
  border-style: inset;
}
#content {
  visibility: hidden;
  -webkit-animation: animDown 1s ease;
  position: absolute;
  top: 100px;
  width: 100%;
  height: 100%;
  background-color: lightgreen;
}
#container:hover #content {
  -webkit-animation: animUp 1s ease;
  animation-fill-mode: forwards;
  -webkit-animation-fill-mode: forwards;
}
@-webkit-keyframes animUp {
  0% {
    -webkit-transform: translateY(0);
    visibility: hidden;
    opacity: 0;
  }
  100% {
    -webkit-transform: translateY(-100%);
    visibility: visible;
    opacity: 1;
  }
}
@-webkit-keyframes animDown {
  0% {
    -webkit-transform: translateY(-100%);
    visibility: visible;
    opacity: 1;
  }
  100% {
    -webkit-transform: translateY(0);
    visibility: hidden;
    opacity: 0;
  }
}
<div id="container">
  <div id="content"></div>
</div>

On hover, the content slides into the container div. When I refresh the page and the page loads, the #content's animDown animation will run, and I'd prefer it to run only after a hover event.

Is there a way to do this pure CSS, or I have to figure something out in JS?

http://jsfiddle.net/d0yhve8y/



Solution 1:[1]

I always set preload class to body with animation time value 0 and its working pretty well. I have some back going transitions so I have to remove load animation to them too. I solved this by temporary setting animation time to 0. You can change transitions to match yours.

HTML

... <body class="preload">...

CSS is setting animation to 0s

body.preload *{
animation-duration: 0s !important;
-webkit-animation-duration: 0s !important;
transition:background-color 0s, opacity 0s, color 0s, width 0s, height 0s, padding 0s, margin 0s !important;}

JS will remove class after some delay so animations can happen in normal time :)

setTimeout(function(){
    document.body.className="";
},500);

Solution 2:[2]

Is there a way to do this pure CSS ?

Yes, absolutely : See the fork http://jsfiddle.net/5r32Lsme/2/ There is really no need for JS.

and I'd prefer it to run only after a hover event.

So you need to tell CSS what happens when it is NOT a hover event as well - in your example :

#container:not(:hover) #content {
  visibility: hidden;
  transition: visibility 0.01s 1s;
}

But there are two things to note:

1) The transition delay above should match your animation duration

2) You can't use the property which you use to hide the animation onLoad in the animation. If you do need visibility in the animation, hide the animation initially like e.g.

#container:not(:hover) #content {
  top: -8000px;
  transition: top 0.01s 1s;
}    

A sidenote:

It is recommended to put native CSS properties after prefixed ones, so it should be

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

and now there is a native transform

-webkit-transform: translateY(0);
transform: translateY(0);

Solution 3:[3]

This is not pure CSS but maybe someone will stumble across this thread as I did:

In React I solved this by setting a temporary class in ComponentDidMount() like so:

componentDidMount = () => {
    document.getElementById("myContainer").className =
        "myContainer pageload";
};

and then in css:

.myContainer.pageload {
    animation: none;
}

.myContainer.pageload * {
    animation: none;
}

If you are not familiar the " *" (n.b. the space) above means that it applies to all descendents of the element as well. The space means all descendents and the asterisk is a wildcard operator that refers to all types of elements.

Solution 4:[4]

If you're looking at this after 2019, a better solution is this:

let div = document.querySelector('div')
document.addEventListener('DOMContentLoaded', () => {
    // Adding timeout to simulate the loading of the page
    setTimeout(() => {
        div.classList.remove('prevent-animation')
    }, 2000)
    
    document.querySelector('button').addEventListener('click', () => {
        if(div.classList.contains('after')) {
            div.classList.remove('after')
        } else {
            div.classList.add('after')
        }
    })
})
div {
    background-color: purple;
    height: 150px;
    width: 150px;
}

.animated-class {
    animation: animationName 2000ms;
}

.animated-class.prevent-animation {
    animation-duration: 0ms;
}

.animated-class.after {
    animation: animation2 2000ms;
    background-color: orange;
}

@keyframes animationName {
    0% {
        background-color: red;
    }
    50% {
        background-color: blue;
    }
    100% {
        background-color: purple;
    }
}

@keyframes animation2 {
    0% {
        background-color: salmon;
    }
    50% {
        background-color: green;
    }
    100% {
      background-color: orange;
    }
}
<div class="animated-class prevent-animation"></div>
<button id="btn">Toggle between animations</button>

Solution 5:[5]

It's always better a solution without relying on javascript.

The ones with CSS mentioned here are ok. The idea of hiding when not on mouse hover is fine for some situations, but I noticed that if I wanted the animation to happen when the mouse moves out of the element, it wouldn't happen because of the :not(:hover) rule.

The solution I came up worked best for me, by adding a animation to the parent element, that only adds opacity at the end with the same duration. Easier shown than explain:

I grabbed the fiddle made by @sebilasse and @9000 and I added the below code there:

https://jsfiddle.net/marcosrego/vqo3sr8z/2/

#container{
    animation: animShow 1s forwards;    
}

@keyframes animShow {
    0% {
      opacity: 0;
    }
    99% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
}

Solution 6:[6]

Rotation animation that (appears) not to run until needed.
The CSS below allows for up and down arrows for showing menu items. The animation does not appear to run on page load, but it really does.

@keyframes rotateDown {
   from { transform: rotate(180deg); }
   to   { transform: rotate(0deg); }
}

@keyframes rotateUp {
   from { transform: rotate(180deg); }
   to   { transform: rotate(0deg); }
}

div.menu input[type='checkbox'] + label.menu::before {
   display            :inline-block;
   content            : "?";
   color              : #b78369;
   opacity            : 0.5;
   font-size          : 1.2em;
}

div.menu input[type='checkbox']:checked + label.menu::before {
   display            : inline-block;
   content            : "?";
   color              : #b78369;
   opacity            : 0.5;
   font-size          : 1.2em;
}

div.menu input[type='checkbox'] + label.menu {
   display            : inline-block;
   animation-name     : rotateDown;
   animation-duration : 1ms;
}

div.menu input[type='checkbox']:checked + label.menu {
   display            : inline-block;
   animation-name     : rotateUp;
   animation-duration : 1ms;
}

div.menu input[type='checkbox'] + label.menu:hover {
   animation-duration : 500ms;
}

div.menu input[type='checkbox']:checked + label.menu:hover {
   animation-duration : 500ms;
}

From top to bottom:

  1. Create the rotations. For this there are two... one for the down arrow and one for the up arrow. Two arrows are needed, because, after the rotation, they return to their natural state. So, the down arrow starts up and rotates down, while the up arrow starts down and rotates up.
  2. Create the little arrows. This is a straight forward implementation of ::before
  3. We put the animation on the label. There is nothing special, there, except that the animation duration is 1ms.
  4. The mouse drives the animation speed. When the mouse hovers over the element, the animation-duration is set to enough time to seem smooth.

Working on my site

Solution 7:[7]

Having had to solve a similar challenge, a neat CSS-only trick morewry posted already back in 2013 is to create an animation that initially is in a paused play-state on a keyframe hiding the element:

#content {
  animation:animDown 1s ease, hasHovered 1ms paused;
  animation-fill-mode: forwards; /* for both animations! */
}
#container:hover #content {
  animation:animUp 1s ease, hasHovered 1ms;
}

/* hide #content element until #container has been hovered over */
@keyframes hasHovered {
  0% { visibility: hidden; }     /* property has to be removed */
  100% { visibility: visible; }  /* from the other animations! */
}

When hovering, the very brief animated transformation is applied and stays in the 100%-keyframe-state even after mouse-leave thanks to the animation-fill-mode.

For how to set animation sub-properties with multiple animations, see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations#setting_multiple_animation_property_values

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tominator
Solution 2 9000
Solution 3 ClownBaby
Solution 4 Guilherme P.
Solution 5 Marcos Rego
Solution 6 user1126454
Solution 7 eMPee584