Run css animation back and forth on clicks

I would consider using the animation-play-state property and also use infinite and alternate. The idea is to run the animation and stop it when it ends, then run it again and so on:

const div = document.querySelector('.target')
div.addEventListener('click', (e) => {
   div.classList.add('play');
   setTimeout(function() {
      div.classList.remove('play');
   },2000)
})
.target {
  width: 100px;
  height: 100px;
  background-color: gray;
  cursor: pointer;
  animation: action 2s linear alternate infinite;
  animation-play-state:paused;
}
.play {
  animation-play-state:running;
}

@keyframes action {
  0% {
    background-color: gray; 
  }  

  50% {
    background-color: red;
  }

  100% {
     background-color: green;
  }
}
<div class="target">

</div>


Handling this kind of on/off changes with animations is always tricky, and it is very difficult to handle the repeated changes smoothly and seamlessly.

For your case, I would suggest to change it to a single transition. Browser handle much better the on/off part in this case. To achieve a double change in the background color with a transition, create a gradient with 3 colors, and change the gradient position:

const div = document.querySelector('.test')
div.addEventListener('click', (e) => {
   div.classList.toggle('play');
})
.test {
  border: solid 1px black;
  margin: 10px;
  height: 100px;
  width: 100px;
  background-image: linear-gradient(gray 0%, gray 20%, blue 40%, blue 60%,
   red 80%, red 100%);
  background-size: 100% 9000%;
  background-repeat: no-repeat;
  background-position: center top;
  transition: background-position .3s;
}

.play {
  background-position: center bottom;
}
<div class="test">
</div>