2

In the following example I have 3 divs they need to be animated using CSS3 Keyframe Animation with the following rules (when I mention hidden/show in the text below actually use opacity 0.1/1 in the CSS):

A) When the page load

  • ladyDefault is shown
  • ladyCorrect is hidden
  • ladyWrong is hidden

B) When User click btnCorrect

  • ladyDefault is hidden
  • ladyCorrect is shown
  • ladyWrong is hidden

C) When User click btnWrong

  • ladyDefault is hidden
  • ladyCorrect is hidden
  • ladyWrong is shown

Currently:

  • Sequence case A) and B) work fine.

  • Sequence case A) and C) work fine.

But I am not able to fix the following cases:

  • Sequence case A) and B) and C) does not work(ladyWrong is not shown).

  • Sequence case A) and C) and B) does not work (ladyCorrect is not shown).

I would like to know how to fix it possibly using only CSS 3.

Test the following example using only Google Chrome.

var btnCorrect = document.getElementById('btnCorrect');
var btnWrong = document.getElementById('btnWrong');
var ladyDefault = document.getElementById('ladyDefault');
var ladyCorrect = document.getElementById('ladyCorrect');
var ladyWrong = document.getElementById('ladyWrong');

btnCorrect.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyWrong.classList.add('hideLadyWrong');
    ladyCorrect.classList.add('showLadyCorrect');
});
btnWrong.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyCorrect.classList.add('hideLadyCorrect');
    ladyWrong.classList.add('showLadyWrong');
});
#ladyDefault,
#ladyCorrect,
#ladyWrong {
    width: 100px;
    height: 150px;
    display: inline-block;
    margin: 5px;
}

#ladyDefault {
    background-color: blue;
}

#ladyCorrect {
    background-color: green;
    opacity: 0.1;
}

#ladyWrong {
    background-color: red;
    opacity: 0.1;
}

#btnCorrect,
#btnWrong {
    height: 50px;
    width: 100px;
    display: inline-block;
    margin: 5px;
}

#btnCorrect {
    background-color: lime;
}

#btnWrong {
    background-color: darkred;
}
/*
--------------------------- lady default
*/
@keyframes hideLadyDefault {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyDefault {
    animation-name: hideLadyDefault;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}
/*
--------------------------- lady correct
*/
@keyframes showLadyCorrect {
    0% {
        opacity: 0.1;
    }
    100% {
        opacity: 1;
    }
}

.showLadyCorrect {
    animation-name: showLadyCorrect;
    animation-duration: 1s;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}

@keyframes hideLadyCorrect {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyCorrect {
    animation-name: hideLadyCorrect;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: both
    animation-delay: 0;
}
/*
--------------------------- lady wrong
*/
@keyframes showLadyWrong {
    0% {
        opacity: 0.1;
    }
    100% {
        opacity: 1;
    }
}

.showLadyWrong {
    animation-name: showLadyWrong;
    animation-duration: 1s;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}

@keyframes hideLadyWrong {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyWrong {
    animation-name: hideLadyWrong;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: both;
    animation-delay: 0;
}
<div id="ladyDefault">ladyDefault</div>
<div id="ladyCorrect">ladyCorrect</div>
<div id="ladyWrong">ladyWrong</div>

<div id="btnCorrect">btnCorrect</div>
<div id="btnWrong">btnWrong</div>
GibboK
  • 71,848
  • 143
  • 435
  • 658

2 Answers2

2

Original Answer:

You need to remove the previous classes also before adding the new ones. When you don't remove the other class before adding a new one, each of them try to set an animation on the same element and as is always the case with CSS, the selector (class) which is defined later in the CSS wins.

var btnCorrect = document.getElementById('btnCorrect');
var btnWrong = document.getElementById('btnWrong');
var ladyDefault = document.getElementById('ladyDefault');
var ladyCorrect = document.getElementById('ladyCorrect');
var ladyWrong = document.getElementById('ladyWrong');

btnCorrect.addEventListener('click', function(event) {
  ladyDefault.classList.add('hideLadyDefault');
  ladyWrong.classList.remove('showLadyWrong');
  ladyWrong.classList.add('hideLadyWrong');
  ladyCorrect.classList.remove('hideLadyCorrect');
  ladyCorrect.classList.add('showLadyCorrect');
});
btnWrong.addEventListener('click', function(event) {
  ladyDefault.classList.add('hideLadyDefault');
  ladyCorrect.classList.remove('showLadyCorrect');
  ladyCorrect.classList.add('hideLadyCorrect');
  ladyWrong.classList.remove('hideLadyWrong');
  ladyWrong.classList.add('showLadyWrong');
});
#ladyDefault,
#ladyCorrect,
#ladyWrong {
  width: 100px;
  height: 150px;
  display: inline-block;
  margin: 5px;
}
#ladyDefault {
  background-color: blue;
}
#ladyCorrect {
  background-color: green;
  opacity: 0.1;
}
#ladyWrong {
  background-color: red;
  opacity: 0.1;
}
#btnCorrect,
#btnWrong {
  height: 50px;
  width: 100px;
  display: inline-block;
  margin: 5px;
}
#btnCorrect {
  background-color: lime;
}
#btnWrong {
  background-color: darkred;
}
/*
--------------------------- lady default
*/

