String many operations on calculator - javascript

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.

Related

How to select and manipulate the dynamically created html element with javascript?

I am pretty new to js, and I am building a color scheme generator as a solo project.
I am now stuck on select the html element that created from dynamically.
I tried to select both label and input element below, using document.getElementByClassName but it gives me 'undefined'
I wanna select both label and input elements and add an click eventListner so that they can copy the result color code from that elements.
<label for='${resultColor}' class='copy-label'>Click to copy!</label>
<input class='result-code' id='${resultColor}' type="text" value='${resultColor}'/>`
const colorPickerModes = [ 'monochrome', 'monochrome-dark', 'monochrome-light', 'analogic', 'complement', 'analogic-complement', 'triad quad']
const colorPickerForm = document.getElementById("colorPick-form");
const colorPickerInput = document.getElementById("colorPicker");
const colorPickerModeDropDown = document.getElementById("colorPick-mode");
const resultColorDiv = document.getElementById("result-color-div");
const resultColorCodeDiv = document.getElementById("result-code-div");
let colorPicked = "";
let modePicked = "";
let resultColorDivHtml =''
let resultCodeDivHtml=''
let colorSchemeSetStrings = [];
let resultColorSchemeSet = [];
fetchToRender()
renderDropDownList();
//listen when user change the color input and save that data in global variable
colorPickerInput.addEventListener(
"change",
(event) => {
//to remove # from the color hex code data we got from the user
colorPicked = event.target.value.slice(1, 7);
},
false
);
//listen when user change the scheme mode dropdownlist value and save that data in global variable
colorPickerModeDropDown.addEventListener('change', (event)=>{
modePicked =
colorPickerModeDropDown.options[colorPickerModeDropDown.selectedIndex].text;
})
//whe user click submit btn get data from user's input
colorPickerForm.addEventListener("submit", (event) => {
event.preventDefault();
// To get options in dropdown list
modePicked =
colorPickerModeDropDown.options[colorPickerModeDropDown.selectedIndex].text;
fetchToRender()
});
//when first load, and when user request a new set of color scheme
function fetchToRender(){
if (!colorPicked) {
//initialize the color and mode value if user is not selected anything
colorPicked = colorPickerInput.value.slice(1, 7);
modePicked = colorPickerModes[0]
}
fetch(
`https://www.thecolorapi.com/scheme?hex=${colorPicked}&mode=${modePicked}`
)
.then((res) => res.json())
.then((data) => {
let colorSchemeSetArray = data.colors;
for (let i = 0; i < 5; i++) {
colorSchemeSetStrings.push(colorSchemeSetArray[i]);
}
// to store each object's hex value
for (let i = 0; i < colorSchemeSetStrings.length; i++) {
resultColorSchemeSet.push(colorSchemeSetStrings[i].hex.value);
}
renderColor();
colorSchemeSetStrings = []
resultColorSchemeSet = [];
});
}
function renderColor(){
//to store result of color scheme set object
resultColorDivHtml = resultColorSchemeSet.map((resultColorItem) => {
return `<div class="result-color"
style="background-color: ${resultColorItem};"></div>`;
}).join('')
resultCodeDivHtml = resultColorSchemeSet
.map((resultColor) => {
return `
<label for='${resultColor}' class='copy-label'>
Click to copy!</label>
<input class='result-code' id='${resultColor}'
type="text" value='${resultColor}'/>`;
})
.join("");
resultColorDiv.innerHTML = resultColorDivHtml;
resultColorCodeDiv.innerHTML = resultCodeDivHtml;
}
function renderDropDownList() {
const colorPickerModeOptionsHtml = colorPickerModes
.map((colorPickerMode) => {
return `<option class='colorSchemeOptions' value="#">${colorPickerMode}</option>`;
})
.join("");
colorPickerModeDropDown.innerHTML = colorPickerModeOptionsHtml;
}
* {
box-sizing: border-box;
}
body {
font-size: 1.1rem;
font-family: "Ubuntu", sans-serif;
text-align: center;
margin: 0;
}
/*------Layout------*/
#container {
margin: 0 auto;
width: 80%;
}
#form-div {
width: 100%;
height:10vh;
margin: 0 auto;
}
#colorPick-form {
display: flex;
width: 100%;
height:6vh;
justify-content: space-between;
}
#colorPick-form > * {
margin: 1rem;
height: inherit;
border: 1px lightgray solid;
font-family: "Ubuntu", sans-serif;
}
#colorPick-form > #colorPicker {
width: 14%;
height: inherit;
}
#colorPick-form > #colorPick-mode {
width: 45%;
padding-left: 0.5rem;
}
#colorPick-form > #btn-getNewScheme {
width: 26%;
}
#main {
display: flex;
flex-direction:column;
width:100%;
margin: .8em auto 0;
height: 75vh;
border:lightgray 1px solid;
}
#result-color-div {
width:100%;
height:90%;
display:flex;
}
#result-color-div > *{
width:calc(100%/5);
}
#result-code-div {
width:100%;
height:10%;
display:flex;
}
.copy-label{
width:10%;
display:flex;
padding:0.5em;
font-size:0.8rem;
align-items: center;
cursor: pointer;
background-color: #4CAF50;
color: white;
}
#result-code-div .result-code{
width:calc(90%/5);
text-align: center;
border:none;
cursor: pointer;
}
.result-code:hover, .result-code:focus, .copy-label:hover, .copy-label:focus{
font-weight:700;
}
/*------Button------*/
#btn-getNewScheme {
background-image: linear-gradient(
to right,
#614385 0%,
#516395 51%,
#614385 100%
);
}
#btn-getNewScheme {
padding:0.8rem 1.5rem;
transition: 0.5s;
font-weight: 700;
background-size: 200% auto;
color: white;
box-shadow: 0 0 20px #eee;
border-radius: 5px;
display: block;
cursor: pointer;
}
#btn-getNewScheme:hover,
#btn-getNewScheme:focus {
background-position: right center; /* change the direction of the change here */
color: #fff;
text-decoration: none;
}
}
<!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="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght#300;400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="index.css">
<title>Color Scheme Generator</title>
</head>
<body>
<div id="container">
<div>
<header><h1 class="site-title">🦎 Color Scheme Generator 🦎</h1></header>
</div>
<div id="form-div">
<form id="colorPick-form" method="get" >
<input id="colorPicker" type="color" />
<select name="colorPick-mode" id="colorPick-mode">
</select>
<button type='submit' id="btn-getNewScheme">Get Color Scheme</button>
</form>
</div>
<div id="main">
<div id="result-color-div">
</div>
<div id="result-code-div">
</div>
</div>
<script src="index.js" type="module"></script>
</body>
</html>
I think the problem is rendering timing. So you need to add event listener below the code where set innerHTML.
function renderColor() {
// to store result of color scheme set object
resultColorDivHtml = resultColorSchemeSet
.map((resultColorItem) => {
return `<div class="result-color" style="background-color: ${resultColorItem};"></div>`;
})
.join("");
resultCodeDivHtml = resultColorSchemeSet
.map((resultColor) => {
return `
<label for='${resultColor}' class='copy-label'>Click to copy!</label>
<input class='result-code' id='${resultColor}' type="text" value='${resultColor}'/>
`;
})
.join("");
resultColorDiv.innerHTML = resultColorDivHtml;
resultColorCodeDiv.innerHTML = resultCodeDivHtml;
// here! add event listener
const labels = document.getElementsByClassName("result-code");
Object.entries(labels).forEach(([key, label]) => {
label.addEventListener("click", (event) =>
alert(`copy color: ${event.target.value}`)
);
});
}
const resultColorCodeDiv=document.getElementById("resultColorCodeDiv")
const resultColorDiv=document.getElementById("resultColorDiv")
resultColorSchemeSet=[
{color:"red", code: "#ff0000"},
{color:"green", code: "#00ff00"},
{color:"blue", code: "#0000ff"}]
function renderColor(){
//to store result of color scheme set object
resultColorDivHtml = resultColorSchemeSet.map((resultColorItem) => {
return `<div class="result-color" style="background-color: ${resultColorItem.color};"></div>`
}).join('')
resultCodeDivHtml = resultColorSchemeSet
.map((resultColor) => {
return `
<label for='${resultColor.code}' class='copy-label'>Click to copy!</label>
<input class='result-code' id='${resultColor.code}' type="text" value='${resultColor.code}'/>`
})
.join("")
resultColorDiv.innerHTML = resultColorDivHtml
resultColorCodeDiv.innerHTML = resultCodeDivHtml
addListener(document.querySelectorAll(".result-color"))
addListener(document.querySelectorAll(".result-code"))
}
renderColor()
function addListener(elements){
for(const element of elements){
element.addEventListener("click" , ()=>{
// add copy logic here
console.log("hello")
})
}
}
<body>
<div id="resultColorDiv"></div>
<div id="resultColorCodeDiv"></div>
</body>

