So I want to add or substract a value from total, based on which div is clicked (minus or plus sign). A counter also shows how much the value is (this I have working). So i want to calculate the total from which items are clicked.
My code for now:
var minExtras = document.querySelectorAll(".extra-min");
var plusExtras = document.querySelectorAll(".extra-plus");
var totalPrice = document.getElementById("totalPrice");
totalPrice.innerHTML = "Totaalprijs: € ";
Array.prototype.forEach.call(minExtras, function (minExtra) {
minExtra.addEventListener('click', function() {
var currentCount = minExtra.parentElement.querySelector('.count').innerText;
if (currentCount !== "1") {
var newCount = currentCount - 1;
minExtra.parentElement.querySelector('.count').innerText = newCount;
}
var colorMin = minExtra.parentElement.querySelector('.extra-min');
if (newCount === 1) {
colorMin.setAttribute("class", "change-extra min extra-min");
}
var extraPrice = minExtra.parentElement.querySelector('.extraPrice').innerText;
var extraPriceValue = extraPrice * newCount;
calculateMin(extraPriceValue);
})
});
Array.prototype.forEach.call(plusExtras, function (plusExtra) {
plusExtra.addEventListener('click', function() {
var currentCount = plusExtra.parentElement.querySelector('.count').innerText;
var newCount = +currentCount + +1;
plusExtra.parentElement.querySelector('.count').innerText = newCount;
var colorMin = plusExtra.parentElement.querySelector('.extra-min');
if (newCount !== 1) {
colorMin.setAttribute("class", "change-extra min extra-min color");
} else {
colorMin.setAttribute("class", "change-extra min extra-min");
}
var extraPrice = plusExtra.parentElement.querySelector('.extraPrice').innerText;
var extraPriceValue = extraPrice * newCount;
calculatePlus(extraPriceValue);
})
});
function calculatePlus(price) {
//function here
totalPrice.innerHTML = calculatedPrice;
}
function calculateMin(price) {
//function here
totalPrice.innerHTML = calculatedPrice;
}
.item {
margin-bottom: 10px;
width: 120px;
padding: 20px;
border: 1px solid #C5C5C5;
}
.show-extra-container {
display: -webkit-flex;
display: flex;
-justify-content: flex-start;
justify-content: flex-start;
-webkit-align-items: center;
align-items: center;
}
.extra-aantal {
padding: 0px 14px;
font-weight: 700;
font-size: 16px;
}
.change-extra {
font-weight: 700;
font-size: 28px;
color: #EC008C;
cursor: pointer;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.change-extra.min {
background-color: #C5C5C5;
width: 14px;
height: 3px;
margin-top: 3px;
border-radius: 4px;
}
.change-extra.min.color {
background-color: #EC008C;
}
#totalPrice {
font-weight: 700;
}
<div id="showextra1" class="item">
<label class="col-form-label">Aantal personen</label>
<div class="show-extra-container">
<span class="extraPrice" style="display: none;">10</span>
<div class="change-extra min extra-min"></div>
<div class="extra-aantal count">1</div>
<div class="change-extra plus extra-plus">+</div>
</div>
</div>
<div id="showextra2" class="item">
<label class="col-form-label">Aantal personen</label>
<div class="show-extra-container">
<span class="extraPrice" style="display: none;">5</span>
<div class="change-extra min extra-min"></div>
<div class="extra-aantal count">1</div>
<div class="change-extra plus extra-plus">+</div>
</div>
</div>
<div class="item">
<div id="totalPrice"></div>
</div>
So this code works pretty good for what I need. Now I just need to calculate a totalprice and set the innerHTML of totalPrice. I was thinking to use a seperate function calculateMin() and calculatePlus(). However I have no idea how to write this function that calculates the total. Does anyone know how to do this?
Try like this:
var minExtras = document.querySelectorAll(".extra-min");
var plusExtras = document.querySelectorAll(".extra-plus");
var totalPrice = document.getElementById("totalPrice");
totalPrice.innerHTML = "Totaalprijs: € ";
var calculatedPrice = 0;
//count initial price
Array.prototype.forEach.call(document.querySelectorAll('.show-extra-container'), function(container) {
calculatedPrice += parseInt(container.querySelector('.count').innerText) * parseFloat(container.querySelector('.extraPrice').innerText);
});
totalPrice.innerHTML = "Totaalprijs: € " + calculatedPrice;
Array.prototype.forEach.call(minExtras, function(minExtra) {
minExtra.addEventListener('click', function() {
var currentCount = minExtra.parentElement.querySelector('.count').innerText;
if (currentCount !== "1") {
var newCount = currentCount - 1;
minExtra.parentElement.querySelector('.count').innerText = newCount;
} else {
return;
};
var colorMin = minExtra.parentElement.querySelector('.extra-min');
if (newCount === 1) {
colorMin.setAttribute("class", "change-extra min extra-min");
}
var extraPrice = minExtra.parentElement.querySelector('.extraPrice').innerText;
//calculateMin: -1
calculatedPrice -= parseFloat(extraPrice);
totalPrice.innerHTML = "Totaalprijs: € " + calculatedPrice;
})
});
Array.prototype.forEach.call(plusExtras, function(plusExtra) {
plusExtra.addEventListener('click', function() {
var currentCount = plusExtra.parentElement.querySelector('.count').innerText;
var newCount = +currentCount + +1;
plusExtra.parentElement.querySelector('.count').innerText = newCount;
var colorMin = plusExtra.parentElement.querySelector('.extra-min');
if (newCount !== 1) {
colorMin.setAttribute("class", "change-extra min extra-min color");
} else {
colorMin.setAttribute("class", "change-extra min extra-min");
}
var extraPrice = plusExtra.parentElement.querySelector('.extraPrice').innerText;
//calculatePlus: +1
calculatedPrice += parseFloat(extraPrice);
totalPrice.innerHTML = "Totaalprijs: € " + calculatedPrice;
})
});
.item {
margin-bottom: 10px;
width: 120px;
padding: 20px;
border: 1px solid #C5C5C5;
}
.show-extra-container {
display: -webkit-flex;
display: flex;
-justify-content: flex-start;
justify-content: flex-start;
-webkit-align-items: center;
align-items: center;
}
.extra-aantal {
padding: 0px 14px;
font-weight: 700;
font-size: 16px;
}
.change-extra {
font-weight: 700;
font-size: 28px;
color: #EC008C;
cursor: pointer;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.change-extra.min {
background-color: #C5C5C5;
width: 14px;
height: 3px;
margin-top: 3px;
border-radius: 4px;
}
.change-extra.min.color {
background-color: #EC008C;
}
#totalPrice {
font-weight: 700;
}
<div id="showextra1" class="item">
<label class="col-form-label">Aantal personen</label>
<div class="show-extra-container">
<span class="extraPrice" style="display: none;">10</span>
<div class="change-extra min extra-min"></div>
<div class="extra-aantal count">1</div>
<div class="change-extra plus extra-plus">+</div>
</div>
</div>
<div id="showextra2" class="item">
<label class="col-form-label">Aantal personen</label>
<div class="show-extra-container">
<span class="extraPrice" style="display: none;">5</span>
<div class="change-extra min extra-min"></div>
<div class="extra-aantal count">1</div>
<div class="change-extra plus extra-plus">+</div>
</div>
</div>
<div class="item">
<div id="totalPrice"></div>
</div>
Calculate the initial calculatedPrice (.count * .extraPrice) for each item
When clicking - or +, simply add or remove one .extraPrice for that item
return from minExtra listener if the count is already at the minimum of 1.
Related
I have a series of randomized buttons and I want to know how to detect which button is clicked, and when it is clicked, it turned into a color classed in css corresponding to that randomized button.
var randomCharacter = '';
var button = document.getElementsByClassName("button");
//gets a random character
function getRandomCharacter(length) {
var randomResult = '';
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (var i = 0; i < length; i++) {
randomResult += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return randomResult;
}
//gives a random image displayed
function giveDifferentQuestionImg() {
randomCharacter = getRandomCharacter(1);
// console.log(randomCharacter); // test
document.canvas.src = "alphabetAndNum/" + randomCharacter + ".png";
// 3 different characters in 3 times
var possibleAns = [getRandomCharacter(1), getRandomCharacter(1), getRandomCharacter(1), randomCharacter];
shuffleArr(possibleAns);
document.getElementById("ans1").innerHTML = possibleAns[0];
document.getElementById("ans2").innerHTML = possibleAns[1];
document.getElementById("ans3").innerHTML = possibleAns[2];
document.getElementById("ans4").innerHTML = possibleAns[3];
// console.log(randomCharacter)
}
//shuffles array
function shuffleArr(array) {
var currentIndex = array.length,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]
];
}
return array;
}
//checks for if the user answer is correct
function selectAnswer(event) {
//need to know if character selected = randomcharacter var
const selectedButton = event.target.innerHTML;
if (selectedButton == randomCharacter) {
document.querySelector("button").classList.add("btn-correct")
document.getElementById("elemental").innerHTML = selectedButton
console.log(selectedButton, randomCharacter);
} else {
}
}
.imgButton {
text-align: center;
}
table {
margin-left: auto;
margin-right: auto;
}
.display-btn {
padding: 1.5rem;
border-radius: 2rem !important;
}
#answers-btns {
padding: 0;
margin: 0;
list-style: none;
display: flex;
align-items: center;
justify-content: center;
}
.btn-row {
width: 100%
}
#randomPhoto {
display: block;
margin-left: auto;
margin-right: auto;
}
.bodyQuiz {
padding: 0;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
.btn-grid {
display: grid;
grid-template-columns: repeat(4, auto);
gap: 10px;
margin: 20px 0;
}
.btn {
border-radius: 2rem !important;
outline: none;
}
.btn-correct {
background-color: rgb(35, 202, 35);
}
.btn-wrong {
background-color: rgb(226, 58, 58);
}
.start-btn,
.next-btn {
font-weight: bold;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
}
.hide {
display: none;
}
<div>
<img src="" name="canvas" id="randomPhoto" alt="handsign">
</div>
<div id="answer-buttons" class="btn-grid">
<button class="btn" id="ans1" onclick="selectAnswer(event)">1</button>
<button class="btn" id="ans2" onclick="selectAnswer(event)">2</button>
<button class="btn" id="ans3" onclick="selectAnswer(event)">3</button>
<button class="btn" id="ans4" onclick="selectAnswer(event)">4</button>
</div>
check this code.
<div id="answer-buttons" class="btn-grid">
<button class="btn" id="ans1" onclick="buttonFunction(this)" value="red">1</button>
<button class="btn" id="ans2" onclick="buttonFunction(this)" value="yellow">2</button>
<button class="btn" id="ans3" onclick="buttonFunction(this)" value="black">3</button>
<button class="btn" id="ans4" onclick="buttonFunction(this)" value="green">4</button>
</div>
</div>
<div style = "width: 100px; height: 100px; border: 1px solid black" id ="gridContent">
</div>
<script>
function buttonFunction(el) {
var gridContent = document.getElementById("gridContent")
gridContent.style.backgroundColor = el.value
}
I've been doing a little game lately. You have two buttons with which you can earn money, One has a timer and when the timer is over you can click on it and you get +$500 in your account.
With the other button you can earn money by clicking on it. With every click you get +1$ in your account.
Now I wanted to make a button that doubles the money. For this I made a third button that says "Enable me".
I wanted to make a function that enables the button when your account is over $100. Do you know how I could do it?
Here is my code:
$(".addOneButton").click(function() {
setCounter(getCounter() + 1);
});
function myFunction(){
$(".addOneButton").click(function() {
setCounter(getCounter() + 1);
});
}
function hideUpgradeButton() {
document.getElementById("upgrade").style.display = "none";
}
$('#btn').prop('disabled',true);
startCountDown();
function getCounter(){
return parseInt($('#counter').html());
}
function setCounter(count) {
$('#counter').html(count);
}
$("#btn").click(function() {
setCounter(getCounter() + 500);
$('#btn').prop('disabled',true);
startCountDown();
});
function startCountDown() {
var counter = document.getElementById("counter")
var minutes = 1,
seconds = 30;
$("#countdown").html(minutes + ":" + seconds);
var count = setInterval(function() {
if (parseInt(minutes) < 0 || parseInt(seconds) <=0 ) {
$("#countdown").html("Collect" + " <i class=\"bi bi-unlock-fill\"></i>");
clearInterval(count);
$('#btn').prop('disabled',false);
} else {
$("#countdown").html(minutes + ":" + seconds + " <i class=\"bi bi-lock-fill\"></i>");
seconds--;
if (seconds < 10) seconds = "0" + seconds;
}
if(parseInt(minutes) === 1 && parseInt(seconds) === 0) {
minutes = 0;
seconds = 60
}
if(parseInt(counter) > 500){
$("#upgrade").prop('disabled', false);
}
}, 1000);
}
* {
font-family: 'Roboto', sans-serif;
}
#total {
display: flex;
justify-content: center;
margin: 0 auto;
width: auto;
}
#border {
border: 1px solid grey;
padding: 0.2em 1em 0.2em 1em;
}
.inline {
display: inline-block;
}
/*
Timer Button Start
*/
button:disabled {
font-size: 25px;
width: 200px;
padding: 0.2em;
background: #f54532;
color: #d0d0d0;
border: none;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
filter: grayscale(30%);
}
button {
font-size: 25px;
width: 200px;
padding: 0.2em;
background: #f54532;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
margin: 0.5em;
user-select: none;
}
button:enabled:hover {
transform: scale(1.1);
}
button:enabled:active {
transform: scale(0.9);
filter: brightness(70%);
}
/*
Timer Button End
*/
.bi {
font-size: 20px;
display: inline-flex;
align-items: center;
}
#centerButtons {
display: flex;
justify-content: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Roboto&subset=latin,greek,greek-ext,latin-ext,cyrillic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.3.0/font/bootstrap-icons.css">
<div id="total">
<div id="border">
<div class="inline" id="counter">0</div>
<div class="inline" id="money">$</div>
</div>
</div><br>
<div id="centerButtons">
<button id="btn">
<span id="countdown">Collect</span>
</button>
<button class="addOneButton" onclick="setCounter()">Add One!</button>
</div>
<button id="upgrade" onclick="myFunction(); hideUpgradeButton()" disabled>Enable me!</button>
Set a variable that holds the increment value , and a var that make sur you enable the button double once ,
then check if counter > 100 && wasn't enabled yet then increment the increment using the function myFunction()
PS I've edited timer value : to go fast with testing :
see below, snippet :
var increment = 1;
var enabled = false;
$(".addOneButton").click(function() {
var count = getCounter();
if(count > 100 && !enabled) {
$("#upgrade").prop('disabled', false)
enabled = true;
}
setCounter(count + increment);
});
function myFunction() {
increment++;
}
function hideUpgradeButton() {
document.getElementById("upgrade").style.display = "none";
}
$('#btn').prop('disabled', true);
startCountDown();
function getCounter() {
return parseInt($('#counter').html());
}
function setCounter(count) {
$('#counter').html(count);
}
$("#btn").click(function() {
setCounter(getCounter() + 500);
$('#btn').prop('disabled', true);
startCountDown();
});
function startCountDown() {
var counter = document.getElementById("counter")
var minutes = 0,
seconds = 10
$("#countdown").html(minutes + ":" + seconds);
var count = setInterval(function() {
if (parseInt(minutes) < 0 || parseInt(seconds) <= 0) {
$("#countdown").html("Collect" + " <i class=\"bi bi-unlock-fill\"></i>");
clearInterval(count);
$('#btn').prop('disabled', false);
} else {
$("#countdown").html(minutes + ":" + seconds + " <i class=\"bi bi-lock-fill\"></i>");
seconds--;
if (seconds < 10) seconds = "0" + seconds;
}
if (parseInt(minutes) === 1 && parseInt(seconds) === 0) {
minutes = 0;
seconds = 60
}
if (parseInt(counter) > 500) {
$("#upgrade").prop('disabled', false);
}
}, 1000);
}
* {
font-family: 'Roboto', sans-serif;
}
#total {
display: flex;
justify-content: center;
margin: 0 auto;
width: auto;
}
#border {
border: 1px solid grey;
padding: 0.2em 1em 0.2em 1em;
}
.inline {
display: inline-block;
}
/*
Timer Button Start
*/
button:disabled {
font-size: 25px;
width: 200px;
padding: 0.2em;
background: #f54532;
color: #d0d0d0;
border: none;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
filter: grayscale(30%);
}
button {
font-size: 25px;
width: 200px;
padding: 0.2em;
background: #f54532;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
margin: 0.5em;
user-select: none;
}
button:enabled:hover {
transform: scale(1.1);
}
button:enabled:active {
transform: scale(0.9);
filter: brightness(70%);
}
/*
Timer Button End
*/
.bi {
font-size: 20px;
display: inline-flex;
align-items: center;
}
#centerButtons {
display: flex;
justify-content: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Roboto&subset=latin,greek,greek-ext,latin-ext,cyrillic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.3.0/font/bootstrap-icons.css">
<div id="total">
<div id="border">
<div class="inline" id="counter">0</div>
<div class="inline" id="money">$</div>
</div>
</div><br>
<div id="centerButtons">
<button id="btn">
<span id="countdown">Collect</span>
</button>
<button class="addOneButton" onclick="setCounter()">Add One!</button>
</div>
<button id="upgrade" onclick="myFunction(); hideUpgradeButton()" disabled>Enable me!</button>
change getCounter:
function getCounter(){
var counter = parseInt($('#counter').html());
if(parseInt(counter) > 99){
$("#upgrade").prop('disabled', false);
}
return counter;
}
I am making a Simple Calculator App using vanilla JS and I have a div element to display the numbers. But when the textContent within it exceeds its width it starts looking ugly. I want to make the font-size smaller so that its always within the width.
This is how I am adding numbers to my display div
for(let i=0; i<numbers.length; i++){
numbers[i].addEventListener("click", function(){
display.textContent += this.textContent;
})
}
When the textContent is less it looks good
But when the textContent exceeds the width it looks bad
The original code here.
//Selectors
let numbers = document.querySelectorAll(".numbers")
let operators = document.querySelectorAll(".operators")
let equalto = document.querySelector(".equalto")
let clear = document.querySelector(".clear")
let backspace = document.querySelector(".backspace")
let plusMinus = document.querySelector(".plus-minus")
let dot = document.querySelector(".dot")
let display = document.querySelector(".display")
let output = document.querySelector(".output")
let equaltoPressed = false;
//Event Listeners
for(let i=0; i<numbers.length; i++){
numbers[i].addEventListener("click", function(){
if (equaltoPressed){
display.textContent = "";
equaltoPressed = false;
}
//if condition so that if the display has "Infinity" on it, we don't append digits
if ("0123456789.+-×÷".includes(display.textContent[display.textContent.length-1]) || display.textContent == "")
display.textContent += this.textContent;
evaluate();
})
}
for(let i=0; i<operators.length; i++){
operators[i].addEventListener("click", function(){
equaltoPressed = false;
if ("+-×÷".includes(display.textContent[display.textContent.length-1]))
display.textContent = display.textContent.substring(0,display.textContent.length-1) + this.textContent;
else
display.textContent += this.textContent;
})
}
equalto.addEventListener("click", function(){
if (output.textContent !== ""){
display.textContent = output.textContent;
output.textContent = "";
equaltoPressed = true;
}
});
clear.addEventListener("click", function(){
equaltoPressed = false;
display.textContent = "";
output.textContent = "";
})
backspace.addEventListener("click", function(){
equaltoPressed = false;
display.textContent = display.textContent.substr(0,display.textContent.length-1);
evaluate();
})
plusMinus.addEventListener("click", function(){
equaltoPressed = false;
let expression = display.textContent;
let flag = true;
for (let i=expression.length-1; i>=0; i--){
if ("+-×÷".includes(expression[i])){
if (expression[i] !== "-")
expression = expression.substring(0,i+1) + "-" + expression.substring(i+1,expression.length);
flag = false;
break;
}
}
if (flag)
expression = "-"+expression;
display.textContent = expression;
evaluate();
})
dot.addEventListener("click", function(){
if (equaltoPressed)
display.textContent = "";
let start = 0;
for (let i=display.textContent.length-1; i>=0; i--){
if("+-×÷".includes(display.textContent[i])){
start = i+1;
break;
}
}
if (!display.textContent.substring(start,display.textContent.length).includes("."))
display.textContent += ".";
})
//Functions
function evaluate(){
let expression = display.textContent;
for (let i=0; i<expression.length; i++){
if (expression[i] === "×")
expression = expression.substring(0,i) + "*" + expression.substring(i+1,expression.length);
if (expression[i] === "÷")
expression = expression.substring(0,i) + "/" + expression.substring(i+1,expression.length);
}
if("0123456789.".includes(expression[expression.length-1]) && eval(expression) != expression)
output.textContent = eval(expression);
else
output.textContent = "";
}
*{
border:0;
margin:0;
}
body{
min-height: 100vh;
background: black;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: arial;
font-size: 1.5rem;
color: #f8f8f8;
}
.row{
display: flex;
}
.light-grey{
display: flex;
align-items: center;
justify-content: center;
background-color: #a6a6a6;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
color: black;
}
.dark-grey{
display: flex;
align-items: center;
justify-content: center;
background-color: #333333;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
}
.yellow{
display: flex;
align-items: center;
justify-content: center;
background-color: #ff9501;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
}
#zero{
width: 9.2rem;
border-radius:0 50px 50px 0;
border-top-right-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
border-top-left-radius: 50px;
}
.display{
width: 19.2rem;
height: 3rem;
margin: 0 .4rem 2rem .4rem;
text-align: right;
font-size: 3rem;
}
.output{
width: 19.2rem;
height: 2rem;
margin: 0 .4rem 2rem .4rem;
text-align: right;
font-size: 2rem;
}
i{
font-size: 1.3rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<div class="display"></div>
<div class="output"></div>
<div class="row">
<div class="light-grey clear">AC</div>
<div class="light-grey plus-minus">+/-</div>
<div class="light-grey operators">÷</div>
<div class="yellow backspace"><i class="fas fa-backspace"></i></div>
</div>
<div class="row">
<div class="dark-grey numbers">7</div>
<div class="dark-grey numbers">8</div>
<div class="dark-grey numbers">9</div>
<div class="yellow operators">×</div>
</div>
<div class="row">
<div class="dark-grey numbers">4</div>
<div class="dark-grey numbers">5</div>
<div class="dark-grey numbers">6</div>
<div class="yellow operators">-</div>
</div>
<div class="row">
<div class="dark-grey numbers">1</div>
<div class="dark-grey numbers">2</div>
<div class="dark-grey numbers">3</div>
<div class="yellow operators">+</div>
</div>
<div class="row">
<div class="dark-grey numbers" id="zero">0</div>
<div class="dark-grey dot">.</div>
<div class="yellow equalto">=</div>
</div>
Here, better view it on a Full page. Added a cover elements .display-font and .display-cover around .display, fixed their styles a little.
Then added a construction with observer on the end of your JS, which is looking for changing in .display, and then changes the font-size: n% of the .display-cover depends on the size of a .display-font. Here we need .display-font with a fixed value of font-size. Although we can't change font-size of .display, because we are observing the changes in it and font-size changes causes a circle error.
But everything works fine with 2 cover blocks. Look please.
UPDATED
Add everything above to .output block, missed it on the first try.
UPDATE 2
Added white-space: nowrap; to .display-cover and .output-cover to prevent jumping the text to the next line.
Add a lot of comments and explanations to the part of the code, where the changes in .display and .output are observing.
More about MutationObserver. It is not a quantum mechanics or a high level programming. Used it yesterday for the first time, to be honest.
More about while statement. Used it to check if the .display and .output width.
About rounding bug in your comment (when you are using a ling digits, more than 17 symbols).
This bug can be repeated in your original code.
This bug can be repeated in every online calculator i ever found. If you set 100000000000000000+68 - you will get 100000000000000060. This is some Javascript rounding feature. You can test it here https://www.jqueryscript.net/demo/Calculator-App-jQuery-Bootstrap/.
//Selectors
let numbers = document.querySelectorAll(".numbers")
let operators = document.querySelectorAll(".operators")
let equalto = document.querySelector(".equalto")
let clear = document.querySelector(".clear")
let backspace = document.querySelector(".backspace")
let plusMinus = document.querySelector(".plus-minus")
let dot = document.querySelector(".dot")
let display = document.querySelector(".display")
let output = document.querySelector(".output")
let equaltoPressed = false;
//Event Listeners
for (let i = 0; i < numbers.length; i++) {
numbers[i].addEventListener("click", function() {
if (equaltoPressed) {
display.textContent = "";
equaltoPressed = false;
}
//if condition so that if the display has "Infinity" on it, we don't append digits
if ("0123456789.+-×÷".includes(display.textContent[display.textContent.length - 1]) || display.textContent == "")
display.textContent += this.textContent;
evaluate();
})
}
for (let i = 0; i < operators.length; i++) {
operators[i].addEventListener("click", function() {
equaltoPressed = false;
if ("+-×÷".includes(display.textContent[display.textContent.length - 1]))
display.textContent = display.textContent.substring(0, display.textContent.length - 1) + this.textContent;
else
display.textContent += this.textContent;
})
}
equalto.addEventListener("click", function() {
if (output.textContent !== "") {
display.textContent = output.textContent;
output.textContent = "";
equaltoPressed = true;
}
});
clear.addEventListener("click", function() {
equaltoPressed = false;
display.textContent = "";
output.textContent = "";
})
backspace.addEventListener("click", function() {
equaltoPressed = false;
display.textContent = display.textContent.substr(0, display.textContent.length - 1);
evaluate();
})
plusMinus.addEventListener("click", function() {
equaltoPressed = false;
let expression = display.textContent;
let flag = true;
for (let i = expression.length - 1; i >= 0; i--) {
if ("+-×÷".includes(expression[i])) {
if (expression[i] !== "-")
expression = expression.substring(0, i + 1) + "-" + expression.substring(i + 1, expression.length);
flag = false;
break;
}
}
if (flag)
expression = "-" + expression;
display.textContent = expression;
evaluate();
})
dot.addEventListener("click", function() {
if (equaltoPressed)
display.textContent = "";
let start = 0;
for (let i = display.textContent.length - 1; i >= 0; i--) {
if ("+-×÷".includes(display.textContent[i])) {
start = i + 1;
break;
}
}
if (!display.textContent.substring(start, display.textContent.length).includes("."))
display.textContent += ".";
})
//Functions
function evaluate() {
let expression = display.textContent;
for (let i = 0; i < expression.length; i++) {
if (expression[i] === "×")
expression = expression.substring(0, i) + "*" + expression.substring(i + 1, expression.length);
if (expression[i] === "÷")
expression = expression.substring(0, i) + "/" + expression.substring(i + 1, expression.length);
}
if ("0123456789.".includes(expression[expression.length - 1]) && eval(expression) != expression)
output.textContent = eval(expression);
else
output.textContent = "";
}
// .display and .output font resizing changing addon
let displayCover = document.querySelector(".display-cover");
let outputCover = document.querySelector(".output-cover");
let displaySize;
let outputSize;
// This part is a config of MutationObserver, options for the observer (which mutations to observe)
let config = {
attributes: true,
childList: true,
characterData: true
};
// Defining new class of the object MutationObserver, which provides the ability to watch for changes being made to the DOM tree. Basically - this class will be executed on every observing changes in '.display'.
let displayObserver = new MutationObserver(function(mutations) {
displaySize = 100; // we will use it like a base 100% font-size later
// set font size to '.display-cover'
displayCover.style.cssText = 'font-size:' + displaySize + '%';
// 'while' statement creates a loop that executes a specified statement as long as the test condition evaluates to true. The condition is evaluated before executing the statement. Each time we set font size -1 - the width of '.display' changes, and we keeping this loop until the width of '.display' will be not more then it's parent '.display-cover'
while(display.offsetWidth > displayCover.offsetWidth) {
displaySize = displaySize - 1; //decrements font size by 1% each loop
displayCover.style.cssText = 'font-size:' + displaySize + '%';
// basically in DOM you see only result of the very last loop of this 'while' statment
}
});
// Start observing the target node (your '.display') for configured mutations (using config)
displayObserver.observe(display, config);
//----------
// everything the same as above, but for '.output'
let outputObserver = new MutationObserver(function(mutations) {
outputSize = 100;
outputCover.style.cssText = 'font-size:' + outputSize + '%';
while(output.offsetWidth > outputCover.offsetWidth) {
outputSize = outputSize - 1;
outputCover.style.cssText = 'font-size:' + outputSize + '%';
}
});
// as you can see, 'config' is the same
outputObserver.observe(output, config);
* {
border: 0;
margin: 0;
}
body {
min-height: 100vh;
background: black;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: arial;
font-size: 1.5rem;
color: #f8f8f8;
}
.row {
display: flex;
}
.light-grey {
display: flex;
align-items: center;
justify-content: center;
background-color: #a6a6a6;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
color: black;
}
.dark-grey {
display: flex;
align-items: center;
justify-content: center;
background-color: #333333;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
}
.yellow {
display: flex;
align-items: center;
justify-content: center;
background-color: #ff9501;
height: 4.2rem;
width: 4.2rem;
border-radius: 50%;
margin: .4rem;
cursor: pointer;
}
#zero {
width: 9.2rem;
border-radius: 0 50px 50px 0;
border-top-right-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
border-top-left-radius: 50px;
}
.display-font {
font-size: 3rem;
}
.display-cover {
width: 19.2rem;
height: 3rem;
margin: 0 .4rem 2rem .4rem;
text-align: right;
font-size: 100%;
white-space: nowrap;
}
.display {
font-size: 100%;
display: inline-block;
}
.output-font {
font-size: 2rem;
}
.output-cover {
width: 19.2rem;
height: 2rem;
margin: 0 .4rem 2rem .4rem;
text-align: right;
font-size: 100%;
white-space: nowrap;
}
.output {
font-size: 100%;
display: inline-block;
}
i {
font-size: 1.3rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<div class="display-font">
<div class="display-cover">
<div class="display"></div>
</div>
</div>
<div class="output-font">
<div class="output-cover">
<div class="output"></div>
</div>
</div>
<div class="row">
<div class="light-grey clear">AC</div>
<div class="light-grey plus-minus">+/-</div>
<div class="light-grey operators">÷</div>
<div class="yellow backspace"><i class="fas fa-backspace"></i></div>
</div>
<div class="row">
<div class="dark-grey numbers">7</div>
<div class="dark-grey numbers">8</div>
<div class="dark-grey numbers">9</div>
<div class="yellow operators">×</div>
</div>
<div class="row">
<div class="dark-grey numbers">4</div>
<div class="dark-grey numbers">5</div>
<div class="dark-grey numbers">6</div>
<div class="yellow operators">-</div>
</div>
<div class="row">
<div class="dark-grey numbers">1</div>
<div class="dark-grey numbers">2</div>
<div class="dark-grey numbers">3</div>
<div class="yellow operators">+</div>
</div>
<div class="row">
<div class="dark-grey numbers" id="zero">0</div>
<div class="dark-grey dot">.</div>
<div class="yellow equalto">=</div>
</div>
I have got a problem at the coding project I am doing, as I formatted the numbers to be shown nicer, I ran into a problem. The webpage when it loads shows NaN. undefined in the total income/expenses at the top. I can't figure out what is the problem.
//Budget controller
var budgetController = (function() {
var Expense = function(id, description, value) {
this.id = id;
this.description = description;
this.value = value;
this.percentage = -1;
};
Expense.prototype.calcPercentage = function(totalIncome) {
if (totalIncome > 0) {
this.percentage = Math.round((this.value / totalIncome) * 100);
} else {
this.percentage = -1;
}
};
Expense.prototype.getPercentage = function() {
return this.percentage;
};
var Income = function(id, description, value) {
this.id = id;
this.description = description;
this.value = value;
};
var calculateTotal = function(type) {
var sum = 0;
data.allItems[type].forEach(function(cur) {
sum = sum + cur.value;
});
data.totals[type] = sum;
};
var data = {
allItems: {
exp: [],
inc: []
},
totals: {
exp: 0,
inc: 0
},
budget: 0,
percentage: -1
};
return {
addItem: function(type, des, val) {
var newItem, ID;
//create new iD
if (data.allItems[type].length > 0) {
ID = data.allItems[type][data.allItems[type].length - 1].id + 1;
} else {
ID = 0;
}
//CREATe new item, if it is inc or exp
if (type === 'exp') {
newItem = new Expense(ID, des, val);
} else if (type === 'inc') {
newItem = new Income(ID, des, val);
}
// Push all items into data structure and return the new element
data.allItems[type].push(newItem);
return newItem;
},
deleteItem: function(type, id) {
var ids, index;
ids = data.allItems[type].map(function(current) {
return current.id;
});
index = ids.indexOf(id);
if (index !== -1) {
data.allItems[type].splice(index, 1);
}
},
calculateBudget: function() {
// calculate the total income and expenses
calculateTotal('exp');
calculateTotal('inc');
// calculate the budget: income - expenses
data.budget = data.totals.inc - data.totals.exp;
if (data.totals.inc > 0) {
data.percentage = Math.round((data.totals.exp / data.totals.inc) * 100);
} else {
data.percentage = -1;
}
},
calculatePercentages: function() {
data.allItems.exp.forEach(function(cur) {
cur.calcPercentage(data.totals.inc);
});
},
getPercentages: function() {
var allPerc = data.allItems.exp.map(function(cur) {
return cur.getPercentage();
});
return allPerc;
},
getBudget: function() {
return {
budget: data.budget,
totalInc: data.totals.inc,
totalExp: data.totals.exp,
percentage: data.percentage
};
}
};
})();
// UI Controller
var UIController = (function() {
var DOMstrings = {
inputType: '.add__type',
inputDescription: '.add__description',
inputValue: '.add__value',
inputBtn: '.add__btn',
incomeContainer: '.income__list',
expensesContainer: '.expenses__list',
budgetLabel: '.budget__value',
incomeLabel: '.budget__income--value',
expensesLabel: '.budget__expenses--value',
percentageLabel: '.budget__expenses--percentage',
container: '.container',
expensesPercLabel: '.item__percentage',
dateLabel: '.budget__title--month'
};
var formatNumber = function(num, type) {
var numSplit, int, dec, type;
/* + or - befofe a number
on 2 decimals
comma seperating thousands
*/
num = Math.abs(num);
num = num.toFixed(2);
numSplit = num.split('.');
int = numSplit[0];
if (int.length > 3) {
int = int.substr(0, int.length - 3) + ',' + int.substr(int.length - 3, 3); //input 23510, output 23,510
}
dec = numSplit[1];
return (type === 'exp' ? '-' : '+') + ' ' + int + '.' + dec;
};
var nodeListForEach = function(list, callback) {
for (var i = 0; i < list.length; i++) {
callback(list[i], i);
}
};
return {
getInput: function() {
return {
type: document.querySelector(DOMstrings.inputType).value, //will be either inc or exp
description: document.querySelector(DOMstrings.inputDescription).value,
value: parseFloat(document.querySelector(DOMstrings.inputValue).value)
};
},
addListItem: function(obj, type) {
var html, newHtml, element;
// Create HTML string with placeholder text
if (type === 'inc') {
element = DOMstrings.incomeContainer;
html = '<div class="item clearfix" id="inc-%id%"> <div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
} else if (type === 'exp') {
element = DOMstrings.expensesContainer;
html = '<div class="item clearfix" id="exp-%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__percentage">21%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
}
// Replace the placeholder text with some actual data
newHtml = html.replace('%id%', obj.id);
newHtml = newHtml.replace('%description%', obj.description);
newHtml = newHtml.replace('%value%', formatNumber(obj.value, type));
// Insert the HTML into the DOM
document.querySelector(element).insertAdjacentHTML('beforeend', newHtml);
},
deleteListItem: function(selectorID) {
var el = document.getElementById(selectorID);
el.parentNode.removeChild(el);
},
clearFields: function() {
var fields, fieldsArr;
fields = document.querySelectorAll(
DOMstrings.inputDescription + ',' + DOMstrings.inputValue
);
fieldsArr = Array.prototype.slice.call(fields);
fieldsArr.forEach(function(current, index, array) {
current.value = "";
});
fieldsArr[0].focus();
},
displayBudget: function(obj) {
var type;
obj.budget > 0 ? type = 'inc' : type = 'exp';
document.querySelector(DOMstrings.budgetLabel).textContent = formatNumber(obj.budget, type);
document.querySelector(DOMstrings.incomeLabel).textContent = formatNumber(obj.totalInc, 'inc');
document.querySelector(DOMstrings.expensesLabel).textContent = formatNumber(obj.totalExp, 'exp');
if (obj.percentage > 0) {
document.querySelector(DOMstrings.percentageLabel).textContent = obj.percentage + '%';
} else {
document.querySelector(DOMstrings.percentageLabel).textContent = '---';
}
},
displayPercentages: function(percentages) {
var fields = document.querySelectorAll(DOMstrings.expensesPercLabel);
nodeListForEach(fields, function(current, index) {
if (percentages[index] > 0) {
current.textContent = percentages[index] + '%';
} else {
current.textContent = '---';
}
});
},
getDOMstrings: function() {
return DOMstrings;
}
};
})();
// App Controller - global
var controller = (function(budgetCtrl, UICtrl) {
var setupEventListeners = function() {
var DOM = UICtrl.getDOMstrings();
document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
document.addEventListener('keypress', function(event) {
if (event.keyCode === 13 || event.which === 13) {
ctrlAddItem();
}
});
document.querySelector(DOM.container).addEventListener('click', ctrlDeleteItem);
};
var updateBudget = function() {
// 1. Calculate the budget
budgetCtrl.calculateBudget();
// 2. Return the budget
var budget = budgetCtrl.getBudget();
// 3. Display the budget on the UI
UICtrl.displayBudget(budget);
};
var updatePercentages = function() {
// 1. Calculate percentages
budgetCtrl.calculatePercentages();
// 2. Read percentages from the budget controller
var percentages = budgetCtrl.getPercentages();
// 3. Update the UI with the new percentages
UICtrl.displayPercentages(percentages);
};
var ctrlAddItem = function() {
var input, newItem;
// 1. Get the field input data
input = UICtrl.getInput();
if (input.description !== "" && !isNaN(input.value) && input.value > 0) {
// 2. Add the item to the budget controller
newItem = budgetCtrl.addItem(input.type, input.description, input.value);
// 3. Add the item to the UI
UICtrl.addListItem(newItem, input.type);
// 4. Clear the fields
UICtrl.clearFields();
// 5. Calculate and update budget
updateBudget();
// 6. Calculate and update percentages
updatePercentages();
}
};
var ctrlDeleteItem = function(event) {
var itemID, splitID, type, ID;
itemID = event.target.parentNode.parentNode.parentNode.parentNode.id;
if (itemID) {
//inc-1
splitID = itemID.split('-');
type = splitID[0];
ID = parseInt(splitID[1]);
// 1. delete the item from the data structure
budgetCtrl.deleteItem(type, ID);
// 2. Delete the item from the UI
UICtrl.deleteListItem(itemID);
// 3. Update and show the new budget
updateBudget();
// 4. Calculate and update percentages
updatePercentages();
}
};
return {
init: function() {
console.log('App has started');
UICtrl.displayBudget({
budget: 0,
totalIncome: 0,
totalExpenses: 0,
percentage: -1
});
setupEventListeners();
}
};
})(budgetController, UIController);
controller.init();
/**********************************************
*** GENERAL
**********************************************/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.clearfix::after {
content: "";
display: table;
clear: both;
}
body {
color: #555;
font-family: Open Sans;
font-size: 16px;
position: relative;
height: 100vh;
font-weight: 400;
}
.right {
float: right;
}
.red {
color: #FF5049 !important;
}
.red-focus:focus {
border: 1px solid #FF5049 !important;
}
/**********************************************
*** TOP PART
**********************************************/
.top {
height: 40vh;
background-image: linear-gradient(rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)), url(back.png);
background-size: cover;
background-position: center;
position: relative;
}
.budget {
position: absolute;
width: 350px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
}
.budget__title {
font-size: 18px;
text-align: center;
margin-bottom: 10px;
font-weight: 300;
}
.budget__value {
font-weight: 300;
font-size: 46px;
text-align: center;
margin-bottom: 25px;
letter-spacing: 2px;
}
.budget__income,
.budget__expenses {
padding: 12px;
text-transform: uppercase;
}
.budget__income {
margin-bottom: 10px;
background-color: #28B9B5;
}
.budget__expenses {
background-color: #FF5049;
}
.budget__income--text,
.budget__expenses--text {
float: left;
font-size: 13px;
color: #444;
margin-top: 2px;
}
.budget__income--value,
.budget__expenses--value {
letter-spacing: 1px;
float: left;
}
.budget__income--percentage,
.budget__expenses--percentage {
float: left;
width: 34px;
font-size: 11px;
padding: 3px 0;
margin-left: 10px;
}
.budget__expenses--percentage {
background-color: rgba(255, 255, 255, 0.2);
text-align: center;
border-radius: 3px;
}
/**********************************************
*** BOTTOM PART
**********************************************/
/***** FORM *****/
.add {
padding: 14px;
border-bottom: 1px solid #e7e7e7;
background-color: #f7f7f7;
}
.add__container {
margin: 0 auto;
text-align: center;
}
.add__type {
width: 55px;
border: 1px solid #e7e7e7;
height: 44px;
font-size: 18px;
color: inherit;
background-color: #fff;
margin-right: 10px;
font-weight: 300;
transition: border 0.3s;
}
.add__description,
.add__value {
border: 1px solid #e7e7e7;
background-color: #fff;
color: inherit;
font-family: inherit;
font-size: 14px;
padding: 12px 15px;
margin-right: 10px;
border-radius: 5px;
transition: border 0.3s;
}
.add__description {
width: 400px;
}
.add__value {
width: 100px;
}
.add__btn {
font-size: 35px;
background: none;
border: none;
color: #28B9B5;
cursor: pointer;
display: inline-block;
vertical-align: middle;
line-height: 1.1;
margin-left: 10px;
}
.add__btn:active {
transform: translateY(2px);
}
.add__type:focus,
.add__description:focus,
.add__value:focus {
outline: none;
border: 1px solid #28B9B5;
}
.add__btn:focus {
outline: none;
}
/***** LISTS *****/
.container {
width: 1000px;
margin: 60px auto;
}
.income {
float: left;
width: 475px;
margin-right: 50px;
}
.expenses {
float: left;
width: 475px;
}
h2 {
text-transform: uppercase;
font-size: 18px;
font-weight: 400;
margin-bottom: 15px;
}
.icome__title {
color: #28B9B5;
}
.expenses__title {
color: #FF5049;
}
.item {
padding: 13px;
border-bottom: 1px solid #e7e7e7;
}
.item:first-child {
border-top: 1px solid #e7e7e7;
}
.item:nth-child(even) {
background-color: #f7f7f7;
}
.item__description {
float: left;
}
.item__value {
float: left;
transition: transform 0.3s;
}
.item__percentage {
float: left;
margin-left: 20px;
transition: transform 0.3s;
font-size: 11px;
background-color: #FFDAD9;
padding: 3px;
border-radius: 3px;
width: 32px;
text-align: center;
}
.income .item__value,
.income .item__delete--btn {
color: #28B9B5;
}
.expenses .item__value,
.expenses .item__percentage,
.expenses .item__delete--btn {
color: #FF5049;
}
.item__delete {
float: left;
}
.item__delete--btn {
font-size: 22px;
background: none;
border: none;
cursor: pointer;
display: inline-block;
vertical-align: middle;
line-height: 1;
display: none;
}
.item__delete--btn:focus {
outline: none;
}
.item__delete--btn:active {
transform: translateY(2px);
}
.item:hover .item__delete--btn {
display: block;
}
.item:hover .item__value {
transform: translateX(-20px);
}
.item:hover .item__percentage {
transform: translateX(-20px);
}
.unpaid {
background-color: #FFDAD9 !important;
cursor: pointer;
color: #FF5049;
}
.unpaid .item__percentage {
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1);
}
.unpaid:hover .item__description {
font-weight: 900;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:100,300,400,600" rel="stylesheet" type="text/css">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
<link type="text/css" rel="stylesheet" href="style.css">
<title>Budgety</title>
</head>
<body>
<div class="top">
<div class="budget">
<div class="budget__title">
Available Budget in <span class="budget__title--month">%Month%</span>:
</div>
<div class="budget__value">+ 2,345.64</div>
<div class="budget__income clearfix">
<div class="budget__income--text">Income</div>
<div class="right">
<div class="budget__income--value">+ 4,300.00</div>
<div class="budget__income--percentage"> </div>
</div>
</div>
<div class="budget__expenses clearfix">
<div class="budget__expenses--text">Expenses</div>
<div class="right clearfix">
<div class="budget__expenses--value">- 1,954.36</div>
<div class="budget__expenses--percentage">45%</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="add">
<div class="add__container">
<select class="add__type">
<option value="inc" selected>+</option>
<option value="exp">-</option>
</select>
<input type="text" class="add__description" placeholder="Add description">
<input type="number" class="add__value" placeholder="Value">
<button class="add__btn"><i class="ion-ios-checkmark-outline"></i></button>
</div>
</div>
<div class="container clearfix">
<div class="income">
<h2 class="icome__title">Income</h2>
<div class="income__list">
<!--
<div class="item clearfix" id="income-0">
<div class="item__description">Salary</div>
<div class="right clearfix">
<div class="item__value">+ 2,100.00</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
<div class="item clearfix" id="income-1">
<div class="item__description">Sold car</div>
<div class="right clearfix">
<div class="item__value">+ 1,500.00</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
-->
</div>
</div>
<div class="expenses">
<h2 class="expenses__title">Expenses</h2>
<div class="expenses__list">
<!--
<div class="item clearfix" id="expense-0">
<div class="item__description">Apartment rent</div>
<div class="right clearfix">
<div class="item__value">- 900.00</div>
<div class="item__percentage">21%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
<div class="item clearfix" id="expense-1">
<div class="item__description">Grocery shopping</div>
<div class="right clearfix">
<div class="item__value">- 435.28</div>
<div class="item__percentage">10%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
-->
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
The problem is that in the function formatNumber, the value num is not initialized at first time. For solving this, you can put a default value when the value of num is empty, like this:
var formatNumber = function(num = 0, type = '') {
var numSplit, int, dec, type;
/* + or - befofe a number
on 2 decimals
comma seperating thousands
*/
num = Math.abs(num);
num = num.toFixed(2);
numSplit = num.split('.');
int = numSplit[0];
if (int.length > 3) {
int = int.substr(0, int.length - 3) + ',' + int.substr(int.length - 3, 3); //input 23510, output 23,510
}
dec = numSplit[1];
return (type === 'exp' ? '-' : '+') + ' ' + int + '.' + dec;
};
I am a newbie in JS (level 0), and although I try to store all the variables of my practice in Local Storage, I am doing something wrong, because when reloading the page many functions (previously visible), now disappear in the reload.
SEE LIVE DEMO
Where is my errors...?
What am I doing wrong...?
Thanks in advance!
CSS
html{top:0;left:0;margin:0}body{top:0;margin:0;padding:0;color:#323232;width:100%;height:100%;line-height:1.5;font-family:'Roboto',serif}#container{width:500px;margin:0 auto}#container p{display:inline-block;margin-top:20px}#productos{display:none}.img-prod{display:inline-block;float:left;margin-right:10px}.img-prod img{width:100%;height:auto;max-width:70px;display:block;border:0}#comp-p1,#comp-p2,#comp-p3{width:120px;height:30px;margin-top:15px;background:green;padding:10px 0 5px;text-align:center;vertical-align:middle;color:#fff;cursor:pointer}.derecha{border:solid 1px #999;max-height:400px;width:350px;margin:0 auto;text-align:center;padding:10px 0;overflow-y:auto;float:right}#producto-1,#producto-2,#producto-3{display:inline-block;width:220px;padding:10px;float:left;text-align:left;font-size:.9em;margin-right:5px}#producto-1{background:green;color:#fff}#producto-2{background:#add8e6;color:#000}#producto-3{background:#666;color:#fff}.cont-p{display:inline-block;margin:7px auto;line-height:1}.bbp{display:inline-block;vertical-align:top;width:24px;height:24px;text-align:center;background:red;color:#fff;margin-left:5px;line-height:1.5;cursor:pointer}.cont-num{float:left;width:24px;height:24px;margin:20px 5px 0 20px;padding:4px 3px 3px;background:red;text-align:center;font-size:16px;font-family:Arial,sans-serif;color:#fff}#mostrar{display:none;width:100px;margin:70px 0 0;padding:10px;text-align:center;background:grey;color:#fff;cursor:pointer}.derecha input{width:0;height:0;border:none;visibility:hidden}#cont-resultado{text-align:center;width:110px;margin-top:70px;background:grey;padding:5px 10px 10px;color:#fff}#cont-resultado input{display:inline-block;margin:10px auto;background:#fff;color:#000;border:1px solid #000;padding:8px 0}#cont-resultado p{display:inline-block;text-decoration:none;color:#fff;background:grey;padding:8px 10px;cursor:pointer}#total{display:block;width:80px;text-align:center}
HTML
<div id="container">
<div id="productos">
<!-- ============================================== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"> </div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
<div id="cont-p2" class="cont-p">
<div id="producto-2">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p2 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
<div id="cont-p3" class="cont-p">
<div id="producto-3">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p3 cloned!<br><br>Input Value = 198</div>
<input class="add-prod" type="num" value="198">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
</div><!-- // productos -->
<div class="derecha"></div>
<div id="comp-p1" onClick="clickME();clickME2();">Clone 1</div>
<div id="comp-p2" onClick="clickME();clickME2();">Clone 2</div>
<div id="comp-p3" onClick="clickME();clickME2();">Clone 3</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<input name="total" id="total">
Total of sum
</div>
</div>
<!-- // container -->
<script>
// Script que suma y resta los clicks
var clicks = 0;
function clickME() {
clicks += 1;
document.getElementById("clicks").innerHTML = clicks
}
var clicksdos = 0;
function clickME2() {
clicksdos += 1;
document.getElementById("clicksdos").innerHTML = clicksdos;
if (clicksdos === 1) {
document.getElementById("cont-resultado").style.display = "block";
}
}
if (clicksdos === 0) {
document.getElementById("cont-resultado").style.display = "none";
}
function restar() {
if (clicks > 0) clicks -= 1;
document.getElementById("clicks").innerHTML = clicks;
}
function restardos() {
if (clicksdos > 0) clicksdos -= 1;
document.getElementById("clicksdos").innerHTML = clicksdos;
if (clicksdos === 0) {
document.getElementById("cont-resultado").style.display = "none";
}
}
</script>
SCRIPT
// Script for clone the div´s
$(document).ready(function() {
$("#comp-p1").click(function() {
$("#cont-p1").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
// =============
$("#comp-p2").click(function() {
$("#cont-p2").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
// =============
$("#comp-p3").click(function() {
$("#cont-p3").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
});
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// Deal with remove
document.addEventListener('click', (event) => {
let target = event.target;
if (target.matches('.bbp')) {
restar();
restardos();
getParent('.derecha', target).removeChild(target.parentNode);
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
}
})
// New Script for sum inputs
const displaySuma = () => document.getElementById("total").value = suma();
const suma = function() {
let x = Array.from(document.querySelectorAll(".derecha div .add-prod"));
let sum = 0;
for (var i = 0; i < x.length; i++) {
sum += parseFloat(x[i].value);
}
console.log(sum);
return sum;
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
}
//console.log(suma());
document.getElementById("total").value = suma();
// New
if ((localStorage.getItem("clicks")!=null) && (localStorage.getItem("clicksdos")!=null))
{
clicks=parseInt(localStorage.getItem("clicks"));
clicksdos=parseInt(localStorage.getItem("clicksdos"));
document.getElementById("clicks").innerHTML=clicks;
document.getElementById("clicksdos").innerHTML=clicksdos;
}
if (localStorage.getItem("html")!=null)
{
document.getElementsByClassName("derecha")[0].innerHTML=localStorage.getItem("html")
}
//});
Here is a version using localStorage unfortunately snippets does not allow use of localStorage.
Because of this, here is a version of the code running in jsFiddle
let clicks = 0;
let clicksdos = 0;
const safeInt = (key) => {
let value = parseInt(getValue(key));
return (isNaN(value) || value < 0) ? 0 : value;
}
// This loads our clicks from the LocalStorage
const loadClicks = () => {
clicks = safeInt('clicks');
clicksdos = safeInt('clicksdos');
}
const loadHTML = () => {
return getValue('html', '');
}
const loadFromStorage = () => {
let html = loadHTML();
if (html !== '') {
loadClicks();
}
displayClicks();
document.querySelector(".derecha").innerHTML = html;
}
// Display the clicks on the screen
const displayClicks = () => {
clicks = (clicks === NaN) ? 0 : clicks;
clicksdos = (clicksdos === NaN) ? 0 : clicksdos;
document.querySelector('#clicks').innerHTML = clicks;
document.querySelector('#clicksdos').innerHTML = clicksdos;
// Hide / Show Result
let display = (clicks > 0) ? 'block' : 'none';
document.querySelector('#cont-resultado').style.display = display;
}
const adjustClicks = (value) => {
clicks += value;
clicksdos += value;
storeValue('clicks', clicks);
storeValue('clicksdos', clicksdos);
displayClicks();
}
const addClick = () => adjustClicks(1);
const removeClick = () => adjustClicks(-1);
// Manage localStorage
const storeValue = (key, value) => (localStorage) ? localStorage.setItem(key, value) : '';
const getValue = (key, defaultValue) => (localStorage) ? localStorage.getItem(key) : defaultValue;
const storeHTML = () => storeValue("html", document.getElementsByClassName("derecha")[0].innerHTML);
// Add a node to the Derecha
const addToDerecha = (nodeId) => {
let node = document.querySelector(`#${nodeId}`);
document.querySelector('.derecha').appendChild(node.cloneNode(true));
storeHTML();
displaySuma();
};
// Monitor ALL click events
document.addEventListener('click', (event) => {
let target = event.target;
// Add
if (target.matches('.comp-clone')) {
addClick();
addToDerecha(event.target.dataset.clone);
}
// Remove
if (target.matches('.bbp')) {
removeClick();
getParent('.derecha', target).removeChild(target.parentNode);
storeHTML();
displaySuma();
}
});
// This is just a helper function.
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// New Script for sum inputs
const displaySuma = () => document.getElementById("total").value = suma();
const suma = function() {
return Array.from(document.querySelectorAll(".derecha div .add-prod"))
.reduce((a, v) => a + parseFloat(v.value), 0);
}
// Code to run when the document loads.
document.addEventListener('DOMContentLoaded', () => {
if (localStorage) {
loadFromStorage();
}
displaySuma();
});
body {
margin: 0 auto;
color: #323232;
width: 100%;
height: 100%;
line-height: 1.5;
font-family: 'Roboto', serif
}
#container {
width: 500px;
margin: 0 auto
}
#container p {
display: inline-block;
margin-top: 20px
}
span {
font-weight: bold;
text-decoration: underline
}
#productos {
display: none
}
.img-prod {
display: inline-block;
float: left;
background: #000;
margin-right: 10px
}
.img-prod img {
width: 100%;
height: auto;
max-width: 70px;
display: block;
border: 0
}
#comp-p1,
#comp-p2,
#comp-p3 {
width: 120px;
height: 30px;
margin-top: 15px;
background: green;
padding: 10px 0 5px;
text-align: center;
vertical-align: middle;
color: #fff;
cursor: pointer
}
.derecha {
border: solid 1px #999;
max-height: 400px;
width: 350px;
margin: 0 auto;
text-align: center;
padding: 10px 0;
overflow-y: auto;
float: right
}
#producto-1,
#producto-2,
#producto-3 {
display: inline-block;
width: 220px;
padding: 10px;
float: left;
text-align: left;
font-size: .9em;
margin-right: 5px
}
#producto-1 {
background: green;
color: #fff
}
#producto-2 {
background: #add8e6;
color: #000
}
#producto-3 {
background: #666;
color: #fff
}
.cont-p {
display: inline-block;
margin: 7px auto;
line-height: 1
}
.bbp {
display: inline-block;
float: right;
width: 24px;
height: 24px;
text-align: center;
background: red;
color: #fff;
margin-left: 5px;
line-height: 1.5;
cursor: pointer
}
.cont-num {
float: left;
width: 24px;
height: 24px;
margin: 20px 5px 0 18px;
padding: 4px 3px 3px;
background: red;
text-align: center;
font-size: 16px;
font-family: Arial, sans-serif;
color: #fff
}
#mostrar {
display: none
}
#mostrar {
width: 100px;
margin: 70px 0 0;
padding: 10px;
text-align: center;
background: grey;
color: #fff;
cursor: pointer
}
/* ==== Style of Sume ==== */
.derecha input {
width: 40px;
display: block;
margin: 0 auto 10px 0;
padding: 2px 0;
background: #f2f2f2;
border: none;
border: 1px solid #000;
text-align: center
}
#cont-resultado {
text-align: center;
width: 110px;
margin-top: 70px;
background: grey;
padding: 5px 10px 10px;
color: #fff
}
#cont-resultado input {
display: inline-block;
margin: 10px auto;
background: #fff;
color: #000;
border: 1px solid #000;
padding: 8px 0
}
#cont-resultado p {
display: inline-block;
text-decoration: none;
color: #fff;
background: grey;
padding: 8px 10px;
cursor: pointer
}
#total {
display: block;
width: 80px;
text-align: center
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="container">
<div id="productos">
<!-- =============== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"> </div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- =============== -->
<div id="cont-p2" class="cont-p">
<div id="producto-2">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p2 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- =============== -->
<div id="cont-p3" class="cont-p">
<div id="producto-3">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p3 cloned!<br><br>Input Value = 198</div>
<input class="add-prod" type="num" value="198">
<div class="bbp">X</div>
</div>
<!-- =============== -->
</div>
<!-- // productos -->
<div class="derecha"></div>
<div id="comp-p1" data-clone="cont-p1" class="comp-clone">Clone 1</div>
<div id="comp-p2" data-clone="cont-p2" class="comp-clone">Clone 2</div>
<div id="comp-p3" data-clone="cont-p3" class="comp-clone">Clone 3</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<span>RESULT:</span><br>
<input name="total" id="total">
<br>Is the sum of the cloned divs
<!--<p id='sumup'>Ver total</p>-->
</div>
<p><span>NOTE:</span><br>Here we are looking for only the cloned inputs can be sumed (and see the result in the box color gray).<br><br>The problem is that the current script does not apply a sume of the cloned inputs only... it adds ALL the inputs presents
in the html...<br><br>So (1), how do you sum only the cloned inputs, ignoring those that are outside...?<br><br>And (2) also, how to subtract from the total result all the cloned divs that are deleted...?</p>
</div>
<!-- // container -->
<script src="script.js"></script>
</body>
</html>