why my function total doesn't work in calculator - javascript

I need a small help.
I'm at the beginning of my way with programming in Javascript. I'm trying do a calculator. I have a problem with '='. Don't show me a score, when I've clicked on it. Please, tell me, where I do a mistake?
here is my git: https://github.com/madzikowael/calculator/blob/main/script.js
const calculate = () => {
let action;
if(!previousOperation || !actualOperation) {
return
}
const previous = parseFloat(previousOperation)
const actual = parseFloat(actualOperation)
if(isNaN(previous) || isNan(actual)){
return
}
switch (operation) {
case '+':
action = previous + actual
break
case '-':
action = previous - actual
break
case '×':
action = previous * actual
break
case '÷':
if(actual === 0) {
deleteScore()
return
}
action = previous / actual
break
case '^':
action = Math.pow(previous, actual)
break
case '√':
action = Math.pow(previous, 1 / actual)
break
case '%':
action = previous / 100 * actual
break
case 'log':
action = Math.log(previous) / Math.log(actual)
break
default:
return
}
actualOperation = action
operation = undefined
previousOperation = ''
}
const chooseOperation = (operator) => {
if(actualOperation === '') {
return
}
if(previousOperation !== '') {
const previous = previousOperation.innerText
if(actualOperation.toString() === '0' && previous[previous.length - 1] === '÷') {
deleteScore()
return
}
calculate()
}
operation = operator;
previousOperation = actualOperation;
actualOperation = '';
}
const updateScore = () => {
actualScore.innerText = actualOperation;
if(operation != null) {
previousScore.innerText = previousOperation + operation
} else {
previousScore.innerText = ''
}
}
numbers.forEach((numb) => {
numb.addEventListener('click', () => {
addNumber(numb.innerText);
updateScore();
})
})
del.addEventListener('click', () => {
deleteNumber();
updateScore();
})
operators.forEach((operator) => {
operator.addEventListener('click', () => {
chooseOperation(operator.innerText);
updateScore();
})
})
total.addEventListener('click', () => {
calculate();
updateScore();
})
clean.addEventListener('click', () => {
deleteScore();
updateScore();
})

It's because of this line:
if(isNaN(previous) || isNan(actual)){
return
}
the function isNan() does not exist. Therefore an error is thrown and the calculate() function is aborted.
Replace it by:
if(isNaN(previous) || isNaN(actual)){
return
}

I see it's just a typo, change isNan(actual) to isNaN(actual).
As i'm unable to edit the actual question, added code snippets here.
const numbers = document.querySelectorAll('.number');
const operators = document.querySelectorAll('.operator');
const clean = document.querySelector('.clean');
const del = document.querySelector('.delete');
const total = document.querySelector('.total');
const previousScore = document.querySelector('.previous-operation');
const actualScore = document.querySelector('.actual-operation');
let actualOperation = '';
let operation = undefined;
let previousOperation = '';
const calculate = () => {
let action;
if(!previousOperation || !actualOperation) {
return
}
const previous = parseFloat(previousOperation)
const actual = parseFloat(actualOperation)
if(isNaN(previous) || isNaN(actual)){
return
}
switch (operation) {
case '+':
action = previous + actual
break
case '-':
action = previous - actual
break
case '×':
action = previous * actual
break
case '÷':
if(actual === 0) {
deleteScore()
return
}
action = previous / actual
break
case '^':
action = Math.pow(previous, actual)
break
case '√':
action = Math.pow(previous, 1 / actual)
break
case '%':
action = previous / 100 * actual
break
case 'log':
action = Math.log(previous) / Math.log(actual)
break
default:
return
}
actualOperation = action
operation = undefined
previousOperation = ''
}
const chooseOperation = (operator) => {
if(actualOperation === '') {
return
}
if(previousOperation !== '') {
const previous = previousOperation.innerText
if(actualOperation.toString() === '0' && previous[previous.length - 1] === '÷') {
deleteScore()
return
}
calculate()
}
operation = operator;
previousOperation = actualOperation;
actualOperation = '';
}
const addNumber = (numb) => {
if(numb === '.') {
if(actualOperation.includes('.')) {
return
}
numb = '.'
}
actualOperation = actualOperation.toString() + numb.toString();
}
const deleteNumber = () => {
actualOperation = actualOperation.toString().slice(0, -1)
}
const updateScore = () => {
actualScore.innerText = actualOperation;
if(operation != null) {
previousScore.innerText = previousOperation + operation
} else {
previousScore.innerText = ''
}
}
const deleteScore = () => {
actualOperation = ''
operation = undefined
previousOperation = ''
}
numbers.forEach((numb) => {
numb.addEventListener('click', () => {
addNumber(numb.innerText);
updateScore();
})
})
del.addEventListener('click', () => {
deleteNumber();
updateScore();
})
operators.forEach((operator) => {
operator.addEventListener('click', () => {
chooseOperation(operator.innerText);
updateScore();
})
})
total.addEventListener('click', () => {
calculate();
updateScore();
})
clean.addEventListener('click', () => {
deleteScore();
updateScore();
})
#import url('https://fonts.googleapis.com/css2?family=Dela+Gothic+One&display=swap');
*, *::after, *::before {
box-sizing: border-box;
font-family: 'Dela Gothic One', sans-serif;
font-size: 22px;
font-weight: 500;
color: white;
}
body {
padding: 0;
margin: 0;
background: #6c757d;
}
.calculator {
margin: 25px;
display: grid;
justify-content: center;
align-content: center;
grid-template-columns: repeat(5, 80px);
grid-template-rows: minmax(120px, auto) repeat(5, 80px);
min-height: 70vh;
}
.score {
grid-column: 1 / -1;
background-color: #333533;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-between;
padding: 20px;
box-shadow: 0px 4px 25px -10px rgba(0, 0, 0, 0.75);
word-wrap: break-word;
word-break: break-all;
}
.previous-operation {
font-size: 16px;
font-weight: 400;
color: rgba(255, 255, 255, .8);
}
.actual-operation {
font-size: 30px;
}
button {
cursor: pointer;
border: 1px solid #333533;
background: #242423;
outline: none;
}
button:hover, button:focus {
background: rgba(36, 36, 35, .95);
}
.col-two {
grid-column: span 2;
}
.row-two {
grid-row: span 2;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width-device-width, initial-scale=1.0">
<title>Najlepszy kalkulator</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<div class="calculator">
<div class="score">
<div class="previous-operation"></div>
<div class="actual-operation"></div>
</div>
<button class="clean col-two">AC</button>
<button class="delete">DEL</button>
<button class="operator">√</button>
<button class="operator">÷</button>
<button class="number">1</button>
<button class="number">2</button>
<button class="number">3</button>
<button class="operator">^</button>
<button class="operator">&times</button>
<button class="number">4</button>
<button class="number">5</button>
<button class="number">6</button>
<button class="operator">%</button>
<button class="operator">-</button>
<button class="number">7</button>
<button class="number">8</button>
<button class="number">9</button>
<button class="operator">log</button>
<button class="operator row-two">+</button>
<button class="number">.</button>
<button class="number">0</button>
<button class="total col-two">=</button>
</div>
</body>
</html>

Related

Why is my conditional statement not working when trying to swap <p> text between 3 options in javascript?

I want to change the paragraph text when the next arrow is clicked. I can change it once but if I want to change it to a third option, it's not working. Can someone explain why this is the case?
I have made a Codepen with the issue: Conditional statement problem
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>
let text = document.querySelector("p");
text.textContent = "text1";
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
if ((text.textContent = "text1")) {
arrowBack.classList.add("hidden");
nextRight.addEventListener("click", () => {
arrowBack.classList.remove("hidden");
text.textContent = "text2";
});
backLeft.addEventListener("click", () => {
arrowBack.classList.add("hidden");
text.textContent = "text1";
});
} else if ((text.textcontent = "text2")) {
nextRight.addEventListener("click", () => {
text.textContent = "text3";
});
backLeft.addEventListener("click", () => {
text.textContent = "text2";
});
} else {
text.textContent = "none";
}
Conditions need to be added in the event listener and based on the conditions, hide or show elements
let text = document.querySelector("p");
text.textContent = "text1";
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
arrowBack.classList.add("hidden");
nextRight.addEventListener("click", () => {
arrowBack.classList.remove("hidden");
if (text.textContent === "text1") {
text.textContent = "text2";
} else if (text.textContent === "text2") {
text.textContent = "text3";
}
});
backLeft.addEventListener("click", () => {
if (text.textContent === "text3") {
text.textContent = "text2";
} else if (text.textContent === "text2") {
text.textContent = "text1";
arrowBack.classList.add("hidden");
}
});
body {
background-color: black;
}
.tutNavigation {
display: flex;
padding: 3rem 2rem;
box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.25);
flex-direction: column;
flex-wrap: nowrap;
align-content: center;
gap: 1rem;
}
.tutNavigation.flexNavigation {
display: flex;
align-items: center;
justify-content: space-evenly;
flex-wrap: nowrap;
gap: 1rem;
flex-direction: row;
}
.flexNavigation.tutorialText {
text-align: center;
max-width: 500px;
}
p {
color: white;
font-size: 16px;
line-height: 1.3rem;
}
#back,
#next {
padding: 1rem;
cursor: pointer;
border-radius: 5px;
width: 72px;
}
i {
font-size: 40px;
color: white;
}
.hidden {
display: none;
}
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>
You should pull out the eventListerens form the conditions!
Beacouse you want to do something if you clicking on the arrows!
And inside the eventListeners you should write the logic.
const text = document.querySelector("p");
const possiblities = ["text1", "text2", "text3", "text4"];
let index = 0;
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
const checkAndHideArrows = () => {
if (index >= possiblities.length - 1) {
nextRight.classList.add("hidden");
arrowNext.classList.add("hidden");
console.log("hide right");
} else {
nextRight.classList.remove("hidden")
arrowNext.classList.remove("hidden")
console.log("show right");
}
if (index <= 0) {
backLeft.classList.add("hidden");
arrowBack.classList.add("hidden");
console.log("hide left");
} else {
backLeft.classList.remove("hidden");
arrowBack.classList.remove("hidden");
console.log("show left");
}
}
const setText = () => {
text.textContent = possiblities[index];
console.log(possiblities[index]);
checkAndHideArrows();
};
const next = () => {
if (index++ >= possiblities.length - 1) {
index = possiblities.length - 1;
}
};
const prev = () => {
if (index-- <= 0) {
index = 0;
}
};
setText();
nextRight.addEventListener("click", () => {
next();
console.log(index);
setText();
});
backLeft.addEventListener("click", () => {
prev();
console.log(index);
setText();
});
.hidden {
color: transparent;
}
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>