my textcontent get erased with each click, how do i keep each clicks value?

I am little confused on the next step. I am building a calculator, so far I can click each button and each button changes the text content to the desired number. However, it's not storing each number, it just replaces it. Which makes sense but I don't know how to make it store the number. By store I mean display like a normal calculator.
I've had some ideas but again not sure how to implement even with the documentation.
ideas:
for each
for loop
I'd really appreciate some guidance on this, please don't re-write my code as this will not help me, i'm still a beginner using vanilla JS. Ideally I would love something with documentation I can read.
html
<!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">
<title>Calculator</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital#1&display=swap" rel="stylesheet">
</head>
<body>
<div class="heading">
<h1>Calculator!!!!</h1>
</div>
<div class="value-store">
<span id= "number-input" class="starting-point">0</span>
<span id= "increment" class="increment-point"></span>
</div>
<div id="numbers" class="inputs">
<button id ="seven">7</button>
<button id="eight">8</button>
<button id="nine">9</button>
<button id="multiply">x</button>
<button id="four">4</button>
<button id ="five">5</button>
<button id="six">6</button>
<button id="minus">-</button>
<button id="one">1</button>
<button id="two">2</button>
<button id ="three">3</button>
<button id="add">+</button>
<button id="zero">0</button>
<button id="divide">/</button>
<button id="equals">=</button>
<button id="clear">AC</button>
<button id="del">DEL</button>
</div>
<script src="script.js"></script>
</body>
</html>
css
body {
font-size: medium;
font-family: 'Montserrat', sans-serif;
justify-content: center;
background-color: bisque;
}
h1 {
font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
display: flex;
justify-content: center;
padding-top: 100px;
padding-bottom: 100px;
font-family: 'Montserrat', sans-serif;
font-weight: bold;
color:darkgreen;
}
.value-store {
display: flex;
justify-content: center;
border: solid 3px;
padding-top: 20px;
font-family: 'Montserrat', sans-serif;
font-size: x-large;
font-weight: bold;
background-color: black;
color: white;
width: 500px;
}
.starting-point {
display: flex;
justify-items: right;
}
.inputs {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
justify-content: center;
padding-top: 10px;
border: solid 3px;
width: 500px;
}
button {
font-family: 'Montserrat', sans-serif;
font-weight: bold;
padding: 10px;
border-radius: 50%;
background-color: white;
}
javascript
//display
const numbersValue = document.getElementById("numbers");
const operators = document.getElementById("operator")
//let inputValue_span = document.getElementById("user");
//buttons with numbers
const buttonZero = document.getElementById("zero");
const buttonOne = document.getElementById("one");
const buttonTwo = document.getElementById("two");
const buttonThree = document.getElementById("three");
const buttonFour = document.getElementById("four");
const buttonFive = document.getElementById("five");
const buttonSix = document.getElementById("six");
const buttonSeven = document.getElementById("seven");
const buttonEight = document.getElementById("eight");
const buttonNine = document.getElementById("nine");
//buttons with operators
const plus = document.getElementById("add");
const minus = document.getElementById("minus");
const divide = document.getElementById("divide");
const equals = document.getElementById("equals");
const multiply = document.getElementById("multiply");
//numbers
numbersValue.addEventListener("click", function () {
buttonZero.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 0;}
buttonOne.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 1;}
buttonTwo.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 2;}
buttonThree.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 3;}
buttonFour.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 4;}
buttonFive.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 5;}
buttonSix.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 6;}
buttonSeven.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 7;}
buttonEight.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 8;}
buttonNine.onclick = function changeNumber() {
document.getElementById("number-input").textContent = 9;}
})
//operators
numbers.addEventListener("click", function () {
plus.onclick = function changeNumber() {
document.getElementById("number-input").innerHTML = "+";}
minus.onclick = function changeNumber() {
document.getElementById("number-input").innerHTML = "-";}
divide.onclick = function changeNumber() {
document.getElementById("number-input").innerHTML = "/";}
equals.onclick = function changeNumber() {
document.getElementById("number-input").innerHTML = "=";}
multiply.onclick = function changeNumber() {
document.getElementById("number-input").innerHTML = "x";}
})
//delete all
numbers.addEventListener("click", function () {
clear.onclick = function eraseAll () {
document.getElementById("number-input").innerHTML =0;}
})
//delete one at a time
// show each number pressed
in fact when we click on a button in calculator we don't replace the number
you must do real world act
we multiple last number by 10 and add new number to that
so
when you click 4 -> lastnumber is 0 ->0*10+4 = 4
after that click 4 again -> lastnumber is 4 -> 4*10+4 = 44
and ....
or append by string ability -> 4 append to 4 = 44
you can call a function like this :
buttonFour.onclick = function changeNumber() {
document.getElementById("number-input").textContent = getTextValue(4);}
and this function is :
getTextValue=(data)=>{
return document.getElementById("number-input").textContent*10+data;
}

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);

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

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>

