Why do I have to click button twice before event fires? - javascript

A simple multiple choice quiz with one problem I can't solve. At first When I clicked the 'next question' button the next question and answers didn't show only when clicked a second time the next question and answers showed.
When I placed runningQuestion++ above questions[runningQuestion].displayAnswers()
like I did in the nextQuestion function the initial problem is solved but reappears after the last question when you are asked to try again. Only now when you click 'try again' now ofcourse it skips the first question.
class Question {
constructor(question, answers, correct) {
this.question = question;
this.answers = answers;
this.correct = correct;
}
displayAnswers() {
document.querySelector('.question').innerHTML = `<div class="q1">${this.question}</div>`
let i = 0
let answers = this.answers
for (let el of answers) {
let html = `<div class="name" id=${i}>${el}</div>`
document.querySelector('.answers').insertAdjacentHTML('beforeend', html)
i++
}
}
}
const q1 = new Question('What\'s the capitol of Rwanda?', ['A: Dodoma', 'B: Acra', 'C: Kigali'], 2);
const q2 = new Question('What\'s is the square root of 0?', ["A: Not possible", 'B: 0', 'C: 1'], 1);
const q3 = new Question('Who was Rome\'s first emperor?', ['A: Tiberius', 'B: Augustus', 'C: Marcus Aurelius'], 1);
const questions = [q1, q2, q3];
let runningQuestion;
let gamePlaying;
init()
document.querySelector('.button1').addEventListener('click', nextQuestion)
function nextQuestion(e) {
console.log(e.target)
if (gamePlaying === true && runningQuestion <= questions.length - 1) {
clearAnswers()
document.querySelector('.button1').textContent = 'Next Question'
runningQuestion++
questions[runningQuestion].displayAnswers()
}
if (runningQuestion >= questions.length - 1) {
document.querySelector('.button1').textContent = 'Try again!'
runningQuestion = 0
}
}
function clearAnswers() {
document.querySelectorAll('.name').forEach(el => {
el.remove()
})
}
document.querySelector('.button2').addEventListener('click', resetGame)
function resetGame() {
document.querySelector('.button1').textContent = 'Next Question'
clearAnswers()
runningQuestion = 0
questions[runningQuestion].displayAnswers()
}
function init() {
gamePlaying = true;
runningQuestion = 0;
questions[runningQuestion].displayAnswers()
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.container {
display: flex;
width: 400px;
height: auto;
margin: 100px auto;
align-items: center;
flex-direction: column;
}
.question {
margin-top: 40px;
color: rgb(102, 0, 0);
font-size: 1.4rem;
}
.answers {
display: flex;
flex-direction: column;
margin-top: 10px;
height: 100px;
margin-bottom: 15px;
}
.name {
margin-top: 20px;
cursor: pointer;
color: rgb(102, 0, 0);
font-size: 1.2rem;
}
.button1 {
margin-top: 50px;
border-style: none;
width: 350px;
height: 50px;
font-size: 1.4rem;
}
ul>li {
list-style-type: none;
margin-top: 10px;
font-size: 1.2rem;
color: rgb(102, 0, 0);
height: 30px;
cursor: pointer;
display: block;
}
.button2 {
margin-top: 20px;
border-style: none;
width: 350px;
height: 50px;
font-size: 1.4rem;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Quiz</title>
</head>
<body>
<div class="container">
<div class="question"></div>
<div class="answers"></div>
<button type="button" class="button1">Next Question</button>
<button type="button" class="button2">Reset</button>
</div>
<script src="app.js"></script>
</body>
</html>

The problem with the current version is that you reset runningQuestion to 0, and when clicking on the button, you execute nextQuestion, which, as the name implies, goes to the next question (runningQuestion++).
I see 2 ways of solving this. Either the "easy" way, by resetting runningQuestion to -1 so that it goes to 0:
class Question{constructor(e,s,t){this.question=e,this.answers=s,this.correct=t}displayAnswers(){document.querySelector(".question").innerHTML=`<div class="q1">${this.question}</div>`;let e=0,s=this.answers;for(let t of s){let s=`<div class="name" id=${e}>${t}</div>`;document.querySelector(".answers").insertAdjacentHTML("beforeend",s),e++}}}const q1=new Question("What's the capitol of Rwanda?",["A: Dodoma","B: Acra","C: Kigali"],2),q2=new Question("What's is the square root of 0?",["A: Not possible","B: 0","C: 1"],1),q3=new Question("Who was Rome's first emperor?",["A: Tiberius","B: Augustus","C: Marcus Aurelius"],1),questions=[q1,q2,q3];let runningQuestion,gamePlaying;init(),document.querySelector(".button1").addEventListener("click",nextQuestion);
/* Nothing changed above */
function nextQuestion(e) {
runningQuestion++; // <---------------------------------------------------------
if (gamePlaying === true && runningQuestion <= questions.length - 1) {
clearAnswers();
document.querySelector('.button1').textContent = 'Next Question';
questions[runningQuestion].displayAnswers();
}
if (runningQuestion >= questions.length - 1) {
document.querySelector('.button1').textContent = 'Try again!';
runningQuestion = -1; // <-----------------------------------------------------
}
}
/* Nothing changed below */
function clearAnswers(){document.querySelectorAll(".name").forEach(e=>{e.remove()})}function resetGame(){document.querySelector(".button1").textContent="Next Question",clearAnswers(),runningQuestion=0,questions[runningQuestion].displayAnswers()}function init(){gamePlaying=!0,runningQuestion=0,questions[runningQuestion].displayAnswers()}document.querySelector(".button2").addEventListener("click",resetGame);
/* Same CSS as yours */ *{box-sizing:border-box;margin:0;padding:0}.container{display:flex;width:400px;height:auto;margin:100px auto;align-items:center;flex-direction:column}.question{margin-top:40px;color:#600;font-size:1.4rem}.answers{display:flex;flex-direction:column;margin-top:10px;height:100px;margin-bottom:15px}.name{margin-top:20px;cursor:pointer;color:#600;font-size:1.2rem}.button1{margin-top:50px;border-style:none;width:350px;height:50px;font-size:1.4rem}ul>li{list-style-type:none;margin-top:10px;font-size:1.2rem;color:#600;height:30px;cursor:pointer;display:block}.button2{margin-top:20px;border-style:none;width:350px;height:50px;font-size:1.4rem}
<!-- Same HTML as yours --> <div class="container"> <div class="question"></div><div class="answers"></div><button type="button" class="button1">Next Question</button> <button type="button" class="button2">Reset</button></div>
or another way, which I find cleaner. A problem you can run into with your current code, is that if you have other things to keep track of, like a score, for example, you might forget to reset them as well, inside your nextQuestion function. And if you add other stuff, you'll need to reset them in multiple places in your code.
What I would do is simply reuse the resetGame function to reset everything:
class Question{constructor(e,s,t){this.question=e,this.answers=s,this.correct=t}displayAnswers(){document.querySelector(".question").innerHTML=`<div class="q1">${this.question}</div>`;let e=0,s=this.answers;for(let t of s){let s=`<div class="name" id=${e}>${t}</div>`;document.querySelector(".answers").insertAdjacentHTML("beforeend",s),e++}}}const q1=new Question("What's the capitol of Rwanda?",["A: Dodoma","B: Acra","C: Kigali"],2),q2=new Question("What's is the square root of 0?",["A: Not possible","B: 0","C: 1"],1),q3=new Question("Who was Rome's first emperor?",["A: Tiberius","B: Augustus","C: Marcus Aurelius"],1),questions=[q1,q2,q3];let runningQuestion,gamePlaying;
/* Nothing changed above */
const btn1 = document.querySelector('.button1');
init();
btn1.addEventListener("click", onButtonClick);
function isLastQuestion() { return runningQuestion >= questions.length - 1; }
function onButtonClick() {
if (gamePlaying === true && !isLastQuestion()) {
runningQuestion++;
displayQuestion();
} else {
resetGame();
}
}
function displayQuestion() {
clearAnswers();
btn1.textContent = isLastQuestion() ? 'Try again' : 'Next Question';
questions[runningQuestion].displayAnswers();
}
/* Nothing changed below */
function clearAnswers(){document.querySelectorAll(".name").forEach(e=>{e.remove()})}function resetGame(){document.querySelector(".button1").textContent="Next Question",clearAnswers(),runningQuestion=0,questions[runningQuestion].displayAnswers()}function init(){gamePlaying=!0,runningQuestion=0,questions[runningQuestion].displayAnswers()}document.querySelector(".button2").addEventListener("click",resetGame);function init(){gamePlaying=true;runningQuestion = 0;questions[runningQuestion].displayAnswers()}
/* Same CSS as yours */ *{box-sizing:border-box;margin:0;padding:0}.container{display:flex;width:400px;height:auto;margin:100px auto;align-items:center;flex-direction:column}.question{margin-top:40px;color:#600;font-size:1.4rem}.answers{display:flex;flex-direction:column;margin-top:10px;height:100px;margin-bottom:15px}.name{margin-top:20px;cursor:pointer;color:#600;font-size:1.2rem}.button1{margin-top:50px;border-style:none;width:350px;height:50px;font-size:1.4rem}ul>li{list-style-type:none;margin-top:10px;font-size:1.2rem;color:#600;height:30px;cursor:pointer;display:block}.button2{margin-top:20px;border-style:none;width:350px;height:50px;font-size:1.4rem}
<!-- Same HTML as yours --> <div class="container"> <div class="question"></div><div class="answers"></div><button type="button" class="button1">Next Question</button> <button type="button" class="button2">Reset</button></div>

Related

String many operations on calculator

I am stucked with the logic of one exercise from The Odin Project. I am actually working on a simple calculator and it's almost done (except for minor bugs I think) but I need to implement the last functionality and honestly I don't know where to start.
Basically the exercise says:
"Users should be able to string together several operations and get
the right answer, with each pair of numbers being evaluated at a time.
For example, 12 + 7 - 5 * 3 = should yield 42.
Your calculator should not evaluate more than a single pair of numbers
at a time. Example: you press a number button (12), followed by an
operator button (+), a second number button (7), and finally a second
operator button (-). Your calculator should then do the following:
first, evaluate the first pair of numbers (12 + 7), second, display
the result of that calculation (19), and finally, use that result (19)
as the first number in your new calculation, along with the next
operator (-)."
The thing is, I'm very lost and confused about this last step and when I try to operate like that on my calculator it simply does not work. It's like I have to priorize multiplying and dividing over adding and subtracting, right? Could anyone enlight me?
Here is the code:
const displayPrevResult = document.querySelector('.prev-result');
const displayCurrentResult = document.querySelector('.current-result');
const equal = document.querySelector('.equal');
const decimal = document.querySelector('.decimal');
const clear = document.querySelector('.clear');
const numberBtn = document.querySelectorAll('.number');
const operatorBtn = document.querySelectorAll('.operator');
let current = '';
let previous = '';
let opString = '';
numberBtn.forEach((button) => {
button.addEventListener('click', (e) => {
getNum(e.target.textContent);
})
})
operatorBtn.forEach((button) => {
button.addEventListener('click', (e) => {
getOp(e.target.textContent);
})
})
clear.addEventListener('click', clearCalc);
// Operate when clicking equal
equal.addEventListener('click', () => {
current = parseFloat(current);
previous = parseFloat(previous);
if (opString === '+') {
current = add(previous, current);
} else if (opString === '-') {
current = subtract(previous, current);
} else if (opString === 'x') {
current = multiply(previous, current);
} else if (opString === '÷') {
if (current === 0) {
clearCalc();
displayCurrentResult.textContent = 'ERROR';
return;
}
current = divide(previous, current);
}
displayCurrentResult.textContent = current;
})
function clearCalc() {
current = '';
previous = '';
displayCurrentResult.textContent = '0';
displayPrevResult.textContent = '';
}
// Store current number, get operator and display it
function getOp(opStr) {
opString = opStr;
previous = current;
displayPrevResult.textContent = previous;
current = '';
}
// Get the number and display it
function getNum(num) {
current += num;
displayCurrentResult.textContent = current;
}
// Operating functions
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
function operate(a, b) {
return divide(b, a);
}
console.log(operate(22, 4));
body {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.calcContainer {
background: linear-gradient(276deg, #40a179, #77cea9);
padding: 1em;
border-radius: 5px;
border: 1px solid #000;
}
button {
padding: 1em;
margin: 0.1em;
width: 40px;
background: #a2ffaf;
border: 1px solid #fff;
border-radius: 3px;
cursor: pointer;
}
button:hover {
background: #72e782;
}
.clr {
background: #87e4bd;
}
.clr:hover {
background: #53ad88;
}
.clear {
margin: 0em 0.1em 0.5em 0.5em;
padding: 0;
}
.output-clear-container {
display: flex;
}
.output {
flex-grow: 1;
height: 40px;
background: #c2fcca;
border-radius: 5px;
border: 1px solid #fff;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
padding-right: 0.5em;
margin-bottom: 0.5em;
}
.par {
margin-bottom: 0.3em;
}
.prev-result {
font-size: 14px;
padding-bottom: 0.3em;
color:#40a179;
}
.current-result {
font-size: 18px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="main.js" defer></script>
<title>Calculator</title>
</head>
<body>
<div class="calcContainer">
<div class="output-clear-container">
<div class="output">
<div class="prev-result"></div>
<div class="current-result">0</div>
</div>
<button class="clear">AC</button>
</div>
<div class="par">
<button class="number">7</button>
<button class="number">8</button>
<button class="number">9</button>
<button class="operator clr">÷</button>
</div>
<div class="par">
<button class="number">4</button>
<button class="number">5</button>
<button class="number">6</button>
<button class="operator clr">x</button>
</div>
<div class="par">
<button class="number">1</button>
<button class="number">2</button>
<button class="number">3</button>
<button class="operator clr">-</button>
</div>
<div class="par">
<button class="decimal clr">.</button>
<button class="number">0</button>
<button class="equal clr">=</button>
<button class="operator clr">+</button>
</div>
</div>
</body>
</html>
Thank you very much.

Background is not changing accordingly in html

I am remaking Wordle for a fun project to get my brain going. I have run into an issue though where squares are getting their background color changed when they are not supposed to.
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>replit</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
<script src="script.js"></script>
</body>
</html>
js:
var letter = 0
var id
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
var key = event.key
letter+=1
id = "l".concat(letter)
document.getElementById(id).innerHTML = key
guess.push(key)
event.preventDefault();
if(letter == 5){
for(i in word){
b=parseInt(i)+1-0
letter = word[i]
for(x in guess){
gulet = guess[x]
if(gulet==letter){
id = "l"+b
document.getElementById(id).style.background = "yellow"
}
}
}
}
}, true);
css:
html, body {
width: 100%;
height: 100%;
}
#element1 {display:inline-block;margin-right:10px;}
.letterBox {
display: inline-block;
text-align: center;
font-size: 40px;
height: 50px;
width: 50px;
background-color: #ffffff;
border: 2px solid black;
border-radius: 7px;
var letter = 0
var id
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
var key = event.key
letter+=1
id = "l".concat(letter)
document.getElementById(id).innerHTML = key
guess.push(key)
event.preventDefault();
if(letter == 5){
for(i in word){
b=parseInt(i)+1-0
letter = word[i]
for(x in guess){
gulet = guess[x]
if(gulet==letter){
id = "l"+b
document.getElementById(id).style.background = "yellow"
}
}
}
}
}, true);
html, body {
width: 100%;
height: 100%;
}
#element1 {display:inline-block;margin-right:10px;}
.letterBox {
display: inline-block;
text-align: center;
font-size: 40px;
height: 50px;
width: 50px;
background-color: #ffffff;
border: 2px solid black;
border-radius: 7px;
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
The constant 'word' is what the letters are being compared to.
Someone removed this part so I am adding it back. An example of a word that breaks it is 'halaa' and 'haala'
I researched this problem and I have not found anyone with this same problem, so I do not know where to even start.
There are quite some mistakes in your code, I'll try to address them one by one:
Watch out ids with leading numbers
No need for the letter variable, we can use guess.length for the same result
id = "l".concat(letter) can just ben 'l' + n' (but not needed)
b=parseInt(i)+1-0 can be: parseInt(i) + 1, since the - 0 doesn't do anything
if(gulet==letter){ compares an char vs a int, won't work as expected
Fixing the above, simplifying the code, gives us something like:
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", (event) => {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
event.preventDefault();
var key = event.key
var id = "l" + (guess.length + 1);
document.getElementById(id).innerHTML = key
guess.push(key)
if (guess.length == 5){
for (let i in guess){
if (guess[i] == word[i]){
id = 'l' + (+i + 1)
document.getElementById(id).style.background = "yellow" ;
}
}
}
}, true);
html, body { width: 100%; height: 100%; }
.letterBox { display: inline-block; text-align: center; font-size: 40px; height: 50px; width: 50px; background-color: #ffffff; border: 2px solid black; border-radius: 7px; }
#element1 {display:inline-block;margin-right:10px;}
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
I changed this code snippet for you & I hope it works
if(letter === 5){
let idx = 0;
for(let i in word){
if (word[i] === guess[i]) {
document.getElementById(`l${idx}`).style.background = "yellow";
}
idx++;
}
}

Are these unexpected effects in my TicTacToe just Javascript timing aspects I'm unaware of?

I made a TicTacToe game that happily works. I'm trying to solve two things though.
The opponent's move in "DumbAI" shows immediately after I choose mine. When I impose a setTimeout(), and the AI opponent wins, the endgame sequence does not fire. It works when I win though.
The endgame sequence is that when anyone gets 3 in a row, an alert is supposed to flash, the 3 squares that won are highlighted and the eventlistener is removed so no more marks can be made.
Instead, the code lets me swap to the active player. And if the active player gets 3 in a row, the endgame sequence fires.
All these functions are in the same block. By putting a setTimeout() on the opponent's move, is it skipping over the endgame sequence?
Similarly, when I break out the endgame sequence into a separate block, another issue occurs.
When I take the endgame sequence out of the block and I win, the code will flash the alert and highlight the spaces, but it will also allow the AI opponent to make an extra move.
By taking the endgame sequence out of the block, is the computer moving too quickly through the code by allowing opponent to take his turn before firing the endgame sequence?
script.js:
var ONE_CLASS
var TWO_CLASS
const btn = document.querySelector('#PlayerOneSymbol');
btn.onclick = function () {
const XOs = document.querySelectorAll('input[name="choice"]');
for (const XO of XOs) {
if (XO.checked) {
ONE_CLASS = XO.value
TWO_CLASS = XO.value == 'X' ? 'O' : 'X'
break;
}
}
alert("First Move Belongs to " + ONE_CLASS + ". Select Player Two.");
};
var playerTwoIdentity
const btn2 = document.querySelector('#PlayerTwoChoice');
btn2.onclick = function () {
const Opponents = document.querySelectorAll('input[name="choice2"]');
for (const Opponent of Opponents) {
if (Opponent.checked) {
playerTwoIdentity = Opponent.value
break;
}
}
alert("Your Opponent is " + playerTwoIdentity + ". Start New Game.")
};
let playerOneTurn
function swapTurns() {
playerOneTurn = !playerOneTurn
};
const winningTrios = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[6, 4, 2]
]
restartBtn.addEventListener('click', startGame);
function startGame() {
if (ONE_CLASS == undefined || playerTwoIdentity == undefined) {return alert ("Make sure players are defined")}
console.log("player 1 = " + ONE_CLASS + ", player 2 = " + playerTwoIdentity)
drawBoard();
playerOneTurn = true;
}
const arrayfromBoxes = Array.from(document.getElementsByClassName('box'));
const stylingOfBoxes = document.querySelectorAll('.box');
function drawBoard() {
console.log(stylingOfBoxes)
for (let i = 0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].addEventListener('click', boxmarked, {once: true});}
stylingOfBoxes.forEach(gridBox => {
gridBox.classList.remove(ONE_CLASS)
gridBox.classList.remove(TWO_CLASS)
gridBox.classList.remove('winner')
gridBox.innerHTML = ""
})
}
function boxmarked(e) {
const index = arrayfromBoxes.indexOf(e.target)
// how to consolidate? maybe I just let ONE_CLASS mark and then if the AI or player
// or do it even earlier and link it with playerTurn?
if(playerOneTurn) {
arrayfromBoxes[index].classList.add(ONE_CLASS)
e.target.innerHTML = ONE_CLASS
} else {
arrayfromBoxes[index].classList.add(TWO_CLASS)
e.target.innerHTML = TWO_CLASS
}
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
// eliminate repetition -
if(playerTwoIdentity === "Dumb AI") {
var dumbAIArray = arrayfromBoxes.reduce((dumbAIArray, box, idx) => {
if (box.innerHTML === "") {
dumbAIArray.push(idx);
}
return dumbAIArray;
}, []);
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
// why does Timeoutfunction prevent opponent sequence?
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)}, 500);
// setTimeout(() => {arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS}, 500);
// if (playerhasWon()) {
// declareWinner()
// return
// }
// if (emptySpaceRemains() == false) {
// declareTie()
// return
// }
hasGameEnded()
swapTurns()
} else { console.log("Human")
}
}
function hasGameEnded() {
// fix declareWinner() appears before the added classes bc alert happens quicker than redraw
// I also cannot pull these out because then the opponent move fires and shows
// could have something to do with timing of in-block code
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
}
function checkClass() {
if(playerOneTurn) {
return ONE_CLASS
} else {
return TWO_CLASS
};}
function emptySpaceRemains() {
var innerHTMLempty = (insidebox) => insidebox.innerHTML===""
console.log(arrayfromBoxes.some(innerHTMLempty))
return (arrayfromBoxes.some(innerHTMLempty))
}
function declareTie() {
setTimeout(alert ("TIE GAME"), 1000)}
function playerhasWon() {
var indexOfSelected = arrayfromBoxes.reduce((indexOfSelected, box, idx) => {
if (box.classList[1] === checkClass()) {
indexOfSelected.push(idx);
}
return indexOfSelected;
}, []);
const winningThreeIndexes = winningTrios
.map(trio => trio.filter(i => indexOfSelected.includes(i)))
.filter(i => i.length === 3);
console.log(winningThreeIndexes)
console.log(winningThreeIndexes.length)
if (winningThreeIndexes.length === 1) {winningThreeIndexes[0].map((index) => {arrayfromBoxes[index].className += ' winner'})}
var isThereAWinner =
winningTrios.some(trio => {return trio.every(i => indexOfSelected.includes(i))});
console.log({isThereAWinner});
return isThereAWinner
}
function declareWinner() {
setTimeout(alert (checkClass() + " WINS"), 1000);
for (let i=0; i < stylingOfBoxes.length; i++) {
stylingOfBoxes[i].removeEventListener('click', boxmarked, {once: true});}
}
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tic Tac Toe</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1 id="playtext">Let's Play</h1>
<div class="radioContainer">
<div id="playerOne">
<h3>Player One</h3>
<form>
<input type="radio" name="choice" value="X"> X<br>
<input type="radio" name="choice" value="O"> O<br>
<input type="button" id="PlayerOneSymbol" value="Confirm">
</form>
</div>
<div id="playerTwo">
<h3>Player Two</h3>
<form>
<input type="radio" name="choice2" value="Human"> Human<br>
<input type="radio" name="choice2" value="Dumb AI"> Dumb AI<br>
<input type="radio" name="choice2" value="Smart AI"> Smart AI<br>
<input type="button" id="PlayerTwoChoice" value="Confirm">
</form>
</div>
</div>
<div class="buttonHolder">
<div class="buttonWrapper">
<button id="restartBtn">Start New Game</button>
</div>
</div>
<div class="gameboard">
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
<div class="box" ></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
style.css:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
}
#playtext {
text-align: center;
padding: 10px;
}
.buttonHolder {
height: 60px;
width: 100%;
float: left;
position: relative;
background-color: purple;
}
.buttonWrapper {
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.container {
background-color: purple;
justify-content: center;
/* display: flex;
flex-wrap: wrap; */
width: 400px;
height: 600px;
}
#gameboard {
border-top:10px;
border-bottom: 4px;
border-bottom-color: black;
background-color: chartreuse;
}
.box {
background-color: yellow;
width: 125px;
height: 125px;
float: left;
width: 33.33%;
}
button:hover {
cursor: pointer;
transform: translateY(-2px);
}
.winner {
background-color: black;
}
.X {
content: 'X';
font-size: 135px;
}
.O {
content: 'O';
font-size: 135px;
}
#spacer {
height: 10px;
width: 100%;
background-color: purple;
padding: 10px;
}
#playerOne {
background-color: blanchedalmond;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
#playerTwo {
background-color: mintcream;
padding: 5px;
height: 110px;
float: left;
width: 50%;
}
A friend helped me figure out what's happening in #1 -- I think. He points out that JS is asynchronous. I had three functions:
Opponent puts down marker in a space.
Board is evaluated to see if opponent won.
If not, it switches turns and lets player pick a space.
If so, it ends the game and prevents picking a space
I was hoping when (1) was delayed, (2) and (3) wouldn't fire until (1) did.
But in reality (1) is delayed, so (2) runs anyway and doesn't see the opponent win and so (3) lets player pick a space.
So to fix this, I put the timeout on all 3 functions:
setTimeout(() => {
let dumbAIpicked = dumbAIArray[Math.floor(dumbAIArray.length * (Math.random()))]
arrayfromBoxes[dumbAIpicked].classList.add(TWO_CLASS)
arrayfromBoxes[dumbAIpicked].innerHTML = TWO_CLASS
if (playerhasWon()) {
declareWinner()
return
}
if (emptySpaceRemains() == false) {
declareTie()
return
}
// hasGameEnded()
swapTurns()
``}, 500);