How to sort data in javascript?

let div = document.createElement('div');
let ul = document.createElement('ul');
const data = {};
const el = document.getElementById("name");
sortName = 0;
sortCapital = 0;
sortPopulation = 0;
sortArea = 0;
function sortByArea(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortArea == 0){
data.sort((a, b) => {
if (a.area > b.area) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.area < b.area) return 1;
else return -1
});
return data;
}
function sortByPopulation(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortPopulation == 0){
data.sort((a, b) => {
if (a.population > b.population) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.population < b.population) return 1;
else return -1
});
return data;
}
function sortByCapital(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortCapital == 0){
data.sort((a, b) => {
if (a.capital > b.capital) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.capital < b.capital) return 1;
else return -1
});
return data;
}
function sortByName(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortName == 0){
data.sort((a, b) => {
if (a.name > b.name) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.name < b.name) return 1;
else return -1
});
return data;
}
document.getElementById('area').addEventListener('click', () => f(4));
document.getElementById('population').addEventListener('click', () => f(3));
document.getElementById('capital').addEventListener('click', () => f(2));
document.getElementById('name').addEventListener('click', () => f(1));
div.appendChild(ul);
async function f(e) {
//fetching and sorting data by regions and subregions
const res = await fetch("https://restcountries.com/v3.1/all");
var data = await res.json();
const container = document.getElementById('container');
const accordion = document.createElement('div');
const olWrapper = document.getElementById('listWrapper');
const subRegionWrapper = document.getElementById('subRegionWrapper');
switch (e) {
case 1:
data = sortByName(data);
sortName += 1;
sortName %= 2;
case 2:
data = sortByCapital(data);
sortCapital += 1;
sortCapital %= 2;
case 3:
data = sortByPopulation(data);
sortPopulation += 1;
sortPopulation %= 2;
case 4:
data = sortByArea(data);
sortArea += 1;
sortArea %= 2;
}
data.sort((a, b) => {
if (a.region > b.region) return 1;
else if (a.region < b.region) return -1
else {
if (a.subregion > b.subregion) return 1;
else return -1;
}
});
const subRegions = data.reduce((r, a) => {
r[a.subregion] = r[a.subregion] || [];
r[a.subregion].push(a);
return r;
}, {});
const dropdownValues = Object.entries(subRegions);
dropdownValues.forEach(subRegion => {
const accordionWrapper = document.createElement('div');
const panel = document.createElement('div');
panel.classList.add('panel');
accordionWrapper.classList.add('accordion');
const totalArea = subRegion[1].reduce((acc, curr) => acc + curr.area, 0);
const totalPopulation = subRegion[1].reduce((acc, curr) => acc + curr.population, 0);
const li = createSubregion(subRegion[0], totalPopulation, totalArea);
accordionWrapper.appendChild(li);
accordion.appendChild(accordionWrapper);
subRegion[1].forEach(item => {
const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
const subOl = document.createElement('ol');
subOl.appendChild(subLi);
panel.appendChild(subOl);
accordion.appendChild(panel);
});
accordionWrapper.addEventListener('click', function () {
this.classList.toggle("active");
const panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
});
container.appendChild(accordion);
}
function createSubregion(name, population, area) {
var li = document.createElement("li");
li.setAttribute("class", "subregion");
var header = document.createElement("div");
header.setAttribute("class", "subregion-header disp-flex");
var nameDiv = document.createElement("div");
var nameh2 = document.createElement("h2");
nameh2.innerText = name;
nameDiv.appendChild(nameh2);
header.append(nameDiv);
var emptyDiv = document.createElement("div");
header.appendChild(emptyDiv);
var populationDiv = document.createElement("div");
var populationh2 = document.createElement("h3");
populationh2.innerText = population;
populationDiv.appendChild(populationh2);
header.append(populationDiv);
var areaDiv = document.createElement("div");
var areah2 = document.createElement("h3");
areah2.innerText = area;
areaDiv.appendChild(areah2);
header.append(areaDiv);
li.appendChild(header);
return li;
}
function createCountry(name, capital, area, population) {
var country = document.createElement("li");
country.setAttribute("class", "country disp-flex")
var namediv = document.createElement("div");
var nameh4 = document.createElement("h4");
nameh4.innerText = name;
namediv.appendChild(nameh4);
country.appendChild(namediv);
var capitaldiv = document.createElement("div");
var capitalh4 = document.createElement("h4");
capitalh4.innerText = capital;
capitaldiv.appendChild(capitalh4);
country.appendChild(capitaldiv);
var popdiv = document.createElement("div");
var poph4 = document.createElement("h4");
poph4.innerText = population;
popdiv.appendChild(poph4);
country.appendChild(popdiv);
var areadiv = document.createElement("div");
var areah4 = document.createElement("h4");
areah4.innerText = area;
areadiv.appendChild(areah4);
country.appendChild(areadiv);
return country;
}
f(0);
*{
transition: 500ms;
}
body {
margin: 0 15%;
min-height: 100vh;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: aliceblue;
font-family: 'Open Sans', Arial;
font-size: 18px;
}
#name {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#capital {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#population {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#area {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
header {
margin: 0 10%;
display: flex;
justify-content: space-between;
padding: 22px 0;
color: rgb(5, 5, 5);
}
ul {
list-style: none;
list-style-type: none;
outline: 2px solid #ddd;
padding: 1rem 2rem;
border-radius: 0.5rem;
list-style-position: inside;
color: blue;
}
ul ol {
color: rgb(197, 105, 18);
list-style: none;
list-style-type: none;
font-size: .9em;
margin: 0.4rem 0;
}
.country {
display: flex;
justify-content: space-between;
}
.disp-flex {
display: flex;
justify-content: space-between;
}
.disp-flex>div {
width: 23%;
padding: 15px 0px;
}
.subregion-header>div:nth-child(1) {
position: relative;
left: 30px;
}
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
margin: 15px 2px;
}
.accordion li {
list-style-type: none;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
margin-left: 5%;
display: none;
background-color: white;
overflow: hidden;
}
#name:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#capital:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#population:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#area:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
<!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>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="container">
<header>
<div id="name">
<h1>Name</h1>
</div>
<div id="capital">
<h1>Capital</h1>
</div>
<div id="population">
<h1>Population</h1>
</div>
<div id="area">
<h1>Area</h1>
</div>
</header>
<div id="container"></div>
<div id="subRegionWrapper"> </div>
<div id="listWrapper"></div>
<script src="script.js"></script>
</main>
</body>
</html>
I made a little data-list containing some information about countries (names, capitals, areas and population), all the countries are grouped by sub-regions. In the header section I have four positions describing each column (name, capital, area and population). I want to add functionality of sorting, so that after clicking on position in the header section, the data would sort by that position. For example after clicking once on name, data should sort by name(asc or desc) and then after clicking once more on name, data should sort inversely to previous sort. I would like to allow sorting by couple columns at same time, so for example after clicking on capital and area, the data should be sorted by capitals and area. Here is my code:
I tried to do that sorting in different ways (adding extra function, passing parameter to main function etc.) but unfortunately nothing worked.I am really struggling with that, I will be extremely grateful for any help.
Your code is actually working, but you are not removing the old table. So the new sorted entries are appended to the already existing ones. You should clear the table some way, for example:
const container = document.getElementById('container');
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
Put this where you get the container in your function f(e) and it should be working

Increase or decrease value continuously using mousedown JavaScript

I copied some code from here.
The following code has some errors when the value reaches the minFontSize or maxFontSize.
I don't know how to fix this...
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
btn.addEventListener("mouseup", clearTimers);
btn.addEventListener("mouseleave", clearTimers);
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
Your timers are not cleared when you hit the minimum or maximum because (as #timmmmmb already said) disabled buttons don't trigger mouse events. Thus, when you try to go in the other direction, the original timer is again executed.
The simplest would probably be, calling clearTimers also when you hit the end of the range
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
btn.addEventListener("mouseup", clearTimers);
btn.addEventListener("mouseleave", clearTimers);
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
clearTimers();
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
clearTimers();
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
I think the problem is, that when you reach the max Fontsize you disable the button and that means your mouseup event will not be triggerd. I moved the mouseup event away from the buttons and on the container and i think it works now.
const decreaseFontSizeBtn = document.querySelector(".fontsize-via-btn #decrease");
const increaseFontSizeBtn = document.querySelector(".fontsize-via-btn #increase");
const fontSizeDisplay = document.querySelector(".fontsize-via-btn .current-fontsize");
const fontSizeContainer = document.querySelector(".fontsize-via-btn");
const defaultFontSize = 20;
const minFontSize = 16;
const maxFontSize = 40;
let currentFontSize = defaultFontSize;
var timeout, interval;
fontSizeContainer.addEventListener("mouseup", clearTimers);
fontSizeContainer.addEventListener("mouseleave", clearTimers);
function clearTimers() {
clearTimeout(timeout);
clearInterval(interval);
}
[decreaseFontSizeBtn, increaseFontSizeBtn].forEach(btn => {
btn.addEventListener("mousedown", () => {
if (btn.id === "decrease") {
decreaseFontSize();
hold(decreaseFontSize);
}
if (btn.id === "increase") {
increaseFontSize();
hold(increaseFontSize);
}
saveFontSize();
})
})
function hold(func) {
timeout = setTimeout(() => {
interval = setInterval(() => {
func();
saveFontSize();
}, 50)
}, 300)
}
function decreaseFontSize() {
if (currentFontSize > minFontSize) {
currentFontSize -= 2;
}
if (currentFontSize === minFontSize) {
decreaseFontSizeBtn.disabled = true;
} else {
increaseFontSizeBtn.disabled = false;
}
}
function increaseFontSize() {
if (currentFontSize < maxFontSize) {
currentFontSize += 2;
}
if (currentFontSize === maxFontSize) {
increaseFontSizeBtn.disabled = true;
} else {
decreaseFontSizeBtn.disabled = false;
}
}
function saveFontSize() {
fontSizeDisplay.textContent = currentFontSize;
// localStorage ...
}
.fontsize-via-btn {
width: 100px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
font-size: 2rem;
}
<div class="fontsize-via-btn">
<button id="decrease">A-</button>
<div class="current-fontsize">20</div>
<button id="increase">A+</button>
</div>
Discrepancies
"mouseup" and "mouseleave" needs to be registered on each button or the events must be delegated to the buttons. Right now the mouse events only affect the <div> which doesn't have any functions firing off on it. So that's why the buttons get stuck -- they are consantly repeating themselves even after the user releases the mousebutton.
The example below has separate event handlers for each button. Normally I prefer to use one event handler to control everything through
event delegation, but because of the need to use set\clearInterval() as event handlers, the event object gets lost. I could solve that problem with a closure, but your code was close enough that a radical change wouldn't be to your benifet.
Saving with localStorage (I know it's not part of the question) is omitted from this version since this site blocks it's use. I wrote another version that saves the current font-size and loads it as well, to review that one go to this Plunker.
const io = document.forms.UI.elements;
let current = 1.5;
let repeatInc = null;
let repeatDec = null;
function holdInc(e) {
if (repeatInc === null) {
repeatInc = setInterval(incFontSize, 100);
}
};
function releaseInc(e) {
if (repeatInc != null) {
clearInterval(repeatInc);
repeatInc = null;
}
};
function incFontSize() {
const io = document.forms.UI.elements;
const max = 4;
if (current < max) {
current += 0.25;
io.dec.disabled = false;
}
if (current == max) {
io.inc.disabled = true;
releaseInc();
}
io.fSize.value = current.toFixed(2);
io.text.style.fontSize = `${current}rem`
};
function holdDec(e) {
if (repeatDec == null) {
repeatDec = setInterval(decFontSize, 100);
}
};
function releaseDec(e) {
if (repeatDec != null) {
clearInterval(repeatDec);
repeatDec = null;
}
};
function decFontSize() {
const io = document.forms.UI.elements;
const min = 1;
if (current > min) {
current -= 0.25;
io.inc.disabled = false;
}
if (current == min) {
io.dec.disabled = true;
releaseDec()
}
io.fSize.value = current.toFixed(2);
io.text.style.fontSize = `${current}rem`
};
io.inc.addEventListener('mousedown', holdInc);
io.inc.addEventListener('mouseup', releaseInc);
io.inc.addEventListener('mousemove', releaseInc);
io.dec.addEventListener('mousedown', holdDec);
io.dec.addEventListener('mouseup', releaseDec);
io.dec.addEventListener('mouseleave', releaseDec);
:root {
font: 2ch/1 'Segoe UI'
}
body {
font-size: 1ch;
}
.ctrl {
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 1rem;
padding: 5px;
}
#fSize {
display: block;
width: 5ch;
margin: 0 4px;
font-family: Consolas;
text-align: center;
}
button {
display: block;
width: max-content;
font: inherit;
cursor: pointer;
}
label {
display: block;
width: max-content;
margin: 0 8px;
font-size: 1rem;
}
.text {
min-height: 50px;
font-size: 1.25rem;
}
<form id='UI'>
<fieldset name='ctrl' class='ctrl'>
<button id="dec" type='button'>🗛-</button>
<output id="fSize">1.50</output>
<button id="inc" type='button'>🗚+</button>
<label>Scale: 1ch => 1rem</label>
</fieldset>
<fieldset name='text' class='text' contenteditable>
This test area has editable content
</fieldset>
</form>

I'm not getting any outputs for some reason after replacing the javascript functions with imported python functions

index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="styles.css">
<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>Advanced Calculator</title>
<script src="functions.py" defer></script>
<script src="main.py" defer></script>
<script src="script.js" defer></script>
</head>
<body>
<div class="calculator-grid">
<div class="output">
<div data-previous-operand class="previous-operand">xyz</div>
<div data-current-operand class="current-operand">abc</div>
</div>
<button data-all-clear class="span-two">AC</button>
<button data-delete>DEL</button>
<button data-operation>÷</button>
<button data-operation>//</button>
<button data-operation>rad</button>
<button data-operation>deg</button>
<button data-operation>log</button>
<button data-number>1</button>
<button data-number>2</button>
<button data-number>3</button>
<button data-operation>*</button>
<button data-operation>**</button>
<button data-operation>sin</button>
<button data-operation>cos</button>
<button data-operation>tan</button>
<button data-number>4</button>
<button data-number>5</button>
<button data-number>6</button>
<button data-operation>+</button>
<button data-operation>!</button>
<button data-operation>cosec</button>
<button data-operation>sec</button>
<button data-operation>cot</button>
<button data-number>7</button>
<button data-number>8</button>
<button data-number>9</button>
<button data-operation>-</button>
<button data-operation>√</button>
<button data-operation>ceil</button>
<button data-operation>floor</button>
<button data-operation class="span-twoV">rem</button>
<button data-number>.</button>
<button data-number>0</button>
<button data-equals class="span-two">=</button>
<button data-operation>3√</button>
<button data-operation class="span-two">||</button>
</div>
</body>
</html>
styles.css :
*, *::before, *::after {
box-sizing: border-box;
font-family: Gotham Rounded, sans-serif;
font-weight: normal;
}
body {
padding: 0;
margin: 0;
background-image: linear-gradient(to right, lightblue, rgb(0, 247, 255), blue);
}
.calculator-grid {
display: grid;
justify-content: center;
align-content: center;
min-height: 100vh;
grid-template-columns: repeat(8, 100px);
grid-template-rows: minmax(120px, auto) repeat(5, 100px);
}
.calculator-grid > button {
cursor: pointer;
font-size: 2rem;
border: 1px solid white;
outline: none;
background-color: rgba(255, 255, 255, .75);
}
.calculator-grid > button:hover {
background-color: rgba(14, 221, 187, 0.9);
}
.span-two {
grid-column: span 2;
}
.span-twoV {
grid-row: span 2;
}
.output {
grid-column: 1 / -1;
background-color: rgba(0, 0, 0, .75);
display: flex;
align-items: flex-end;
justify-content: space-around;
flex-direction: column;
padding: 10px;
word-wrap: break-word;
word-break: break-all;
}
.output .previous-operand {
color: rgba(255, 255, 255, .75);
font-size: 2.5rem;
}
.output .current-operand {
color: white;
font-size: 2.5rem;
}
.output input {
grid-column: 1 / -1;
background-color: rgba(0, 0, 0, .75);
display: flex;
align-items: flex-end;
justify-content: space-around;
flex-direction: column;
padding: 10px;
word-wrap: break-word;
word-break: break-all;
}
script.js :
import { add, subt, multi, divi, power, floDivi, remDivi, log, sqrt, cbrt, rad, deg, sin, cos, tan, cosec, sec, cot, ceil, floor, abso, facto } from functions.py;
class Calculator {
constructor(previousOperandTextElement, currentOperandTextElement) {
this.previousOperandTextElement = previousOperandTextElement
this.currentOperandTextElement = currentOperandTextElement
this.clear()
}
clear() {
this.currentOperand = ''
this.previousOperand = ''
this.operation = undefined
}
delete() {
this.currentOperand = this.currentOperand.toString().slice(0, -1)
}
appendNumber(number) {
if (number === '.' && this.currentOperand.includes('.')) return
this.currentOperand = this.currentOperand.toString() + number.toString()
}
chooseOperation(operation) {
if (this.currentOperand === '') return
if (this.previousOperand !== '') {
this.compute()
}
this.operation = operation
this.previousOperand = this.currentOperand
this.currentOperand = ''
}
compute() {
let computation
const prev = parseFloat(this.previousOperand)
const current = parseFloat(this.currentOperand)
if (isNaN(prev) || isNaN(current)) return
switch (this.operation) {
case '+':
computation = add(prev, current)
break
case '-':
computation = subt(prev, current)
break
case '*':
computation = multi(prev, current)
break
case '÷':
computation = divi(prev, current)
break
case '//':
computation = floDivi(prev, current)
break
case '**':
computation = power(prev, current)
break
case '!':
computation = facto(prev)
break
case '√':
computation = sqrt(prev)
break
case '3√':
computation = cbrt(prev)
break
case 'rad':
computation = rad(prev)
break
case 'deg':
computation = deg(prev)
break
case 'sin':
computation = sin(prev)
break
case 'cos':
computation = cos(prev)
break
case 'tan':
computation = tan(prev)
break
case 'cosec':
computation = cosec(prev)
break
case 'sec':
computation = sec(prev)
break
case 'cot':
computation = cot(prev)
break
case 'ceil':
computation = ceil(prev)
break
case 'floor':
computation = floor(prev)
break
case '||':
computation = abso(prev)
break
case 'rem':
computation = remDivi(prev, current)
break
case 'log':
computation = log(prev)
break
default:
return
}
this.currentOperand = computation
this.operation = undefined
this.previousOperand = ''
}
getDisplayNumber(number) {
const stringNumber = number.toString()
const integerDigits = parseFloat(stringNumber.split('.')[0])
const decimalDigits = stringNumber.split('.')[1]
let integerDisplay
if (isNaN(integerDigits)) {
integerDisplay = ''
} else {
integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
}
if (decimalDigits != null) {
return `${integerDisplay}.${decimalDigits}`
} else {
return integerDisplay
}
}
updateDisplay() {
this.currentOperandTextElement.innerText =
this.getDisplayNumber(this.currentOperand)
if (this.operation != null) {
this.previousOperandTextElement.innerText =
`${this.getDisplayNumber(this.previousOperand)} ${this.operation}`
} else {
this.previousOperandTextElement.innerText = ''
}
}
}
const numberButtons = document.querySelectorAll('[data-number]')
const operationButtons = document.querySelectorAll('[data-operation]')
const equalsButton = document.querySelector('[data-equals]')
const deleteButton = document.querySelector('[data-delete]')
const allClearButton = document.querySelector('[data-all-clear]')
const previousOperandTextElement = document.querySelector('[data-previous-operand]')
const currentOperandTextElement = document.querySelector('[data-current-operand]')
const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement)
numberButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.appendNumber(button.innerText)
calculator.updateDisplay()
})
})
operationButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.chooseOperation(button.innerText)
calculator.updateDisplay()
})
})
equalsButton.addEventListener('click', button => {
calculator.compute()
calculator.updateDisplay()
})
allClearButton.addEventListener('click', button => {
calculator.clear()
calculator.updateDisplay()
})
deleteButton.addEventListener('click', button => {
calculator.delete()
calculator.updateDisplay()
})
document.addEventListener('keydown', function (event) {
let patternForNumbers = /[0-9]/g;
let patternForOperators = /[+\-*\/]/g
if (event.key.match(patternForNumbers)) {
event.preventDefault();
calculator.appendNumber(event.key)
calculator.updateDisplay()
}
if (event.key === '.') {
event.preventDefault();
calculator.appendNumber(event.key)
calculator.updateDisplay()
}
if (event.key.match(patternForOperators)) {
event.preventDefault();
calculator.chooseOperation(event.key)
calculator.updateDisplay()
}
if (event.key === 'Enter' || event.key === '=') {
event.preventDefault();
calculator.compute()
calculator.updateDisplay()
}
if (event.key === "Backspace") {
event.preventDefault();
calculator.delete()
calculator.updateDisplay()
}
if (event.key == 'Delete') {
event.preventDefault();
calculator.clear()
calculator.updateDisplay()
}
});
functions.py :
#All functions listed here
import math
# double input functions below
def add(a, b):
return a+b
def subt(a, b):
return a-b
def multi(a, b):
return a*b
def divi(a, b):
return a/b
def floDivi(a, b):
return a//b
def remDivi(a, b):
return a%b
def power(a, b):
return math.pow(a, b)
def log(a, b):
return math.log(a,[b])
# single input functions below
def sqrt(a):
return math.sqrt(a)
def cbrt(a):
return a**(1./3)
# trigonometry related functions below
def rad(a):
return math.radians(a)
def deg(a):
return math.degrees(a)
def cos(a):
return math.cos(a)
def sin(a):
return math.sin(a)
def tan(a):
return math.tan(a)
def sec(a):
return (1/math.cos(a))
def cosec(a):
return (1/math.sin(a))
def cot(a):
return (1/math.tan(a))
# miscellaneous functions
def ceil(a):
return math.ceil(a)
def floor(a):
return math.ceil(a)
def abso(a):
return math.fabs(a)
def facto(a):
return math.factorial(a)
The current status and how it looks
I am not getting any output and no button is working anymore as soon as I replaced the javascript functions in script.js with imported functions from functions.py
If possible please suggest a solution that involves python, I have to include python since it is for a school project. Hopefully we can work out a solution fast.
As I undrstood, your problem is that you have some code in python that you want to run in browser on client-side.
I think that the best solution here would be just to rewrite your python code in js. You could do it manually or use some python to javascript converter like this.
You might also be intrested in this js global Math object.