Javascript -DOM-calculator display digits on a screen

Im implementing a calculator, and I'm stuck trying to display the digit on the screen. I iterate trough all my digit to get them, but when I try to replace them in order to display them in my div with the id #nums it won't work. this is the function i'm stuck with
buttons.forEach(button => {
button.addEventListener('click', function(){
console.log('it work')
document.querySelector('#nums').textContent = buttons.innerHTML
})
})
here is a fiddle to see more
function add(a, b) {
return a + b
}
function substract(a, b) {
return a - b
}
function sum(arr) {
result = 0;
for (var i = 0; i < arr.length; i++) {
result += arr[i]
}
return result
}
/*
function multiply_range(arr){
result = 1;
for(var i = 0; i < arr.length; i++){
result *= arr[i]
}
return result
}
*/
function multiply(a, b) {
return a * b
}
function divide(a, b) {
return a / b
}
var sum = document.getElementById('sum');
var substract = document.getElementById('minus')
var multiply = document.getElementById('multiply')
var divide = document.getElementById('divide')
function operate(operator, a, b) {
if (operator === sum) {
return add(a, b);
} else if (operator === substract) {
return substract(a, b);
} else if (operator === multiply) {
return multiply(a, b);
} else if (operator === divide) {
return divide(a, b);
}
}
operate(sum, 1, 1);
var display_value = document.querySelector('#nums');
const buttons = document.querySelectorAll('.number-btn')
// loop through all the buttons
// Object.keys(buttons) transform my object in a array
/*
Object.keys(buttons).forEach(button => {
button.addEventListener('click', function(){
console.log('it work')
})
})
*/
buttons.forEach(button => {
button.addEventListener('click', function() {
console.log('it work')
document.querySelector('#nums').textContent = buttons.innerHTML
})
})
/*
var btn_1 = document.querySelector('#btn-1')
btn_1.addEventListener('click', function(){
console.log('it work')
document.querySelector('#nums').textContent = btn_1.textContent
})
*/
/*
document.querySelector('#nums').textContent = 0;
*/
/*
document.getElementsByClassName('number-btn').addEventListener('click', function(){
display_value == document.queryselector('nums');
})
*/
body {
background-color: black;
}
.container {
display: grid;
grid-template-columns: auto auto auto auto;
grid-gap: 10px;
padding: 10px;
width: 85%;
height: 300px;
margin: 0 auto;
background-color: #cc1515;
}
#btn-equals {
grid-row-start: 2;
grid-column-start: 4;
grid-row-end: 6;
grid-column-end: 4;
}
.number-btn {
border: 0.5px solid black;
background-color: white;
font-size: 30px;
}
.operator-btn {
border: 0.5px solid black;
background-color: black;
color: white;
font-size: 30px;
}
.results {
margin: 0 auto;
width: 90%;
height: 50px;
background-color: white;
}
.contour {
background-color: lightblue;
position: absolute;
top: 30%;
left: 35%;
width: 400px;
margin: auto;
vertical-align: middle;
}
#nums {
font-size: 40px;
text-align: right;
}
#operator {
font-size: 30px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="contour">
<p>The calculator</p>
<div id="results" class="results">
<div id="nums">55</div>
</div>
<div class="container">
<button id="sum" class="operator-btn">+</button>
<button id="minus" class="operator-btn">-</button>
<button id="multiply" class="operator-btn">x</button>
<button id="divide" class="operator-btn">/</button>
<button id="btn-7" class="number-btn">7</button>
<button id="btn-8" class="number-btn">8</button>
<button id="btn-9" class="number-btn">9</button>
<button id="btn-4" class="number-btn">4</button>
<button id="btn-5" class="number-btn">5</button>
<button id="btn-6" class="number-btn">6</button>
<button id="btn-1" class="number-btn">1</button>
<button id="btn-2" class="number-btn">2</button>
<button id="btn-3" class="number-btn">3</button>
<button id="btn-period" class="number-btn">.</button>
<button id="btn-O" class="number-btn">0</button>
<button id="btn-clear" class="number-btn">AC</button>
<button id="btn-equals" class="operator-btn">=</button>
</div>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
hope someone can help
use button.innerHTML not buttons.innerHTML
The array is called buttons - each item you're pulling out is being initialized as button. You want to set the div equal to that item's innerHTML, not the array buttons - which, as it is an array to begin with, does not have an innerHTML property. Furthermore, it wasn't clear in your question, but if you would like to keep adding digits to the calculator box, be sure to use the += operator instead of the =, like so document.querySelector('#nums').textContent += button.innerHTML That way it will keep adding to each box on button press.
If you would like the buttons to just replace the previous item in the calculator window, this will work:
buttons.forEach(button => {
button.addEventListener('click', function(){
document.querySelector('#nums').textContent = button.innerHTML
})
})
EDIT: As a matter of fact, since you just want the text node within your HTML, it would be better for performance to simply use button.textContent or as #Barmar pointed out, this.textContent ( this also references button )
textContent is faster because when you utilize innerHTML the Browser Engine has to reprocess and parse everything while it copies it over. textContent specifically only deals with a text node and the content therein.
buttons.forEach(button => {
button.addEventListener('click', function(){
document.querySelector('#nums').textContent = button.textContent;
})
})
It should be button and not buttons :)
document.querySelector('#nums').textContent = button.innerHTML
document.querySelector('#nums').textContent += button.innerHTML;

Categories