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(" ");
    };
}
\$\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>
\$\endgroup\$

Your Answer

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

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