How do I use a for loop to manage increments of an object property in JavaScript?

I’m doing a rockPaperScissors project. Here’s the published link: https://george-swift.github.io/rockPaperScissors/. I want to add a first to 5 feature to put an end score to the game. If either the player or computer reaches 5, I'll have a message declare a winner (I know how to manipulate the message). Attached is a snippet for clarity.
//computer will randomly pick from this array
const RPS = ['rock','paper','scissors']
//object defines the rules of the game for each player selection
const rulesRPS = {
rock: {
beats: "scissors"
},
paper: {
beats: "rock"
},
scissors: {
beats: "paper"
}
};
//cached elements for scores
const pScore = document.getElementById("player-score");
const tScore = document.getElementById("tie-score");
const cScore = document.getElementById("computer-score");
//declared variables for helper functions
let winner, results, scores;
//for event listeners
document.getElementById("rock-btn").addEventListener('click', pSelectedR);
document.getElementById("paper-btn").addEventListener("click", pSelectedP);
document.getElementById("scissors-btn").addEventListener("click", pSelectedS);
// updates the scores after each game round
function render() {
pScore.innerHTML = scores.player;
tScore.innerHTML = scores.tie;
cScore.innerHTML = scores.computer;
}
function pickRandom () {
const selection = RPS[Math.floor(Math.random() * 3)];
return selection;
}
//handle results after player selection
function manageResults () {
results.computer = pickRandom();
winner = findWinner();
scores[winner]++;
render();
}
//player selections and subsequent action
function pSelectedR () {
results.player = "rock";
manageResults();
}
function pSelectedP () {
results.player = "paper";
manageResults();
}
function pSelectedS () {
results.player = "scissors";
manageResults();
}
//compare selections to find winner
function findWinner() {
results.computer = pickRandom();
if (results.player === results.computer) {
return 'tie';
}
if (rulesRPS[results.player].beats === results.computer) {
return 'player';
}
return 'computer';
}
//instantiates the state of the game
function init() {
scores = {
player: 0,
tie: 0,
computer: 0,
};
results = {
player: 'rock',
computer: 'rock'
};
winner = null;
}
init();
* {
box-sizing: border-box;
}
body {
font-family: 'Judson', serif;
text-align: center;
height: 100vh;
margin: 40px 0px;
}
h1, h2 {
font-family: 'Archivo Black', sans-serif;
}
body,
main,
aside {
display: flex;
justify-content: space-evenly;
align-items: center;
}
body,
main {
flex-direction: column;
}
table {
width: 100%;
}
td {
width: 33.3%;
font-size: 2.5rem;
}
img {
width: 50vh;
height: 50vh;
}
aside {
width: 75vw;
padding: 0.5em 0 0 0;
}
button {
width: 30vw;
margin: 2vw;
background-color: rgb(38,39,39);
color: whitesmoke;
border: rgb(113, 125, 126) solid 2px;
border-radius: 3px;
padding: 5px;
cursor: pointer;
}
button:hover {
background-color: rgb(112, 123, 124);
color: black;
transition: 0.5s;
}
button,
#link {
font-size: 20px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rock Paper Scissors</title>
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css?family=Archivo+Black|Judson:400,700" rel="stylesheet">
<script defer src="main.js"></script>
</head>
<body>
<header>
<h1>ROCK PAPER SCISSORS- FOR PASTIME</h1>
</header>
<main>
<table>
<caption>
<h2>Scores</h2>
<p>Best of 5?</p>
</caption>
<tbody>
<tr>
<th>Player</th>
<th>Tie</th>
<th>Computer</th>
</tr>
<tr>
<td id="player-score">0</td>
<td id="tie-score">0</td>
<td id="computer-score">0</td>
</tr>
</tbody>
</table>
<figure>
<img src="https://res.cloudinary.com/george-swift/image/upload/v1603381143/9d966b99b233a6768b6981b83e43e0f0_hobh0x.jpg"
alt="rock paper scissors illustrated"/>
</figure>
</main>
<aside>
<button id="rock-btn">Rock</button>
<button id="paper-btn">Paper</button>
<button id="scissors-btn">Scissors</button>
</aside>
<nav>
GitHub
</nav>
</body>
</html>
From what I see, the incrementing is working as expected. You only need to put in a condition to check for 5 after the winner score has been incremented.
And then add the check to your current code.
function manageResults () {
results.computer = pickRandom();
winner = findWinner();
scores[winner]++;
render();
if (scores[winner] === 5) {
renderEndGame();
init(); // To reset the scores to 0 for a new game.
}
}
Then you can add the extra function to the js file:
function renderEndGame () {
// Display end game message and score
// maybe an alert message
}

