Reputation: 406
I have 3 values in the select: rock, paper, scissor.
The first time I choose a value in the select (rock, paper or scissor), the onChange event fires, and the @keyframes animation works for all 3 values.
But the second time I choose any of the values, the animation no longer works. I do not understand why.
The code is this: jsfiddle.net/9efvxph3/83/
body {
background: radial-gradient(gold, goldenrod);
font-family: monospace;
text-align: center;
font-size: 1.5rem;
}
@keyframes example {
0% {
transform: scale(1);
}
100% {
transform: scale(1.3);
}
}
img {
padding: 10px;
height: 100px;
width: auto;
}
img:hover {
transform: scale(1.4);
transition: all 1s;
}
<select id="choice" onchange="winner()">
<option value="" disabled selected>choose</option>
<option value="rock">rock</option>
<option value="paper">paper</option>
<option value="scissors">scissors</option>
</select>
<img src="https://img.icons8.com/windows/2x/hand-rock.png" class="user-choice">
<img src="https://img.icons8.com/wired/2x/paper.png" class="user-choice">
<img src="https://img.icons8.com/ios/2x/hand-scissors.png" class="user-choice">
<h3 id="result"> </h3>
<script>
const type = ["paper", "scissors", "rock"];
function winner() {
var index = document.getElementById("choice").selectedIndex;
var userChooice = document.getElementById("choice").value;
var PcChooice = type[Math.floor(Math.random() * type.length)];
document.getElementsByClassName("user-choice").item(index - 1).style = "animation:example 0.5s alternate";
if (PcChooice == userChooice) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You tied";
} else {
if ((userChooice == "rock") && (PcChooice == "scissors")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "paper") && (PcChooice == "rock")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "scissors") && (PcChooice == "paper")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You loose";
}
}
document.getElementById("choice").selectedIndex = 0;
};
</script>
Upvotes: 0
Views: 894
Reputation: 542
You can simply fix it by adding and removing classname instead of directly updating the element styles.
change the below line
document.getElementsByClassName("user-choice").item(index - 1).style = "animation:example 0.5s alternate";
To
document.getElementsByClassName("user-choice").item(index - 1).classList.add('animate');
setTimeout(() => {
document.getElementsByClassName("user-choice").item(index - 1).classList.remove('animate');
}, 1000)
Where the setimeout delay timing is based on the animation duration
body {
background: radial-gradient(gold, goldenrod);
font-family: monospace;
text-align: center;
font-size: 1.5rem;
}
@keyframes example {
0% {
transform: scale(1);
}
100% {
transform: scale(1.3);
}
}
img {
padding: 10px;
height: 100px;
width: auto;
}
img:hover {
transform: scale(1.4);
transition: all 1s;
}
.animate {
animation: example 0.5s alternate;
}
<select id="choice" onchange="winner()">
<option value="" disabled selected>choose</option>
<option value="rock">rock</option>
<option value="paper">paper</option>
<option value="scissors">scissors</option>
</select>
<img src="https://img.icons8.com/windows/2x/hand-rock.png" class="user-choice">
<img src="https://img.icons8.com/wired/2x/paper.png" class="user-choice">
<img src="https://img.icons8.com/ios/2x/hand-scissors.png" class="user-choice">
<h3 id="result"> </h3>
<script>
const type = ["paper", "scissors", "rock"];
function winner() {
var index = document.getElementById("choice").selectedIndex;
var userChooice = document.getElementById("choice").value;
var PcChooice = type[Math.floor(Math.random() * type.length)];
document.getElementsByClassName("user-choice").item(index - 1).classList.add('animate');
setTimeout(() => {
document.getElementsByClassName("user-choice").item(index - 1).classList.remove('animate');
}, 1000)
if (PcChooice == userChooice) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You tied";
} else {
if ((userChooice == "rock") && (PcChooice == "scissors")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "paper") && (PcChooice == "rock")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "scissors") && (PcChooice == "paper")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You loose";
}
}
document.getElementById("choice").selectedIndex = 0;
};
</script>
Upvotes: 0
Reputation: 13880
This is because you're adding the animation inline once on the first selection, and then never removing it, so it's never invoked again. The easiest way without refactoring everything is to remove the inline style after the animation using the animationend
event, or even using something like setTimeout()
with a delay longer than the animation.
Simply adding the following somewhere after you've added the animation would work:
document.getElementsByClassName("user-choice").item(index - 1).addEventListener("animationend", function(evt){
evt.target.style = "";
});
Or, the less elegant:
setTimeout(function(){
document.getElementsByClassName("user-choice").item(index - 1).style = "";
}.bind(index), 510 );
That should be all you need to "fix the problem", but looking at readability may help a little as well.
To clean things up a bit more - generally if you're requesting an element, nodelist, (or anything really) that's not a static property, you should consider storing it in a variable. Instead of parsing the DOM multiple times for the same thing in the same if/else blocks, you can store it as a variable, and invoke a function (like innerText
) once at the end.
This is beneficial to prevent needing to update references in multiple places if you need to make a change.
Lastly, you may be better suited by attaching your keyframe animation to a class, and adding/removing the class with classList
.
const type = ["paper", "scissors", "rock"],
result = document.getElementById('result'),
choice = document.getElementById('choice'),
choices = document.getElementsByClassName('user-choice');
function winner(){
// Prevent changes while the game is "running"
choice.disabled = true;
var userChoice = choice.value,
pcChoice = type[Math.floor(Math.random() * type.length)],
selected = choices[choice.selectedIndex-1];
status;
if( pcChoice == userChoice ){
status = 'tied';
} else {
if( userChoice == 'rock' && pcChoice == 'scissors' ){
status = 'win';
} else if( userChoice == 'paper' && pcChoice == 'rock' ){
status = 'win';
} else if( userChoice == 'scissors' && pcChoice == 'paper' ){
status = 'win';
} else {
status = 'lose';
}
}
selected.classList.add('animated');
result.innerText = userChoice + ' - ' + pcChoice + ': You ' + status;
// After the animation, reset everything to its default state
selected.addEventListener('animationend', function(evt){
choice.disabled = false;
choice.selectedIndex = 0;
evt.target.classList.remove('animated');
});
};
body {
background: radial-gradient(gold, goldenrod);
font-family: monospace;
text-align: center;
font-size: 1.5rem;
}
@keyframes example {
0% { transform: scale(1); }
100% { transform: scale(1.3); }
}
img {
padding: 10px;
height: 100px;
width: auto;
}
img:hover {
transform: scale(1.4);
transition: all 1s;
}
img.animated {
animation: example 0.5s alternate;
}
<select id="choice" onchange="winner()">
<option value="" disabled selected>choose</option>
<option value="rock">rock</option>
<option value="paper">paper</option>
<option value="scissors">scissors</option>
</select>
<img src="https://img.icons8.com/windows/2x/hand-rock.png" class="user-choice">
<img src="https://img.icons8.com/wired/2x/paper.png" class="user-choice">
<img src="https://img.icons8.com/ios/2x/hand-scissors.png" class="user-choice">
<h3 id="result"> </h3>
Upvotes: 1
Reputation: 3010
First time when you choose the value in the select onchange event fires and animation style is added. (permanently added in the dom)
Second time when you choose any of the values onchange event will fire but the animation will not work because it is already there in the dom.
Add .animate
class onchange
.
CSS:
.animate {
animation: example 0.5s alternate;
}
JS:
document.getElementsByClassName("user-choice").item(index - 1).classList.add('animate');
Before adding .animate
class remove .animate class from all .user-choice
elements.
var choicess = document.getElementsByClassName("user-choice");
[].forEach.call(choicess, function(el) {
el.classList.remove('animate');
});
body {
background: radial-gradient(gold, goldenrod);
font-family: monospace;
text-align: center;
font-size: 1.5rem;
}
.animate {
animation: example 0.5s alternate;
}
@keyframes example {
0% {
transform: scale(1);
}
100% {
transform: scale(1.3);
}
}
img {
padding: 10px;
height: 100px;
width: auto;
}
img:hover {
transform: scale(1.4);
transition: all 1s;
}
<select id="choice" onchange="winner()">
<option value="" disabled selected>choose</option>
<option value="rock">rock</option>
<option value="paper">paper</option>
<option value="scissors">scissors</option>
</select>
<img src="https://img.icons8.com/windows/2x/hand-rock.png" class="user-choice">
<img src="https://img.icons8.com/wired/2x/paper.png" class="user-choice">
<img src="https://img.icons8.com/ios/2x/hand-scissors.png" class="user-choice">
<h3 id="result"> </h3>
<script>
const type = ["paper", "scissors", "rock"];
function winner() {
var index = document.getElementById("choice").selectedIndex;
var userChooice = document.getElementById("choice").value;
var PcChooice = type[Math.floor(Math.random() * type.length)];
var choicess = document.getElementsByClassName("user-choice");
[].forEach.call(choicess, function(el) {
el.classList.remove('animate');
});
document.getElementsByClassName("user-choice").item(index - 1).classList.add('animate');
if (PcChooice == userChooice) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You tied";
} else {
if ((userChooice == "rock") && (PcChooice == "scissors")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "paper") && (PcChooice == "rock")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else if ((userChooice == "scissors") && (PcChooice == "paper")) {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You win";
} else {
document.getElementById("result").innerHTML = userChooice + " - " + PcChooice + ": You loose";
}
}
document.getElementById("choice").selectedIndex = 0;
};
</script>
Upvotes: 0
Reputation: 101
Based on your code it's best to end your function by adding and event listener related to the end of the animation, removing the animation from the style of the element.
Add the code below at the end of your winner() function:
document.getElementsByClassName("user-choice").item(index - 1).addEventListener("animationend", function(e) {
e.target.style = "";
});
Upvotes: 1