2
\$\begingroup\$

Started working with CSS animations and I'm wondering if there's a more elegant way to manage (with class names) when an animation is on or off. The general pattern I have is like this:

<div class="base-class"></div>
<div class="base-class base-class-rest"></div>
<div class="base-class base-class-active"></div>

I really dislike the idea of having a css class for base-class-rest that is different than just the base-class. I came up with this solution because I needed a css state that animates from the end of the base-class-active state back to a state of "rest". base-class alone cannot achieve this because there is no animation defined in its css.

tl;dr: I'm toggling a color on and off but I've written too much code for a task this simple. How can I write less code?

Here's a pen: http://codepen.io/anon/pen/GpaJQa

html

<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>

css

@keyframes box-active {
 0% { background-color: black; }
 100% { background-color: green; }
}
@keyframes box-rest {
 0% { background-color: green; }
 100% { background-color: currentColor; }
}
.box {
 height: 50px;
 width: 50px;
 background-color: black;
 display: inline-block;
 margin: 5px;
}
.box-active {
 animation: box-active 1s 1 forwards;
}
.box-rest {
 animation: box-rest 1s 1 forwards;
}

js

Array.prototype.slice.call(document.querySelectorAll(".box")).forEach(function(el) {
 el.addEventListener("click", click("box", "box-rest", "box-active"));
});
function click(none, rest, active) {
 return function(e) {
 var classname = e.target.className;
 var arr = classname.split(" ");
 var index = [arr.indexOf(none), arr.indexOf(rest), arr.indexOf(active)];
 //no state
 if (index[1] < 0 && index[2] < 0) {
 arr.push(active);
 return e.target.className = arr.join(" ");
 }
 //at rest
 if (index[2] > -1) {
 arr.splice(index[1], 1);
 arr.push(rest);
 }
 //active
 else {
 arr.splice(index[2], 1);
 arr.push(active);
 }
 e.target.className = arr.join(" ");
 };
}
asked Nov 23, 2015 at 14:51
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You can use the toggle method from Element.classList and the animationend event in your example:

<script>
 Array.prototype.slice.call(document.querySelectorAll(".box")).forEach(function(el) {
 el.addEventListener('click', click());
 el.addEventListener('animationend', resetBox());
 });
 function click() {
 return function(e) {
 var target = e.target || e.srcElement;
 if (target.classList.contains('box')) {
 if (target.classList.contains('box-active')) {
 target.classList.toggle('box-rest');
 } else {
 target.classList.toggle('box-active');
 }
 }
 };
 }
 function resetBox() {
 return function(e) {
 var target = e.target || e.srcElement;
 if (target.classList.contains('box-rest')) {
 target.className = 'box';
 }
 };
 }
</script>
answered Nov 23, 2015 at 21:01
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.