skip to Main Content

I’m animating a slide-in header. After a certain HTML element scrolls to the top of the page, a header is supposed to slide in. When the element is scrolled back away from the top of the page, it is supposed to slide out. The problem I’m having is that when the element reaches the top of the page, both animations happen at the same time.

The example is at https://codepen.io/scottthomason/pen/VwNZVLd. Here is the code for posterity. Make sure the browser output window is sized small enough to allow for scrolling.

HTML

<div class="header">
    Header goes here.
</div>
<div class="content">
    <h1>Content 1</h1>
    <h1>Content 2</h1>
    <h1 class="trigger">Content 3</h1>
    <h1>Content 4</h1>
    <h1>Content 5</h1>
    <h1>Content 6</h1>
    <h1>Content 7</h1>
    <h1>Content 8</h1>
</div>

CSS

.header {
    border: 3px solid green;
    color: white;
    background-color: red;
    padding: 16px;
    width: 100%;
    position: fixed;
    top: -60px; 
    opacity: 0;
    animation-duration: 0.5s;
}

@keyframes fade-in {
    from { top: -60px; opacity: 0; }
    to { top: 0; opacity: 1; }
}

@keyframes fade-out {
    from { top: 0; opacity: 1; }
    to { top: -60px; opacity: 0; }
}

Javascript

let header = document.querySelector( '.header' );    
let trigger = document.querySelector( '.trigger' );
    
window.addEventListener( 'scroll', function( e ) {
    let topPos = trigger.getBoundingClientRect().top;
    if ( topPos <= 10 ) {
        console.log( 'Fading in, topPos:', topPos );
        header.style.animationName = 'fade-in';
    } else {
        console.log( 'Fading out, topPos:', topPos );
        header.style.animationName = 'fade-out';
    }
} );

3

Answers


  1. Whenever the browser executes an animation, it will revert back to the styles applied to the element previously. So in your case, it will play the fade-in animation just fine, but the applied styles are still the ones in your .header class, which is in a hidden state.

    There are different ways to address this, such as separating the two styles in different classes and applying them through JS along with your animation class, but I would suggest a one-liner that will handle this for you. Just add animation-fill-mode: forward; to your .header class and it will inform the browser to retain the computed values of your last keyframe.

    You can find more information on MDN.

    Login or Signup to reply.
  2. You can use the shorthand animation and use the forwards for the animation not to return to initial state

    let header = document.querySelector( '.header' );    
    let trigger = document.querySelector( '.trigger' );
    
    window.addEventListener( 'scroll', function( e ) {
    
        let topPos = trigger.getBoundingClientRect().top;
        if ( topPos <= 10) {
            console.log( 'Fading in, topPos:', topPos );
            header.style.animationName = 'fade-in';
        } else {
            console.log( 'Fading out, topPos:', topPos );
            header.style.animationName = 'fade-out';
        }
    } );
    .header {
      border: 3px solid green;
      color: white;
      background-color: red;
      padding: 16px;
      width: 100%;
      position: fixed;
      top: -60px; 
      opacity: 0;
      animation: 0.4s forwards; /*animation shorthand*/
    }
    
    @keyframes fade-in {
      from { top: -60px; opacity: 0; }
      to { top: 0; opacity: 1; }
    }
    
    @keyframes fade-out {
      from { top: 0; opacity: 1; }
      to { top: -60px; opacity: 0; }
    }
      <div class="header">
        Header goes here.
    </div>
    <div class="content">
        <h1>Content 1</h1>
        <h1>Content 2</h1>
        <h1 class="trigger">Content 3</h1>
        <h1>Content 4</h1>
        <h1>Content 5</h1>
        <h1>Content 6</h1>
        <h1>Content 7</h1>
        <h1>Content 8</h1>
    </div>
    Login or Signup to reply.
  3. The problem that arises because the header is going back to its initial state after the animation played.

    Instead of using keyframes for your animation, you can use the transition attribute in your header and manipulate the opacity and position with JavaScript.

    CSS:

    .header {
        border: 3px solid blue;
        color: white;
        background-color: red;
        padding: 16px;
        width: 100%;
        position: fixed;
        top: -60px; 
        opacity: 0;
        animation-duration: 0.5s;
        transition: 0.5s ease
    }
    

    JS:

    let header = document.querySelector( '.header' );    
    let trigger = document.querySelector( '.trigger' );
        
    window.addEventListener( 'scroll', function( e ) {
        let topPos = trigger.getBoundingClientRect().top;
        if ( topPos <= 10 ) {
            header.style.opacity = '1';
            header.style.top = '0';
        } else {
            header.style.opacity = '0';
            header.style.top = '-60px';
        }
    } );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search