@keyframes hideLadyDefault {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0.1;
  }
}
.hideLadyDefault {
  animation-name: hideLadyDefault;
  animation-duration: 0;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
/*
--------------------------- lady correct
*/

@keyframes showLadyCorrect {
  0% {
    opacity: 0.1;
  }
  100% {
    opacity: 1;
  }
}
.showLadyCorrect {
  animation-name: showLadyCorrect;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
@keyframes hideLadyCorrect {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0.1;
  }
}
.hideLadyCorrect {
  animation-name: hideLadyCorrect;
  animation-duration: 0;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: both animation-delay: 0;
}
/*
--------------------------- lady wrong
*/

@keyframes showLadyWrong {
  0% {
    opacity: 0.1;
  }
  100% {
    opacity: 1;
  }
}
.showLadyWrong {
  animation-name: showLadyWrong;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
@keyframes hideLadyWrong {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0.1;
  }
}
.hideLadyWrong {
  animation-name: hideLadyWrong;
  animation-duration: 0;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: both;
  animation-delay: 0;
}
<div id="ladyDefault">ladyDefault</div>
<div id="ladyCorrect">ladyCorrect</div>
<div id="ladyWrong">ladyWrong</div>

<div id="btnCorrect">btnCorrect</div>
<div id="btnWrong">btnWrong</div>

Solution with only one keyframe rule:

You can also achieve the same effect with a single @keyframes rule instead of using multiple. All that would be needed is to set the animation-direction as reverse for the hide. But the old class must still be removed because once an animation is set on the element, the UA remembers its execution as mentioned in this answer of mine.

var btnCorrect = document.getElementById('btnCorrect');
var btnWrong = document.getElementById('btnWrong');
var ladyDefault = document.getElementById('ladyDefault');
var ladyCorrect = document.getElementById('ladyCorrect');
var ladyWrong = document.getElementById('ladyWrong');

btnCorrect.addEventListener('click', function(event) {
  ladyDefault.classList.add('hideLadyDefault');
  ladyWrong.classList.remove('showLadyWrong');
  ladyWrong.classList.add('hideLadyWrong');
  ladyCorrect.classList.remove('hideLadyCorrect');
  ladyCorrect.classList.add('showLadyCorrect');
});
btnWrong.addEventListener('click', function(event) {
  ladyDefault.classList.add('hideLadyDefault');
  ladyCorrect.classList.remove('showLadyCorrect');
  ladyCorrect.classList.add('hideLadyCorrect');
  ladyWrong.classList.remove('hideLadyWrong');
  ladyWrong.classList.add('showLadyWrong');
});
#ladyDefault,
#ladyCorrect,
#ladyWrong {
  width: 100px;
  height: 150px;
  display: inline-block;
  margin: 5px;
}
#ladyDefault {
  background-color: blue;
}
#ladyCorrect {
  background-color: green;
  opacity: 0.1;
}
#ladyWrong {
  background-color: red;
  opacity: 0.1;
}
#btnCorrect,
#btnWrong {
  height: 50px;
  width: 100px;
  display: inline-block;
  margin: 5px;
}
#btnCorrect {
  background-color: lime;
}
#btnWrong {
  background-color: darkred;
}
/*
--------------------------- lady default
*/