How do I implement a timer to deal each card after 2 seconds?

I have this function which will deal a card to a player, then to a dealer, then to a player and then to a dealer.
I have tried to use setTimeout(function, milliseconds); but it doesn't work. For example, if I set 2 seconds, it will wait for 4 seconds, then deal the 2 cards to the player and then straight away to dealer 2 cards or it will wait for 8 seconds, then in one batch deal all the cards out.
Here are my methods:
const dealOneCardToPlayer = () => {
// Take a card from the top deck to be assigned to tempcard.
tempCard = deck.cards.splice(0, 1);
//console.log(tempCard);
player.cards.push(tempCard);
if (player.cards.length === 5) {
player.canHit = false;
}
if (player.canHit) {
$("#btnHit").show();
} else {
$("#btnHit").hide();
}
player.handValue = countHandValue(player.cards);
makeCardPlayer(tempCard[0]);
}
const dealOneCardToDealer = (holeCard) => {
// Take a card from the top deck to be assigned to tempcard.
tempCard = deck.cards.splice(0, 1);
dealer.cards.push(tempCard);
if (dealer.cards.length === 5) {
dealer.canHit = false;
}
if (dealer.canHit) {
$("#btnHit").show();
} else {
$("#btnHit").hide();
}
dealer.handValue = countHandValue(dealer.cards);
makeCardDealer(tempCard[0],holeCard);
}
const deal = () => {
debugger;
newDeck();
// Option: to burn first card before deal a card
// to the first player
burnOneCard;
dealOneCardToPlayer();
dealOneCardToDealer(false);
dealOneCardToPlayer();
// true for hole card
dealOneCardToDealer(true);
showGameButtons(true);
checkEndGame1();
checkGameOver();
}
<link href="check.css" rel="stylesheet" />
<style>
body{
font-size: 2em;
}
h3, h5 {
text-align: center;
}
h5{
margin-top:-40px;
}
/*debugging purpose*/
div#oneDeck {
border: 1px solid green;
margin: 10px;
padding: 10px;
}
/*debugging purpose*/
div#playerCards {
border: 1px solid blue;
margin: 10px;
padding: 10px;
}
/*debugging purpose*/
div#dealerCards {
border: 1px solid red;
margin: 10px;
padding: 10px;
}
#mainContainer {
max-width: 600px;
margin: 0 auto;
}
fieldset {
margin-top: 30px;
border: 1px solid #999;
border-radius: 8px;
box-shadow: 0 0 10px #999;
}
legend {
background: #fff;
}
#cardContainerPlayer {
display: flex;
flex-wrap: wrap;
}
.card {
display: inline-block;
vertical-align: top; /*float: left;*/
text-align: center;
margin: 5px;
padding: 10px;
width: 70px;
height: 100px;
font-size: 26px;
background-color: black;
border: solid 1px black;
color: white;
border-radius: 10px;
}
.holeCard {
/*visibility: hidden;*/
border: solid 1px black;
background: repeating-linear-gradient( 45deg, #606dbc, #606dbc 10px, #465298 10px, #465298 20px );
}
.red {
background-color: red;
border: solid 1px #8C001A;
}
.templatePlayer, .templateDealer {
display: none;
}
#btnGame {
margin: 10px;
}
.winner {
border: solid 5px #7ac142;
}
.btnGame {
background-color: dodgerblue; /* Green */
border: none;
color: white;
padding: 15px 32px;
/*border-radius:10px;*/
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}
#btnHit {
margin-right: 20px;
}
.flex-container {
padding: 0;
margin: 0;
display: flex;
justify-content: space-between;
max-width: 100%;
overflow: auto;
/*border: 1px solid red*/
}
</style>
<h3>Simple Javascript BlackJack Game</h3>
<h5>developed by Steve Ngai</h5>
<div id="mainContainer">
<div id="btnDevelopment">
<input type='button' value='Create new Deck' onclick='newDeck();' />
<input type='button' value='Burn a card' onclick='burnOneCard();' />
<input type='button' value='Refresh Deck' onclick='showDeck();' />
<input type='button' value='Deal a card to Player' onclick='dealOneCardToPlayer();' />
<input type='button' value='Deal a card to Dealer' onclick='dealOneCardToDealer();' />
<input type='button' value='Show hand value' onclick='showHandValue();' />
<input type='button' value='Check end game' onclick='checkEndGame();' />
<input type='button' value='Refresh deck remaining cards count' onclick='getDeckCardCount();' />
</div>
<fieldset id="deck">
<legend>Remaining cards in the Deck: <span id="deckCardCount"></span></legend>
<div id="oneDeck"></div>
</fieldset>
<fieldset id="containerDealer">
<legend>Dealer (Hand Value: <span id="handValueDealer"></span>)</legend>
<div style="width:30px">
<svg class="checkmarkDealer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
</svg>
</div>
<div id="dealerCards"></div>
<div id="cardContainerDealer">
<div class="card templateDealer">
<span class="dealerCardFace"></span>
<span class="dealerCardSuit"></span>
</div>
</div>
<div id="dealerCardsHandValue"></div>
</fieldset>
<div id="btnGame">
<div class="flex-container">
<div class="btn">
<input type='button' class="btnGame" id="btnDeal" value='Deal' onclick='deal();' />
</div>
<div class="btn">
<input type='button' class="btnGame" id="btnHit" value='Hit' onclick='hit();' />
<input type='button' class="btnGame" id="btnStand" value='Stand' onclick='stand();' />
</div>
</div>
</div>
<fieldset id="containerPlayer">
<legend>Player (Hand Value: <span id="handValuePlayer"></span>)</legend>
<div style="width:30px">
<svg class="checkmarkPlayer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
</svg>
</div>
<div id="playerCards"></div>
<div id="cardContainerPlayer">
<div class="card templatePlayer">
<span class="playerCardFace"></span>
<span class="playerCardSuit"></span>
</div>
</div>
<div id="playerCardsHandValue"></div>
</fieldset>
<fieldset id="result">
<legend>Game Result</legend>
<div id="gameResult"></div>
</fieldset>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
"use strict";
// Variable/Object declaration and initialization - Start
const isDebug = false;
const DELAY = 2000;
var gameOver = false;
const deck = {
cards: []
}
var tempCard;
const player = {
cards: [],
handValue: 0,
isWinner: false,
canHit: true
}
const dealer = {
cards: [],
handValue: 0,
isWinner: false,
canHit: true
}
var result = document.getElementById("gameResult");
const cardSuit = ["hearts", "diams", "clubs", "spades"];
const cardFace = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
$(".checkmarkDealer").hide();
$(".checkmarkPlayer").hide();
$("#handValueDealer").hide();
//Variable/Object declaration and initialization - End
if (!isDebug) {
document.getElementById("btnDevelopment").style.display = "none";
document.getElementById("deck").style.display = "none";
document.getElementById("oneDeck").style.display = "none";
document.getElementById("playerCards").style.display = "none";
document.getElementById("dealerCards").style.display = "none";
//document.getElementById("result").style.display = "none";
} else {
document.getElementById("btnDevelopment").style.display = "block";
document.getElementById("deck").style.display = "block";
document.getElementById("oneDeck").style.display = "block";
document.getElementById("playerCards").style.display = "block";
document.getElementById("dealerCards").style.display = "block";
//document.getElementById("result").style.display = "block";
}
const showGameButtons = (cardDealt) => {
if (cardDealt) {
$("#btnDeal").hide();
$("#btnHit").show();
$("#btnStand").show();
//document.getElementById("btnDeal").disabled = true;
//document.getElementById("btnHit").disabled = false;
//document.getElementById("btnStand").disabled = false;
} else {
$("#btnDeal").show();
$("#btnHit").hide();
$("#btnStand").hide();
//document.getElementById("btnDeal").disabled = false;
//document.getElementById("btnHit").disabled = true;
//document.getElementById("btnStand").disabled = true;
}
if (player.isWinner === true) {
document.getElementById("containerDealer").classList.remove("winner");
document.getElementById("containerPlayer").classList.add("winner");
$("#handValueDealer").show();
$(".checkmarkPlayer").show();
$(".checkmarkDealer").hide();
} else if (dealer.isWinner === true) {
document.getElementById("containerPlayer").classList.remove("winner");
document.getElementById("containerDealer").classList.add("winner");
$("#handValueDealer").show();
$(".checkmarkPlayer").hide();
$(".checkmarkDealer").show();
} else {
}
}
showGameButtons(false);
// In JavaScript, functions are objects.
// You can work with functions as if they were objects.
function card(suit, face) {
this.suit = suit;
this.face = face;
switch (face) {
case "A":
this.faceValue = 11;
break;
case "J":
case "Q":
case "K":
this.faceValue = 10;
break;
default:
this.faceValue = parseInt(face);
break;
}
};
const createDeck = () => {
deck.cards = [];
deck.cards.length = 0;
cardSuit.forEach(function (suit) {
cardFace.forEach(function (face) {
deck.cards.push(new card(suit, face));
});
});
}
const shuffleDeck = () => {
// Fisher–Yates shuffle algorithm
let temp, i, rnd;
for (i = 0; i < deck.cards.length; i++) {
rnd = Math.floor(Math.random() * deck.cards.length);
temp = deck.cards[i];
deck.cards[i] = deck.cards[rnd];
deck.cards[rnd] = temp;
}
}
const newDeck = () => {
createDeck();
shuffleDeck();
document.getElementById("oneDeck").innerHTML = "";
player.cards = [];
player.handValue = 0;
dealer.cards = [];
dealer.handValue = 0;
var myNode = document.getElementById("cardContainerPlayer");
var fc = myNode.firstChild.firstChild;
while (fc) {
myNode.removeChild(fc);
fc = myNode.firstChild;
}
var myNodeDealer = document.getElementById("cardContainerDealer");
var fcDealer = myNodeDealer.firstChild.firstChild;
while (fcDealer) {
myNodeDealer.removeChild(fcDealer);
fcDealer = myNodeDealer.firstChild;
}
document.getElementById("playerCards").innerHTML = "";
document.getElementById("dealerCards").innerHTML = "";
document.getElementById("oneDeck").innerHTML = JSON.stringify(deck);
}
const burnOneCard = () => {
// Remove the top deck to burn
deck.cards.splice(0, 1);
}
const showDeck = () => {
document.getElementById("oneDeck").innerHTML = JSON.stringify(deck);
}
const dealOneCardToPlayer = () => {
return new Promise(function (resolve) {
setTimeout(function () {
// Take a card from the top deck to be assigned to tempcard.
tempCard = deck.cards.splice(0, 1);
//console.log(tempCard);
player.cards.push(tempCard);
if (player.cards.length === 5) {
player.canHit = false;
}
if (player.canHit) {
$("#btnHit").show();
} else {
$("#btnHit").hide();
}
//player.cards.push(new card("Spades","A"));
//player.cards.push(new card("Spades","10"));
document.getElementById("playerCards").innerHTML = JSON.stringify(player);
player.handValue = countHandValue(player.cards);
document.getElementById("handValuePlayer").innerHTML = player.handValue;
makeCardPlayer(tempCard[0]);
resolve();
}, DELAY);
});
}
const dealOneCardToDealer = (holeCard) => {
return new Promise(function (resolve) {
setTimeout(function () {
// Take a card from the top deck to be assigned to tempcard.
tempCard = deck.cards.splice(0, 1);
dealer.cards.push(tempCard);
if (dealer.cards.length === 5) {
dealer.canHit = false;
}
if (dealer.canHit) {
$("#btnHit").show();
} else {
$("#btnHit").hide();
}
document.getElementById("dealerCards").innerHTML = JSON.stringify(dealer);
dealer.handValue = countHandValue(dealer.cards);
document.getElementById("handValueDealer").innerHTML = dealer.handValue;
makeCardDealer(tempCard[0], holeCard);
resolve();
}, DELAY);
});
}
const hasAceInHand = (cardsOnHand) => {
for (let key in cardsOnHand) {
let arr = cardsOnHand[key];
for (let i = 0; i < arr.length; i++) {
let obj = arr[i];
for (let prop in obj) {
if (prop === "face") {
if (obj[prop] === "A") {
return true;
}
}
}
}
}
return false;
}
const countHandValue = (cardsOnHand) => {
//console.log(hasAceInHand(cardsOnHand));
let sum = 0;
for (let key in cardsOnHand) {
let arr = cardsOnHand[key];
for (let i = 0; i < arr.length; i++) {
let obj = arr[i];
for (let prop in obj) {
if (prop === "faceValue") {
//console.log(prop + " = " + obj[prop]);
sum = sum + obj[prop];
debugger;
if (sum > 21 && hasAceInHand(cardsOnHand)) {
// Transfer Ace's face value from 11 to 1
sum = sum - 11;
sum = sum + 1;
}
}
}
}
}
return sum;
}
const showHandValue = () => {
document.getElementById("playerCardsHandValue").innerHTML = player.handValue;
document.getElementById("dealerCardsHandValue").innerHTML = dealer.handValue;
}
const getDeckCardCount = () => {
document.getElementById("deckCardCount").innerHTML = deck.cards.length;
}
const checkGameOver = () => {
if (gameOver) {
$(".holeCard > :nth-child(1)").show();
$(".holeCard > :nth-child(2)").show();
$(".holeCard").removeClass("holeCard");
$("#handValueDealer").show();
showGameButtons(false);
}
}
const checkEndGame1 = () => {
gameOver = true;
if (player.handValue === 21 && dealer.handValue !== 21) {
result.innerHTML = "BlackJack! Player won.";
player.isWinner = true;
} else if (player.handValue !== 21 && dealer.handValue === 21) {
result.innerHTML = "BlackJack! Dealer won.";
dealer.isWinner = true;
} else if (player.handValue === 21 && dealer.handValue === 21) {
result.innerHTML = "Push.";
} else {
gameOver = false;
}
}
const checkEndGame2 = () => {
if (player.cards.length <= 5 && player.handValue > 21) {
result.innerHTML = "Bust! Dealer won.";
dealer.isWinner = true;
gameOver = true;
}
}
const checkEndGame3 = () => {
if (player.cards.length <= 5 && dealer.cards.length <= 5) {
// Check bust
if (player.handValue <= 21 && dealer.handValue > 21) {
result.innerHTML = "Bust! Player won.";
player.isWinner = true;
} else if (player.handValue === 21 && dealer.handValue !== 21) {
result.innerHTML = "BlackJack! Player won.";
player.isWinner = true;
} else if (player.handValue !== 21 && dealer.handValue === 21) {
result.innerHTML = "BlackJack! Dealer won.";
dealer.isWinner = true;
} else if (player.handValue === dealer.handValue) {
result.innerHTML = "Push.";
} else if (player.handValue > dealer.handValue) {
result.innerHTML = "Player won.";
player.isWinner = true;
} else if (player.handValue < dealer.handValue) {
result.innerHTML = "Dealer won.";
dealer.isWinner = true;
} else {
result.innerHTML = "Error";
}
} else {
result.innerHTML = "Error";
}
gameOver = true;
}
// This function use JQuery lib
function makeCardPlayer(_card) {
// .card is created in the template card css class
var card = $(".card.templatePlayer").clone();
card.removeClass("templatePlayer");
// .cardFace is created in the template card css class
// It will search for this css class and add the content aka innerHTML
card.find(".playerCardFace").html(_card.face);
// .suit is created in the template card css class
// It will search for this css class and add the content aka innerHTML
card.find(".playerCardSuit").html("&" + _card.suit + ";");
// ♠ -> ♠, ♣ -> ♣, ♥ -> ♥, ♦ -> ♦
// more char, https://www.w3schools.com/charsets/ref_utf_symbols.asp
// hearts and diamonds are red color. otherwise, default black color.
if (_card.suit === "hearts" || _card.suit === "diams") {
card.addClass("red");
}
// option: replace previous card with new card (show one card all the time)
$("#cardContainerPlayer").append(card);
}
// This function use JQuery lib
function makeCardDealer(_card, _holeCard) {
// .card is created in the template card css class
var card = $(".card.templateDealer").clone();
card.removeClass("templateDealer");
// .cardFace is created in the template card css class
// It will search for this css class and add the content aka innerHTML
card.find(".dealerCardFace").html(_card.face);
// .suit is created in the template card css class
// It will search for this css class and add the content aka innerHTML
card.find(".dealerCardSuit").html("&" + _card.suit + ";");
// ♠ -> ♠, ♣ -> ♣, ♥ -> ♥, ♦ -> ♦
// more char, https://www.w3schools.com/charsets/ref_utf_symbols.asp
// hearts and diamonds are red color. otherwise, default black color.
if (_card.suit === "hearts" || _card.suit === "diams") {
card.addClass("red");
}
if (_holeCard) {
card.addClass("holeCard");
}
// option: replace previous card with new card (show one card all the time)
$("#cardContainerDealer").append(card);
$(".holeCard > :nth-child(1)").hide();
$(".holeCard > :nth-child(2)").hide();
}
const deal = () => {
debugger;
newDeck();
// Option: to burn first card before deal a card
// to the first player
burnOneCard;
dealOneCardToPlayer()
.then(dealOneCardToDealer)
.then(dealOneCardToPlayer)
.then(dealOneCardToDealer(true));
//dealOneCardToPlayer();
//dealOneCardToDealer(false);
//dealOneCardToPlayer();
//// true for hole card
//dealOneCardToDealer(true);
showGameButtons(true);
checkEndGame1();
checkGameOver();
}
const hit = () => {
dealOneCardToPlayer();
checkEndGame2();
checkGameOver();
}
const stand = () => {
// Recalculate dealer's hand value
//dealer.handValue = countHandValue(dealer.cards);
debugger;
// Simple AI to automate dealer's decision to hit or stand
if (dealer.handValue >= 17) {
checkEndGame3();
} else {
// Hit until dealer's hand value is more than 16
while (dealer.handValue < 17) {
dealOneCardToDealer();
checkEndGame3();
}
}
checkGameOver();
}
</script>
I think the right way to approach is with promises:
const DELAY = 2000;
function dealCardToPlayer() {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('Dealing card to player');
resolve();
}, DELAY);
});
}
function dealCardToDealer() {
return new Promise(function(resolve) {
setTimeout(function() {
console.log('Dealing card to dealer');
resolve();
}, DELAY);
});
}
dealCardToPlayer()
.then(dealCardToDealer)
.then(dealCardToPlayer)
.then(dealCardToDealer);

Categories