Animations not playing on load or on click in Javascript

Working on a tip calculator with an animation on an h1 tag and a slideDown and slideUp on click on the h2 tags. Problem is, none of the animations are playing and the click event isn't working either.
Here is the HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tip Calculator</title>
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="stylesheet" href="midtermcss.css">
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<script src="animationJS.js"></script>
</head>
<body>
<section id="faqs">
<h1>Tip facts</h1>
<h2>Things to know before you tip</h2>
<div>
<p>Tips Account for 44 Billion dollars of the Food Industry</p>
<p>7 States require servers to be paid minimum wage like everyone else</p>
<ul>
<li>Minnessota</li>
<li>Montana</li>
<li>Washington</li>
<li>Oregon</li>
<li>California</li>
<li>Nevada</li>
<li>Alaska</li>
</ul>
<p>Current Federal minimum tipped wage is $2.13 per hour can you live on that?</p>
<p>Charging with Credit/Debit cards tends to reduce the average tip</p>
</div>
</section>
<section id="js">
<h1 id="heading">Tip Calculator</h1>
<label for="billAmount">Total Amount Of Bill:</label>
<input type="text" id="billAmount"><br>
<label for="percentTip">Percent To Tip:</label>
<input type="text" id="percentTip"><br>
<label for="amountPeople">How Many People?:</label>
<input type="text" id="amountPeople"><br>
<label for="totalTip">Tip Total:</label>
<input type="text" id="totalTip"><br>
<label> </label>
<input type="button" id="calculate" value="Calculate"><br>
</section>
</body>
</html>
Here is the JS file.
$(document).ready(function() {
// runs when an h2 heading is clicked
$("#faqs h2").toggle(
function() {
$(this).toggleClass("minus");
$(this).next().slideDown(1000, "easeOutBounce");
},
function() {
$(this).toggleClass("minus");
$(this).next().slideUp(1000, "easeInBounce");
}
);
$("#faqs h1").animate({
fontSize: "400%",
opacity: 1,
left: "+=375"
}, 1000, "easeInExpo")
.animate({
fontSize: "175%",
left: "-=200"
}, 1000, "easeOutExpo");
$("#faqs h1").click(function() {
$(this).animate({
fontSize: "400%",
opacity: 1,
left: "+=375"
}, 2000, "easeInExpo")
.animate({
fontSize: "175%",
left: 0
}, 1000, "easeOutExpo");
});
});
var $ = function(id) {
return document.getElementById(id);
}
var calculateClick = function() {
var billAmount = parseFloat($("billAmount").value);
var percentTip = parseFloat($("percentTip").value);
var amountPeople = parseInt($("amountPeople").value);
if (isNaN(billAmount) || billAmount <= 0) {
alert("Your bill can't be 0 or less.");
} else if (isNaN(percentTip) || percentTip <= 0) {
alert("The percentage should be a whole number.");
} else if (isNaN(amountPeople) || amountPeople <= 0) {
alert("You are 1 person never count yourself as less.");
} else {
var total = billAmount * (percentTip / 100) / amountPeople;
$("totalTip").value = total.toFixed(2);
}
}
window.onload = function() {
$("calculate").onclick = calculateClick;
$("billAmount").focus();
}
Last but not least the CSS file since the open and minus classes are listed in there
* {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
background-color: white;
margin: 0 auto;
width: 500px;
border: 3px solid blue;
}
section {
padding: 0 1em .5em;
}
section.js {
padding: 0 1em .5em;
}
h1 {
text-align: center;
margin: .5em 0;
}
label {
float: left;
width: 10em;
text-align: right;
}
input {
margin-left: 1em;
margin-bottom: .5em;
}
#faqs h1 {
position: relative;
left: -168px;
font-size: 125%;
color: blue;
}
h2 {
font-size: 120%;
padding: .25em 0 .25em 25px;
cursor: pointer;
background: url(images/plus.png) no-repeat left center;
}
h2.minus {
background: url(images/minus.png) no-repeat left center;
}
div.open {
display: block;
}
ul {
padding-left: 45px;
}
li {
padding-bottom: .25em;
}
p {
padding-bottom: .25em;
padding-left: 25px;
}
I can't figure out for the life of me why the animations work in a separate test file but when I use them now in my tip calculator they don't. I'm using Murach's Javascript and Jquery book but this section has been terribly hard to understand.
Your issue is that you include jQuery but later on in the global scope you redefine the $:
var $ = function(id) {
return document.getElementById(id);
}
Fiddle: http://jsfiddle.net/AtheistP3ace/u0von3g7/
All I did was change the variable name holding that function and replace it in the areas you were using it. Specifically:
var getById = function(id) {
return document.getElementById(id);
}
var calculateClick = function() {
var billAmount = parseFloat(getById("billAmount").value);
var percentTip = parseFloat(getById("percentTip").value);
var amountPeople = parseInt(getById("amountPeople").value);
if (isNaN(billAmount) || billAmount <= 0) {
alert("Your bill can't be 0 or less.");
} else if (isNaN(percentTip) || percentTip <= 0) {
alert("The percentage should be a whole number.");
} else if (isNaN(amountPeople) || amountPeople <= 0) {
alert("You are 1 person never count yourself as less.");
} else {
var total = billAmount * (percentTip / 100) / amountPeople;
getById("totalTip").value = total.toFixed(2);
}
}
window.onload = function() {
getById("calculate").onclick = calculateClick;
getById("billAmount").focus();
}
$ is just shorthand for jQuery. When you include jQuery it creates two functions for you that both do the same thing. jQuery and $. If you set $ equal to something else you have effectively overwritten jQuery library included in your page and it will no longer operate as you would expect. All jQuery functionality begins with using $ or jQuery function. Once that returns a jQuery object to you, you can begin chaining and calling functions off those objects but to get a jQuery object you need to use the jQuery or $ function.
You mentioned in a comment above your teacher had you do that to fix something. I imagine it was because jQuery was not initially included so he just created the $ selector function to get you moving but I would hope he explained why he did that and how it can affect things later.

Categories