.hideLadyDefault {
  animation-name: show;
  animation-duration: 0;
  animation-iteration-count: 1;
  animation-direction: reverse;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
/*
--------------------------- lady correct
*/

@keyframes show {
  0% {
    opacity: 0.1;
  }
  100% {
    opacity: 1;
  }
}
.showLadyCorrect {
  animation-name: show;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
.hideLadyCorrect {
  animation-direction: reverse;
}
/*
--------------------------- lady wrong
*/

.showLadyWrong {
  animation-name: show;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: forwards;
  animation-delay: 0;
}
.hideLadyWrong {
  animation-direction: reverse;
}
<div id="ladyDefault">ladyDefault</div>
<div id="ladyCorrect">ladyCorrect</div>
<div id="ladyWrong">ladyWrong</div>

<div id="btnCorrect">btnCorrect</div>
<div id="btnWrong">btnWrong</div>

Solution with transition: (might be better fit)

The better option would be to use transition if you could instead of animation. Here you could just set the opacity via inline style attributes and let the transition property handle the rest. Unlike the animations, transitions can execute the reverse state by default. (This is the best bet if it only a simple change of opacity from 0.1 to 1, if it is a complex multi-step change the transitions are out of scope.)

var btnCorrect = document.getElementById('btnCorrect');
var btnWrong = document.getElementById('btnWrong');
var ladyDefault = document.getElementById('ladyDefault');
var ladyCorrect = document.getElementById('ladyCorrect');
var ladyWrong = document.getElementById('ladyWrong');

btnCorrect.addEventListener('click', function(event) {
  ladyDefault.style["opacity"] = "0.1";
  ladyWrong.style["opacity"] = "0.1";
  ladyCorrect.style["opacity"] = "1";
});
btnWrong.addEventListener('click', function(event) {
  ladyDefault.style["opacity"] = "0.1";
  ladyCorrect.style["opacity"] = "0.1";
  ladyWrong.style["opacity"] = "1";
});
#ladyDefault,
#ladyCorrect,
#ladyWrong {
  width: 100px;
  height: 150px;
  display: inline-block;
  margin: 5px;
}
#ladyDefault {
  background-color: blue;
  transition: all 1s ease;
}
#ladyCorrect {
  background-color: green;
  opacity: 0.1;
  transition: all 1s ease;
}
#ladyWrong {
  background-color: red;
  opacity: 0.1;
  transition: all 1s ease;
}
#btnCorrect,
#btnWrong {
  height: 50px;
  width: 100px;
  display: inline-block;
  margin: 5px;
}
#btnCorrect {
  background-color: lime;
}
#btnWrong {
  background-color: darkred;
}
<div id="ladyDefault">ladyDefault</div>
<div id="ladyCorrect">ladyCorrect</div>
<div id="ladyWrong">ladyWrong</div>

