Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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(" ");
    };
}
share|improve this question

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>
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.