Search code examples
csscss-animations

CSS force animation to be fully completed on click, active or focus?


Given:

.button_focus, .button_active {
  width: 8rem;
  height: 5rem;
  background-color: #e4e7eb;
}

.button_focus:focus,
.button_active:active {
  animation-name: clickAnimation;
  animation-duration: 250ms;
  animation-timing-function: ease-in-out;
}

@KeyFrames clickAnimation {
    0.00% { background-color: #d5d7da; }
   14.29% { background-color: #ced0d3; }
   28.57% { background-color: #bbbbbb; }
   42.86% { background-color: #b1b2b3; }
   57.14% { background-color: #b1b2b3; }
   71.43% { background-color: #bbbbbb; }
   85.71% { background-color: #ced0d3; }
  100.00% { background-color: #d5d7da; } 
}
<div class="contianer">
  <button class="button_focus">
    focus
  </button>
  <button class="button_active">
    active
  </button>
</div>

I want to find a way to be able to spam-click the button and the animation to fully be processed every time. Currently, with the :focus pseudo-class, I need to click on the button then click away, for the animation to be reinitialized when I click again on the button.

Conversely, if I use the :active pseudo-class, the animation is played on every successive click but it is not fully completed. I need to press the button for 250ms for the animation to be fully completed.


There are a few posts on this matter on SO, and the solutions seem to add an animation class using JS and removing it afterwards, but in most posts, the questions involve hovering. In my case, it's just a click so I don't understand how I can add an animation class and removing it as well at some point. I guess I am just confused.

Does anyone have any ideas or tips?


Solution

  • The problem is that the focus and active pseudo classes are sort of too lingering or too ephemeral' - so to get rid of fous the user has to move away and to keep the active they have to keep pressing.

    Using JS we can listen for the click on the button, add a class which sets the animation.

    We keep a permanent listen out for the animationend event on the button, and when that is triggered we remove the class. That way the animation is not 'disturbed' until it has finished, but we do need to remove it so that on the next click it can be set again (otherwise if there is no change to the setting CSS thinks it's done it).

    const button = document.querySelector('.button_click');
    button.addEventListener('click', function() {
      button.classList.add('clicked');
    });
    button.addEventListener('animationend', function() {
      button.classList.remove('clicked');
    });
    .button_click {
      width: 8rem;
      height: 5rem;
      background-color: #e4e7eb;
      animation-name: none;
    }
    
    .button_click.clicked {
      animation-name: clickAnimation;
      animation-duration: 250ms;
      animation-timing-function: ease-in-out;
    }
    
    @KeyFrames clickAnimation {
      0.00% {
        background-color: #d5d7da;
      }
      14.29% {
        background-color: #ced0d3;
      }
      28.57% {
        background-color: #bbbbbb;
      }
      42.86% {
        background-color: #b1b2b3;
      }
      57.14% {
        background-color: #b1b2b3;
      }
      71.43% {
        background-color: #bbbbbb;
      }
      85.71% {
        background-color: #ced0d3;
      }
      100.00% {
        background-color: #d5d7da;
      }
    }
    <div class="contianer">
      <button class="button_click">
        click me
      </button>
    </div>