<div id="btnCorrect">btnCorrect</div>
<div id="btnWrong">btnWrong</div>
Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • thanks very much for your answer, may I ask you... do you think it is possible to actually "rewind" the animation instead of removing the class? – GibboK Nov 08 '16 at 14:47
  • 1
    @GibboK: Yes, you can actually do this with one single keyframes itself by just playing with the `animation-direction`. Will update answer in a bit. – Harry Nov 08 '16 at 14:52
  • thanks for your comment, YES I am actually need a solution with `animation-direction` if you are also able to provide a brief explanation would be great. Once again thanks for your time on this. – GibboK Nov 08 '16 at 14:55
  • 1
    @GibboK: But you'd still need to specify when the direction should change to reverse, so there are two options that I can think of - (1) set/unset `animation-direction` via style property through JS (2) retain the remove class model (but atleast the keyframes is just one). Which one would you prefer? I'll probably post both versions in answer. – Harry Nov 08 '16 at 14:58
  • please post both versions thanks for your time on this. – GibboK Nov 08 '16 at 15:01
  • 1
    @GibboK: Sorry, I must have been in a confused state of mind when I gave you that comment. Once an animation has been executed we cannot execute the reverse unless we remove the `animation` either by removing the class (or) by changing `animation` inline styles also. Here is a thread that might give you some extra info - http://stackoverflow.com/questions/33347992/reuse-css-animation-in-reversed-direction-by-resetting-the-state/33351228#33351228. – Harry Nov 08 '16 at 15:12
  • 1
    @GibboK: Also, it might be better to just use `transition` instead of `animation`. (This is also mentioned in the other thread but I've added a demo here also.) – Harry Nov 08 '16 at 15:23
1

You are adding all classes correctly, but you are not removing the added classes later, thats the issue. Change your listeners to

btnCorrect.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyWrong.classList.add('hideLadyWrong');
    ladyCorrect.classList.add('showLadyCorrect');
    ladyCorrect.classList.remove('hideLadyCorrect'); //added
});
btnWrong.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyCorrect.classList.add('hideLadyCorrect');
    ladyWrong.classList.add('showLadyWrong');
    ladyWrong.classList.remove('hideLadyWrong'); //added
});

var btnCorrect = document.getElementById('btnCorrect');
var btnWrong = document.getElementById('btnWrong');
var ladyDefault = document.getElementById('ladyDefault');
var ladyCorrect = document.getElementById('ladyCorrect');
var ladyWrong = document.getElementById('ladyWrong');

btnCorrect.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyWrong.classList.add('hideLadyWrong');
    ladyCorrect.classList.add('showLadyCorrect');
    ladyCorrect.classList.remove('hideLadyCorrect');
});
btnWrong.addEventListener('click', function(event) {
    ladyDefault.classList.add('hideLadyDefault');
    ladyCorrect.classList.add('hideLadyCorrect');
    ladyWrong.classList.add('showLadyWrong');
    ladyWrong.classList.remove('hideLadyWrong');
});
#ladyDefault,
#ladyCorrect,
#ladyWrong {
    width: 100px;
    height: 150px;
    display: inline-block;
    margin: 5px;
}

#ladyDefault {
    background-color: blue;
}

#ladyCorrect {
    background-color: green;
    opacity: 0.1;
}

#ladyWrong {
    background-color: red;
    opacity: 0.1;
}

#btnCorrect,
#btnWrong {
    height: 50px;
    width: 100px;
    display: inline-block;
    margin: 5px;
}

#btnCorrect {
    background-color: lime;
}

#btnWrong {
    background-color: darkred;
}
/*
--------------------------- lady default
*/
@keyframes hideLadyDefault {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyDefault {
    animation-name: hideLadyDefault;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}
/*
--------------------------- lady correct
*/
@keyframes showLadyCorrect {
    0% {
        opacity: 0.1;
    }
    100% {
        opacity: 1;
    }
}

.showLadyCorrect {
    animation-name: showLadyCorrect;
    animation-duration: 1s;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}

@keyframes hideLadyCorrect {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyCorrect {
    animation-name: hideLadyCorrect;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: both
    animation-delay: 0;
}
/*
--------------------------- lady wrong
*/
@keyframes showLadyWrong {
    0% {
        opacity: 0.1;
    }
    100% {
        opacity: 1;
    }
}

.showLadyWrong {
    animation-name: showLadyWrong;
    animation-duration: 1s;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: forwards;
    animation-delay: 0;
}

@keyframes hideLadyWrong {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0.1;
    }
}

.hideLadyWrong {
    animation-name: hideLadyWrong;
    animation-duration: 0;
    animation-iteration-count: 1;
    animation-direction: normal;
    animation-fill-mode: both;
    animation-delay: 0;
}
<div id="ladyDefault">ladyDefault</div>
<div id="ladyCorrect">ladyCorrect</div>
<div id="ladyWrong">ladyWrong</div>

<div id="btnCorrect">btnCorrect</div>
<div id="btnWrong">btnWrong</div>
Nikhil Nanjappa
  • 6,454
  • 3
  • 28
  • 44