How to .appendChild() to all elements with the same class - javascript

The blue F turns into an actual amount of weight when you enter a number into the input field above.
The Two Functions Kg() and Lbs() are changing the class .dynamic which is where Kg or Lbs is being appended to the 8 divs that have the .dynamic class. Now that's exactly what isn't happening. When I select the divs with the .dynamic class, It adds Kg or Lbs (whatever button I press) to the first element with the .dynamic class.
How do I make it so that it appends that to all of the elements with the .dynamic class?
//Variables
const mercury = document.getElementById("mercury");
const venus = document.getElementById("venus");
const earth = document.getElementById("earth");
const mars = document.getElementById("mars");
const jupiter = document.getElementById("jupiter");
const saturn = document.getElementById("saturn");
const uranus = document.getElementById("uranus");
const neptune = document.getElementById("neptune");
const weight = document.getElementById("weight");
weight.addEventListener("input", Calc);
function Calc() {
if (weight.value > 99999) {
alert("Max Amount Of Numbers is 99999");
weight.value = "";
} else {
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mercury.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.9;
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1;
calculate = Math.round(calculate);
earth.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mars.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 2.36;
calculate = Math.round(calculate);
jupiter.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.92;
calculate = Math.round(calculate);
saturn.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.89;
calculate = Math.round(calculate);
uranus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1.12;
calculate = Math.round(calculate);
neptune.innerHTML = calculate;
console.log(calculate);
}
}
function lbs() {
let unit = document.getElementById("unit");
if (unit == null) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value");
newElement.setAttribute("id", "unit");
newElement.textContent = "Lbs";
document.querySelector(".dynamic").appendChild(newElement);
} else {
if (unit.innerHTML == "Kg") {
unit.innerHTML = "Lbs";
}
}
}
function kg() {
let unit = document.getElementById("unit");
if (unit == null) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value");
newElement.setAttribute("id", "unit");
newElement.textContent = "Kg";
document.querySelector(".dynamic").appendChild(newElement);
} else {
if (unit.innerHTML == "Lbs") {
unit.innerHTML = "Kg";
}
}
}
#import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
#font-face {
font-family: SpaceQuest;
src: url(https://raw.githubusercontent.com/Lemirq/WODP/master/Fonts/SpaceQuest-yOY3.woff);
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: url(./dario-bronnimann-hNQwIirOseE-unsplash.jpg);
}
/* Handle */
::-webkit-scrollbar-thumb {
background: rgba(59, 59, 59, 0.741);
border-radius: 200px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: rgb(255, 255, 255);
}
* {
--c-light: #f4f4f4;
--c-dark: #141414;
--c-blue: rgb(10, 132, 255);
--f-body: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
--trans-ease-in-out: all 0.2s ease-in-out;
color: var(--c-light);
}
body {
background-image: url(https://raw.githubusercontent.com/Lemirq/WODP/master/images/dario-bronnimann-hNQwIirOseE-unsplash.jpg);
margin: 0;
inset: 50px;
font-family: var(--f-body);
}
a {
color: var(--c-light);
text-decoration: none;
}
/***** NAVBAR *****/
.nav-item {
transition: var(--trans-ease-in-out);
}
.ext-link {
cursor: alias;
}
.nav-item:hover {
color: var(--c-dark);
background-color: var(--c-light);
}
.nav-item:hover li {
color: var(--c-dark);
}
.nav-item.icon-link:hover i {
color: var(--c-dark);
}
.nav-item:not(:last-child) {
margin-right: 20px;
}
navbar {
display: flex;
flex-direction: row;
justify-content: end;
align-items: center;
padding: 20px;
}
.nav-item-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
.nav-item {
display: inline-block;
list-style: none;
padding: 10px;
font-size: 20px;
border-radius: 10px;
}
.gh-icon {
font-size: 30px;
}
/***** End NAVBAR *****/
/***** Main *****/
#wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-family: SpaceQuest, sans-serif;
font-size: 3.5rem;
}
.input-group {
border: 2px var(--c-light) solid;
border-radius: 10px;
/* max-width: 400px; */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 50px;
}
[type="number"]:focus {
outline: none;
}
[type="number"] {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
font-family: var(--f-body);
min-width: 280px;
}
.btn-form {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
transition: var(--trans-ease-in-out);
cursor: pointer;
font-family: var(--f-body);
}
.btn-form:hover {
background-color: rgba(217, 217, 217, 0.4);
}
.btn-form:first {
border-right: var(--c-light) 1px solid;
}
.input-group-text {
background-color: rgba(217, 217, 217, 0.2);
font-size: 17px;
padding: 7px;
}
/***** End Main *****/
/***** CARDS *****/
.card-container {
margin: 50px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
align-items: center;
grid-gap: 10px;
width: calc(100vw - 200px);
}
.card {
background-color: #141414;
width: auto;
height: auto;
border-radius: 10px;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175);
}
.planet-info {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.planet {
font-size: 50px;
margin: 0;
text-transform: capitalize;
}
.planet-img {
width: 80px;
height: auto;
margin-right: 30px;
}
[src="./images/planets/Saturn.png"] {
height: 79.25px;
width: auto;
}
.weight {
margin-top: 10px;
text-transform: capitalize;
font-size: 20px;
}
.weight::after {
content: ":";
}
.divider {
height: 1px;
width: 100%;
margin: 20px 0;
background-color: var(--c-light);
}
.value {
font-size: 60px;
color: var(--c-blue);
}
.dynamic>.value:nth-child(2) {
margin-left: 10px;
}
.dynamic {
display: flex;
flex-direction: row;
justify-content: space-between;
}
/***** End CARDS *****/
.input-error {
outline: 1px solid red;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.8.3/font/bootstrap-icons.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<navbar>
<ul class="nav-item-container">
<a target="_blank" class="nav-item ext-link" href="https://lemirq.github.io">
<li>Website</li>
</a>
<a target="_blank" class="nav-item icon-link ext-link" href="https://github.com/Lemirq">
<li><i class="bi bi-github gh-icon"></i></li>
</a>
</ul>
</navbar>
<div id="wrapper">
<h1 id="vs-h1">Your Weight On Different Planets</h1>
<div class="input-group">
<input id="weight" placeholder="Enter your Weight" type="number">
<button id="kg" onclick="kg()" class="btn-form" type="button">Kg</button>
<button id="lbs" onclick="lbs()" class="btn-form" type="button">Lbs</button>
</div>
<div class="card-container">
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mercury.png" alt="EARTH">
<h3 class="planet">mercury</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mercury" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Venus.png" alt="EARTH">
<h3 class="planet">venus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="venus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Earth.png" alt="EARTH">
<h3 class="planet">earth</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="earth" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mars.png" alt="EARTH">
<h3 class="planet">mars</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mars" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Jupiter.png" alt="EARTH">
<h3 class="planet">jupiter</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="jupiter" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Saturn.png" alt="EARTH">
<h3 class="planet">saturn</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="saturn" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Uranus.png" alt="EARTH">
<h3 class="planet">uranus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="uranus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Neptune.png" alt="EARTH">
<h3 class="planet">neptune</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="neptune" class="value">F</h3>
</div>
</div>
</div>
</div>

Select all elements with the .dynamic class by using querySelectorAll
Iterate through the NodeList and append desired child to each node
let dynamics = document.querySelectorAll(".dynamic")
dynamics.forEach((ele) => {
let newElement = document.createElement("h3");
ele.appendChild(newElement);
}

Minor Problems
There's no such HTML element <navbar>, there's <nav>.
There's a block code hard-coded 8 times with the only difference between them is a number. Whenever code needs to repeat itself, we use some sort of iteration such as a for loop or an array method and the numbers would be passed as a single variable passed with every iteration.
const gforce = [0.38, 0.9, 1, 0.38, 2.36, 0.92, 0.89, 1.12];
for (let i=0; i < 8; i++) {
var val = weight.value;
console.log(val);
var calculate = val * gforce[i];//<== that should be a variable
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
}
Also, the <input> that's supposed to calculate lbs and kg doesn't appear to convert kg to lbs or vice-versa.
Details are commented in example
// An array of objects - each object represents a planet
const data = [{
planet: 'Mercury',
gforce: 0.38,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Mercury_in_true_color.jpg/440px-Mercury_in_true_color.jpg`
},
{
planet: 'Venus',
gforce: 0.9,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Venus_from_Mariner_10.jpg/440px-Venus_from_Mariner_10.jpg`
},
{
planet: 'Earth',
gforce: 1,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/The_Blue_Marble_%28remastered%29.jpg/440px-The_Blue_Marble_%28remastered%29.jpg`
},
{
planet: 'Mars',
gforce: 0.38,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/OSIRIS_Mars_true_color.jpg/440px-OSIRIS_Mars_true_color.jpg`
},
{
planet: 'Jupiter',
gforce: 2.36,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Jupiter_and_its_shrunken_Great_Red_Spot.jpg/440px-Jupiter_and_its_shrunken_Great_Red_Spot.jpg`
},
{
planet: 'Saturn',
gforce: 0.92,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/Saturn_during_Equinox.jpg/600px-Saturn_during_Equinox.jpg`
},
{
planet: 'Uranus',
gforce: 0.89,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Uranus_as_seen_by_NASA%27s_Voyager_2_%28remastered%29.png/440px-Uranus_as_seen_by_NASA%27s_Voyager_2_%28remastered%29.png`
},
{
planet: 'Neptune',
gforce: 1.12,
img: `https://upload.wikimedia.org/wikipedia/commons/thumb/6/63/Neptune_-_Voyager_2_%2829347980845%29_flatten_crop.jpg/440px-Neptune_-_Voyager_2_%2829347980845%29_flatten_crop.jpg`
}
];
// Reference <form> and all form controls
const conv = document.forms.converter;
const IO = conv.elements;
/*
Collect all tags with [name='planet'] and convert it into an array...
...iterate through array and define a htmlString and interpolate planet
name and <img> url...
...render htmlString into current <output>
*/
[...IO.planet].forEach((output, index) => {
const html = `
<h3>${data[index].planet}</h3>
<img src='${data[index].img}'>
<h4></h4>`;
output.insertAdjacentHTML('beforeend', html)
});
// Bind the "input" event to <form>
conv.oninput = IWC;
/*
Event handler passes Event Object by default
Reference all form controls
Reference the tag the user interacted with
If the user typed into [name='weight']...
...if that tag was also #lbs...
...#kg value is the value of >origin< times 0.45359237...
...otherwise #lbs value is the value of >origin< times 2.20462...
*/
/*
Collect all [name='planet'] into an array and iterate with .forEach()...
...clear out the last tag of current <output> (<h4>)...
...display the calculated totals for lbs. and kg of each planet
*/
function IWC(e) {
const IO = this.elements;
const origin = e.target;
if (origin.name === 'weight') {
if (origin.id === 'lbs') {
IO.kg.value = +origin.value * 0.45359237;
} else {
IO.lbs.value = +origin.value * 2.20462;
}
[...IO.planet].forEach((output, index) => {
output.lastElementChild.innerHTML = '';
output.lastElementChild.insertAdjacentHTML('beforeend',
`lbs. ${Math.round(data[index].gforce * IO.lbs.value)}<br>
kg ${Math.round(data[index].gforce * IO.kg.value)}`);
});
}
}
#import url('https://fonts.googleapis.com/css2?family=Oswald:wght#300&family=Raleway:wght#300&display=swap');
html {
font: 300 2ch/1 Oswald;
}
body {
width: 100%;
overflow-x: hidden;
background: #aaa
}
h1 {
color: gold
}
h1,
h2,
h3,
h4 {
margin: 0;
font-family: Raleway;
}
h3,
h4 {
position: absolute;
z-index: 1;
}
h3 {
margin: 0 auto;
}
h4 {
bottom: 0
}
form {
margin: 0 0 0 -15px;
}
fieldset {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
max-width: 90%;
margin: 15px
}
fieldset+fieldset {
background: #222
}
fieldset+fieldset legend {
color: gold
}
output {
position: relative;
display: inline-flex;
width: 24%;
min-height: 8rem;
margin-top: 15px;
padding: 2px;
background: black;
color: cyan
}
input {
font: inherit;
width: 10rem;
margin: 10px;
text-align: center;
}
img {
width: 100%;
}
<form id='converter'>
<fieldset>
<legend>
<h1>Interplanetary<br>Weight Converter</h1>
</legend>
<input id="lbs" name='weight' placeholder=" Weight in lbs." type="number" min='0' max='99999'><label for='lbs'>lbs.</label>
<input id="kg" name='weight' placeholder="Weight in kg." type="number" min='0' max='99999'><label for='kg'>kg</label>
</fieldset>
<fieldset>
<legend>
<h2>The Solar System</h2>
</legend>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
<output name='planet'></output>
</fieldset>
</form>

The script must look something like this:
<script>
let elements = document.querySelectorAll(".dynamic")
elements.forEach(el=>{
let child = document.createElement('div')
el.appendChild(child)
})
</script>

This can be simplified quite a bit, the buttons' innerHTML values match what you want in the new unit elements.
<button id="kg" class="btn-form" type="button">Kg</button>
<button id="lbs" class="btn-form" type="button">Lbs</button>
Use JavaScript to add an event listener to both unit buttons that call the same function which will update the innerHTML of the unit headings to match the clicked button. When initializing the unit heading elements you should not give them all the same id but rather a common class and you must also append a cloned copy of the element or it will just move the element form one spot to the next:
document.querySelectorAll('#kg, #lbs').forEach(ub => ub.addEventListener('click', units));
function units(e) {
let units = document.querySelectorAll(".value.unit");
if (units.length == 0) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value unit");
document.querySelectorAll(".dynamic").forEach((dyn) => {
// append a cloned copy to each, not the same newElement
dyn.appendChild(newElement.cloneNode())
});
// re-run the query to find the newly added nodes
units = document.querySelectorAll(".value.unit");
}
// set the content
units.forEach(unit => unit.innerHTML = e.target.innerHTML);
}
//Variables
const mercury = document.getElementById("mercury");
const venus = document.getElementById("venus");
const earth = document.getElementById("earth");
const mars = document.getElementById("mars");
const jupiter = document.getElementById("jupiter");
const saturn = document.getElementById("saturn");
const uranus = document.getElementById("uranus");
const neptune = document.getElementById("neptune");
const weight = document.getElementById("weight");
weight.addEventListener("input", Calc);
function Calc() {
if (weight.value > 99999) {
alert("Max Amount Of Numbers is 99999");
weight.value = "";
} else {
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mercury.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.9;
calculate = Math.round(calculate);
venus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1;
calculate = Math.round(calculate);
earth.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.38;
calculate = Math.round(calculate);
mars.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 2.36;
calculate = Math.round(calculate);
jupiter.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.92;
calculate = Math.round(calculate);
saturn.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 0.89;
calculate = Math.round(calculate);
uranus.innerHTML = calculate;
console.log(calculate);
var val = weight.value;
console.log(val);
var calculate = val * 1.12;
calculate = Math.round(calculate);
neptune.innerHTML = calculate;
console.log(calculate);
}
}
document.querySelectorAll('#kg, #lbs').forEach(ub => ub.addEventListener('click', units));
function units(e) {
let units = document.querySelectorAll(".value.unit");
if (units.length == 0) {
let newElement = document.createElement("h3");
newElement.setAttribute("class", "value unit");
document.querySelectorAll(".dynamic").forEach((dyn) => {
// append a cloned copy to each, not the same newElement
dyn.appendChild(newElement.cloneNode())
});
// re-run the query to find the newly added nodes
units = document.querySelectorAll(".value.unit");
}
// set the content
units.forEach(unit => unit.innerHTML = e.target.innerHTML);
}
#import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
#font-face {
font-family: SpaceQuest;
src: url(https://raw.githubusercontent.com/Lemirq/WODP/master/Fonts/SpaceQuest-yOY3.woff);
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: url(./dario-bronnimann-hNQwIirOseE-unsplash.jpg);
}
/* Handle */
::-webkit-scrollbar-thumb {
background: rgba(59, 59, 59, 0.741);
border-radius: 200px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: rgb(255, 255, 255);
}
* {
--c-light: #f4f4f4;
--c-dark: #141414;
--c-blue: rgb(10, 132, 255);
--f-body: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
--trans-ease-in-out: all 0.2s ease-in-out;
color: var(--c-light);
}
body {
background-image: url(https://raw.githubusercontent.com/Lemirq/WODP/master/images/dario-bronnimann-hNQwIirOseE-unsplash.jpg);
margin: 0;
inset: 50px;
font-family: var(--f-body);
}
a {
color: var(--c-light);
text-decoration: none;
}
/***** NAVBAR *****/
.nav-item {
transition: var(--trans-ease-in-out);
}
.ext-link {
cursor: alias;
}
.nav-item:hover {
color: var(--c-dark);
background-color: var(--c-light);
}
.nav-item:hover li {
color: var(--c-dark);
}
.nav-item.icon-link:hover i {
color: var(--c-dark);
}
.nav-item:not(:last-child) {
margin-right: 20px;
}
navbar {
display: flex;
flex-direction: row;
justify-content: end;
align-items: center;
padding: 20px;
}
.nav-item-container {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0;
padding: 0;
}
.nav-item {
display: inline-block;
list-style: none;
padding: 10px;
font-size: 20px;
border-radius: 10px;
}
.gh-icon {
font-size: 30px;
}
/***** End NAVBAR *****/
/***** Main *****/
#wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
h1 {
font-family: SpaceQuest, sans-serif;
font-size: 3.5rem;
}
.input-group {
border: 2px var(--c-light) solid;
border-radius: 10px;
/* max-width: 400px; */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 50px;
}
[type="number"]:focus {
outline: none;
}
[type="number"] {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
font-family: var(--f-body);
min-width: 280px;
}
.btn-form {
font-size: 20px;
padding: 5px;
background-color: rgba(217, 217, 217, 0.2);
border: none;
transition: var(--trans-ease-in-out);
cursor: pointer;
font-family: var(--f-body);
}
.btn-form:hover {
background-color: rgba(217, 217, 217, 0.4);
}
.btn-form:first {
border-right: var(--c-light) 1px solid;
}
.input-group-text {
background-color: rgba(217, 217, 217, 0.2);
font-size: 17px;
padding: 7px;
}
/***** End Main *****/
/***** CARDS *****/
.card-container {
margin: 50px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
align-items: center;
grid-gap: 10px;
width: calc(100vw - 200px);
}
.card {
background-color: #141414;
width: auto;
height: auto;
border-radius: 10px;
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175);
}
.planet-info {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.planet {
font-size: 50px;
margin: 0;
text-transform: capitalize;
}
.planet-img {
width: 80px;
height: auto;
margin-right: 30px;
}
[src="./images/planets/Saturn.png"] {
height: 79.25px;
width: auto;
}
.weight {
margin-top: 10px;
text-transform: capitalize;
font-size: 20px;
}
.weight::after {
content: ":";
}
.divider {
height: 1px;
width: 100%;
margin: 20px 0;
background-color: var(--c-light);
}
.value {
font-size: 60px;
color: var(--c-blue);
}
.dynamic>.value:nth-child(2) {
margin-left: 10px;
}
.dynamic {
display: flex;
flex-direction: row;
justify-content: space-between;
}
/***** End CARDS *****/
.input-error {
outline: 1px solid red;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.8.3/font/bootstrap-icons.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<navbar>
<ul class="nav-item-container">
<a target="_blank" class="nav-item ext-link" href="https://lemirq.github.io">
<li>Website</li>
</a>
<a target="_blank" class="nav-item icon-link ext-link" href="https://github.com/Lemirq">
<li><i class="bi bi-github gh-icon"></i></li>
</a>
</ul>
</navbar>
<div id="wrapper">
<h1 id="vs-h1">Your Weight On Different Planets</h1>
<div class="input-group">
<input id="weight" placeholder="Enter your Weight" type="number">
<button id="kg" class="btn-form" type="button">Kg</button>
<button id="lbs" class="btn-form" type="button">Lbs</button>
</div>
<div class="card-container">
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mercury.png" alt="EARTH">
<h3 class="planet">mercury</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mercury" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Venus.png" alt="EARTH">
<h3 class="planet">venus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="venus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Earth.png" alt="EARTH">
<h3 class="planet">earth</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="earth" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Mars.png" alt="EARTH">
<h3 class="planet">mars</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="mars" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Jupiter.png" alt="EARTH">
<h3 class="planet">jupiter</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="jupiter" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Saturn.png" alt="EARTH">
<h3 class="planet">saturn</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="saturn" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Uranus.png" alt="EARTH">
<h3 class="planet">uranus</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="uranus" class="value">F</h3>
</div>
</div>
<div class="card">
<div class="planet-info">
<img class="planet-img" src="./images/planets/Neptune.png" alt="EARTH">
<h3 class="planet">neptune</h3>
</div>
<div class="divider"></div>
<h4 class="weight">weight</h4>
<div class="dynamic">
<h3 id="neptune" class="value">F</h3>
</div>
</div>
</div>
</div>

Related

Counter on scroll in javascript

I am trying to make make my counter work when scrolling.
I have tried Google, StackOverflow and other different sites and the web is full with examples but I cannot figure it out. Can anyone give a tip please?
I have also tried to wrap the code inside $(window).scroll() but it also doesn't seem to work.
const counters = document.querySelectorAll(".count");
const speed = 200;
counters.forEach((counter) => {
const updateCount = () => {
const target = parseInt(+counter.getAttribute("data-target"));
const count = parseInt(+counter.innerText);
const increment = Math.trunc(target / speed);
console.log(increment);
if (count < target) {
counter.innerText = count + increment;
setTimeout(updateCount, 1);
} else {
count.innerText = target;
}
};
updateCount();
});
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "verdana", sans-serif;
background: #ffffff;
}
.container {
width: 80%;
margin: auto;
}
.heading {
text-align: center;
font-size: 3.5rem;
font-weight: bold;
padding: 5rem 0;
color: #505050;
}
.counter-container {
display: flex;
justify-content: space-around;
align-items: center;
}
.counter {
text-align: center;
}
.counter h3 {
padding: 0.5rem 0;
font-size: 2.5rem;
font-weight: 800;
}
.counter h6 {
font-size: 2rem;
padding-bottom: 1rem;
}
.icon {
height: 5rem;
width: auto;
}
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght#400;900&display=swap" rel="stylesheet">
<div class="container">
<div class="heading">
Resultater
</div>
<div class="counter-container">
<div class="counter">
<img src="#" alt="timer" srcset="" class="icon">
<h3 data-target="200" class="count">0</h3>
<h6>Text 1</h6>
</div>
<div class="counter">
<img src="#" srcset="" class="icon">
<h3 data-target="217" class="count">0</h3>
<h6>Text 2</h6>
</div>
<div class="counter">
<img src="#" alt="night" srcset="" class="icon">
<h3 data-target="511" class="count">0</h3>
<h6>Text 3</h6>
</div>
</div>
</div>
If all you need is to start your timers after the first scroll event, you can add a single scroll event listener, and make use of the once config option (So that you only start the animation the first time the user scrolls)
The important part (Where startCounters is a function which creates your counters):
window.addEventListener('scroll', startCounters, {
once: true
});
Full example:
const counters = document.querySelectorAll(".count");
const speed = 200;
// The code to start the animation is now wrapped in a function
const startCounters = () => {
counters.forEach((counter) => {
const updateCount = () => {
const target = parseInt(+counter.getAttribute("data-target"));
const count = parseInt(+counter.innerText);
const increment = Math.trunc(target / speed);
if (count < target) {
counter.innerText = count + increment;
setTimeout(updateCount, 1);
} else {
count.innerText = target;
}
};
updateCount();
});
}
// On the first scroll in this window, call the function to start the counters
window.addEventListener('scroll', startCounters, {
once: true
});
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "verdana", sans-serif;
background: #ffffff;
}
.container {
width: 80%;
margin: auto;
}
.heading {
text-align: center;
font-size: 3.5rem;
font-weight: bold;
padding: 5rem 0;
color: #505050;
}
.counter-container {
display: flex;
justify-content: space-around;
align-items: center;
}
.counter {
text-align: center;
}
.counter h3 {
padding: 0.5rem 0;
font-size: 2.5rem;
font-weight: 800;
}
.counter h6 {
font-size: 2rem;
padding-bottom: 1rem;
}
.icon {
height: 5rem;
width: auto;
}
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght#400;900&display=swap" rel="stylesheet">
<div class="container">
<div class="heading">
Resultater
</div>
<div class="counter-container">
<div class="counter">
<img src="#" alt="timer" srcset="" class="icon">
<h3 data-target="200" class="count">0</h3>
<h6>Text 1</h6>
</div>
<div class="counter">
<img src="#" srcset="" class="icon">
<h3 data-target="217" class="count">0</h3>
<h6>Text 2</h6>
</div>
<div class="counter">
<img src="#" alt="night" srcset="" class="icon">
<h3 data-target="511" class="count">0</h3>
<h6>Text 3</h6>
</div>
</div>
</div>

Tip calculator javascript

I am creating a tip calculator which calculates tip amount based on bill amount, tip percentage, and the number of people (which are all inputs by the user). I have created a script using Javascript but it's not working and I am not sure why. Am I calling the function in the wrong way? Did I make any mistakes in the function or in the for loop? I apologize for the messed-up layout of the calculator in the snippet, I am going with a mobile-first approach and still working on the desktop layout.
let allButtons = document.getElementsByClassName('btn');
let noOfButtons = allButtons.length;
let i;
function tipCalculate(e) {
let billAmount = parseFloat(document.getElementById('bill__amount').value);
let tipPercent = e.target.value;
let noOfPeople = document.getElementById('people-no').value;
let tipAmountPerPerson = billAmount / 100 * tipPercent / noOfPeople;
let totalAmountPerPerson = (billAmount + (billAmount / 100 * tipPercent)) / noOfPeople;
document.getElementByClassName('tip-amount-display').innerHTML = tipAmountPerPerson;
document.getElementByClassName('total-amount-display').innerHTML = totalAmountPerPerson;
}
// append event listeners to each button
for (i = 0; i < noOfButtons; i++) {
allButtons[i].addEventListener('click', tipCalculate);
}
#import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght#400;700&display=swap');
:root {
--clr-primary: hsl(172, 67%, 45%);
--clr-neutral-100: hsl(0, 0%, 100%);
--clr-neutral-200: hsl(189, 41%, 97%);
--clr-neutral-300: hsl(185, 41%, 84%);
--clr-neutral-400: hsl(184, 14%, 56%);
--clr-neutral-500: hsl(186, 14%, 43%);
--clr-neutral-600: hsl(183, 100%, 15%);
}
*,
*::after,
*::before {
box-sizing: border-box;
}
h2 {
margin: 0;
font-size: 1rem;
}
body {
margin: 0;
font-family: 'Space Mono', monospace;
background: var(--clr-neutral-300);
font-size: 1rem;
font-weight: 400;
}
button {
font-family: 'Space Mono', monospace;
text-transform: uppercase;
border: none;
padding: 0.3em 0.6em;
border-radius: 0.45rem;
font-size: 1.4rem;
font-weight: 700;
}
.btn-main {
background-color: var(--clr-neutral-600);
color: var(--clr-neutral-100);
}
.btn-main:hover,
.btn-main:focus {
background-color: var(--clr-primary);
color: var(--clr-neutral-600);
}
.btn-inverse {
background-color: var(--clr-primary);
color: var(--clr-neutral-600);
}
.title {
color: var(--clr-neutral-500);
text-align: center;
letter-spacing: .35em;
padding: 1em 0;
}
form {
background: var(--clr-neutral-100);
border-radius: 1.8rem 1.8rem 0 0;
}
.accent-title {
color: var(--clr-neutral-500);
}
.accent-title-light {
color: var(--clr-neutral-400);
font-size: .8rem;
}
.tip-amount {
background: var(--clr-neutral-600);
border-radius: 1rem;
padding: 1.6rem;
display: flex;
/* flex-direction: column; */
flex-wrap: wrap;
justify-content: space-between;
}
.neutral-title {
color: var(--clr-neutral-100);
}
input {
border: none;
background-color: var(--clr-neutral-200);
width: 100%;
border-radius: .25rem;
}
.bill__amount,
.people-no {
height: 40px;
text-align: right;
font-family: 'Space Mono', monospace;
font-size: 24px;
font-weight: 700;
color: var(--clr-neutral-600);
padding-right: .8rem;
}
.bill__amount {
background-image: url(../images/icon-dollar.svg);
background-repeat: no-repeat;
background-position: left center;
background-origin: content-box;
padding-left: .8rem;
}
.people-no {
background-image: url(../images/icon-person.svg);
background-repeat: no-repeat;
background-position: left center;
background-origin: content-box;
padding-left: .8rem;
}
div+div {
margin-top: 2em;
}
.calculator {
padding: 2rem;
}
.tip__btns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.accent-title-light {
margin-top: 0%;
}
.reset {
width: 100%;
margin-top: 1rem;
}
.tip__heading {
margin-bottom: .9rem;
}
.tip__custom {
font-family: 'Space Mono', monospace;
font-size: 1.3rem;
font-weight: 700;
letter-spacing: .1rem;
text-align: right;
padding-right: .8rem;
}
.bill__heading,
.no-of-people__heading {
margin-bottom: .6rem;
}
.tip-amount-display,
.total-amount-display {
/* text-align: right; */
color: var(--clr-primary);
font-size: 24px;
}
.total-amount-display {
margin-top: 1.25rem;
}
.tip-amount-display {
align-self: start;
margin-top: .4rem;
}
.col+.col {
margin-top: 0;
}
#media (min-width: 768px) {
form {
display: flex;
flex-direction: row;
justify-content: space-between;
align-content: stretch;
width: 50%;
margin: 0 auto;
border-radius: 1.8rem 1.8rem 1.8rem 1.8rem;
}
form>* {
flex: 1 1 50%;
}
.attribution {
align-self: flex-end;
}
div+div {
margin-top: 0;
}
}
.attribution {
font-size: 11px;
text-align: center;
}
.attribution a {
color: hsl(228, 45%, 44%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- displays site properly based on user's device -->
<link rel="stylesheet" href="css/style.css">
<link rel="icon" type="image/png" sizes="32x32" href="./images/favicon-32x32.png">
<script src="js/tip.js"></script>
<title>Frontend Mentor | Tip calculator app</title>
</head>
<body>
<h1 class="title">SPLI<br>TTER</h1>
<form class="calculator">
<div class="main-cols">
<div class="bill">
<h2 class="bill__heading"><label for="bill__amount" class="accent-title">Bill</label></h2>
<input type="text" name="bill__amount" id="bill__amount" class="bill__amount" placeholder="0">
</div>
<div class="tip">
<h2 class="tip__heading"><label for="" class="accent-title">Select Tip %</label></h2>
<div class="tip__btns">
<button type="button" class="btn btn-main">5%</button>
<button type="button" class="btn btn-main">10%</button>
<button type="button" class="btn btn-main">15%</button>
<button type="button" class="btn btn-main">25%</button>
<button type="button" class="btn btn-main">50%</button>
<input type="text" name="tip__custom" class="tip__custom" id="tip__custom" placeholder="Custom">
</div>
</div>
<div class="no-of-people">
<h2 class="no-of-people__heading"><label for="people-no" class="accent-title">Number of People</label></h2>
<input type="text" name="people-no" id="people-no" class="people-no" placeholder="0">
</div>
</div>
<div class="main-cols">
<div class="tip-amount">
<div class="col">
<h2 class="neutral-title">Tip Amount</h2>
<h3 class="accent-title-light ">/ person</h3>
<h2 class="neutral-title">Total</h2>
<h3 class="accent-title-light">/ person</h3>
</div>
<div class="col">
<h2 class="tip-amount-display">$0.00</h2>
<h2 class="total-amount-display">$0.00</h2>
</div>
<button class="btn-inverse reset">Reset</button>
</div>
</div>
<div class="attribution">
Challenge by Frontend Mentor. Coded by Sachin Jadhav.
</div>
</form>
</body>
</html>
Issue 1
It's getElementsByClassName not getElementByClassName (you are missing the 's').
Here is how to use it: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
Specifically document.getElementsByClassName('test')[0] will be of interest for you, to get the first element with that class.
Your code will look like this:
document.getElementsByClassName('tip-amount-display')[0].innerHTML = tipAmountPerPerson;
// ^ ^
Alternatively you can use querySelector to find an element in the DOM using css selectors.
Issue 2.1
e.target.value is always an empty string (doing math with it will result in NaN).
This is because the HTML Element that was clicked did not have a value property on it. To fix it add value="5" to the Buttons in your HTML like this:
<button type="button" value="5" class="btn btn-main">5%</button>
<button type="button" value="10" class="btn btn-main">10%</button>
<!-- ... -->
Issue 2.2
document.getElementById('people-no').value is also an empty string when no value was entered. To fix it you can check for it's truthiness as the empty string and 0 will be falsy you can replace them with 1.
Long version:
let noOfPeople = document.getElementById('people-no').value;
if (!noOfPeople) {
noOfPeople = 1;
}
Short version using short circuit evaluation:
let noOfPeople = document.getElementById('people-no').value || 1;
Here is a working fiddle: https://jsfiddle.net/zg6t4o1a/
let button = document.querySelector("button");
let fun = () => { let totalamount = +document.getElementById("amount").value let select = +document.getElementById("select").value; let totalpeople = +document.getElementById("peo").value; let finale =document.getElementById("h3");let absolute = (totalamount * select) / totalpeople; finale.textContent = absolute;if (totalamount <= 0) { alert("Your entered value is invalid"); }};button.addEventListener("click", fun);

Problem with deleting object from array of objects

I have a issue where deleting dynamically created dom object from an array of objects. The problem is that when i delete some element from the array the rest of the elements after the spliced element also gets deleted.
To my knowledge this is happening due to the index of the next elements gets updated to the one before it and the function deletes the element having the same index over and over.
How can i fix this?(Code uploaded w HTML and CSS incl.)
Is this the recommended way to implement this function?
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
Full code
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
const cards = document.querySelectorAll('[data-index]');
cards.forEach(card => {
const removeButton = card.querySelector('.button');
removeButton.addEventListener('click', () => {
removeBook(book.id)
})
})
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
#font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!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>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
The problem with your code is that for every card aded in populateBook, you loop all the previous cards and add a click event listener, which means the second book gets 2 copies of this handler, the third 3 etc.
Instead of doing that, add a single event handler for clicking and handle appropriately:
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
});
Live example:
const form = document.getElementById('input-form');
const formButton = document.getElementById('add-form');
const formView = document.querySelector('.form-card')
const bookList = document.querySelector('.books-wrapper');
let myLibrary = [];
let newBook;
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
this.info = function() {
return `${this.title} is a book by ${this.author}, ${this.pages} pages, not read yet.`
};
};
Book.prototype.read = false;
function addToLibrary(e) {
{
e.preventDefault();
const title = (document.getElementById('title')).value;
const author = (document.getElementById('author')).value;
const pages = (document.getElementById('pages')).value;
newBook = new Book(title, author, pages);
} {
myLibrary.push(newBook);
populateBooks(myLibrary, bookList);
formDisplay();
form.reset();
console.table(myLibrary)
}
};
function populateBooks(myLib, bookView) {
const bookCards = document.querySelectorAll('.book-card')
bookCards.forEach(bookCard => bookList.removeChild(bookCard));
myLib.forEach((book, index) => {
book.id = index;
const cardContent = `<div class="book-card" data-index=${book.id}>
<div class="card-info-wrapper">
<h2>${book.title}</h2>
<h3>${book.author}</h3>
<h4>${book.pages} Pages</h4>
<p>${book.info()}</p>
</div>
<div class="card-menu">
<div class="button" id="remove-btn">
Remove
</div>
</div>
</div>`
const element = document.createElement('div');
element.innerHTML = cardContent;
// element.dataset.indexx = book.id;
bookView.appendChild(element.firstChild);
});
};
function removeBook(id) {
console.log('deleting', id);
myLibrary.splice(id, 1);
console.table(myLibrary);
populateBooks(myLibrary, bookList);
}
function formDisplay() {
form.reset();
formView.classList.toggle('toggle-on');
};
const theHobbit = new Book('The Hobbit', 'J.R.R. Tolkien', 295);
myLibrary.push(theHobbit)
const harryPotter = new Book('Harry Potter', 'J.K Rowling', 320);
myLibrary.push(harryPotter)
const sangaf = new Book('The Subtle Art of Not Giving a Fuck', 'Mark Manson', 300)
myLibrary.push(sangaf)
document.addEventListener("DOMContentLoaded", function() {
form.addEventListener("submit", function(e) {
addToLibrary(e)
});
document.querySelector(".books-wrapper").addEventListener("click", (e) => {
if(e.target.classList.contains("button")){
const index = e.target.parentElement.parentElement.dataset.index;
removeBook(index);
}
})
});
formButton.addEventListener('click', formDisplay);
populateBooks(myLibrary, bookList);
#font-face {
font-family: "fanwood";
font-style: normal;
font-weight: normal;
src: url("fonts/Fanwood.otf");
font-display: swap;
}
:root {
--color-primary: #e9e2d7;
--color-primary-alt: #8e6549;
--color-secondary: #d42257;
--color-background: #d2fbf7;
--color-text: #412d86;
--color-light: #fff;
--color-anchor: #3a00ff;
--font-family: "fanwoood";
--font-weight-strong: 500;
--font-size-h1: 4rem;
--font-size-h2: 3rem;
--font-size-h3: 2rem;
--font-size-h4: 1.35rem;
--font-size-text: 1.15rem;
--border-radius: 8px;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
html {
overflow-x: hidden;
}
/* Set core body defaults */
body {
font-family: 'fanwood';
min-height: 100vh;
font-size: 100%;
line-height: 1.5;
text-rendering: optimizeSpeed;
overflow-x: hidden;
}
/* Make images easier to work with */
img {
display: block;
max-width: 100%;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
body {
background-color: var(--color-primary);
}
button {
background-color: var(--color-primary);
border: none;
margin: 0;
}
input {
width: 100%;
margin-bottom: 10px 0;
}
.site-wrapper {
margin: 0 4%;
}
.card-info-wrapper {
margin: 4% 4%;
text-align: left;
}
.card-menu {
align-self: flex-end;
margin: 4% 4%;
}
.header {
color: var(--color-primary);
background-color: var(--color-primary-alt);
height: 84px;
display: flex;
align-items: center;
justify-content: center;
}
.tool-bar {
margin-top: 20px;
}
.tools {
display: flex;
}
.button {
cursor: pointer;
display: inline-flex;
padding: 2px 8px;
color: var(--color-primary-alt);
background-color: var(--color-primary);
}
.button.add {
display: inline-flex;
padding: 2px 8px;
background-color: var(--color-primary-alt);
color: var(--color-primary);
}
.books-wrapper {
margin-top: 20px;
/* border: 1px solid white; */
display: flex;
flex-wrap: wrap;
}
.book-card {
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.form-card {
display: none;
word-wrap: normal;
background-color: var(--color-primary-alt);
color: var(--color-primary);
width: 300px;
height: 350px;
margin-right: 10px;
margin-bottom: 10px;
}
.toggle-on {
display: block;
}
<!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>Book</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="header">
<div class="site-wrapper">
<div class="header-logo-container">
<h1>Library</h1>
</div>
</div>
</div>
<div class="tool-bar">
<div class="site-wrapper">
<div class="tools">
<div class="button add" id="add-form">
Add Book
</div>
</div>
</div>
</div>
<div class="books">
<div class="site-wrapper">
<div class="books-wrapper">
<!-- TEMPLATE FOR BOOK CARD -->
<!-- <div class="book-card">
<div class="card-info-wrapper">
<h2>Title</h2>
<h3>Author</h3>
<h4>Pages</h4>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ipsum fugit officiis animi soluta et, sit aliquid.</p>
</div>
<div class="card-menu">
<div class="button">
Remove
</div>
</div>
</div> -->
<div class="form-card">
<div class="card-info-wrapper">
<form id="input-form">
<label for="title"><h3>Title</h3></label>
<input type="text" id="title" name="title" placeholder="Name of the Book" required>
<label for="author"><h3>Author</h3></label>
<input type="text" id="author" name="author" placeholder="Name of the Author" required>
<label for="pages"><h3>Pages</h3></label>
<input type="number" id="pages" name="pages" placeholder="Number of Pages" required>
<button type="submit" class="button" id="addBook">Add Book</button>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>

Budget doesn't recalculate after deleting last item from data

So I have this code for a budget app that I'm doing on a javascript course on Udemy. There's a function to delete the item from the data structure, from the UI and to update the budget. It works fine until when I delete the last item from the list i.e the last item on the array, it deletes the item from the data array and from the UI, but don't recalculate the budget.
I tried to look up into my code, and didn't found anything, so that's why I need you, more experienced guys help :)
// BUDGET CONTROLLER
var budgetController = (function() {
var Expense = function(id, description, value) {
this.id = id;
this.description = description;
this.value = value;
}
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 += 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 a new ID
if (data.allItems[type].length > 0) {
ID = data.allItems[type][data.allItems[type].length - 1].id + 1;
} else {
ID = 0;
}
// Create a new item object
if (type === 'exp') {
newItem = new Expense(ID, des, val);
} else if (type === 'inc') {
newItem = new Income(ID, des, val);
}
// Push the created object into the data structure
data.allItems[type].push(newItem);
// Return the newItem publicly
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() {
// Calc total income and expenses
calculateTotal('exp');
calculateTotal('inc');
// Calc the budget(income - expenses)
data.budget = data.totals.inc - data.totals.exp;
// Calc the percentage of income spent
if (data.totals.inc > 0) {
data.percentage = Math.round((data.totals.exp / data.totals.inc) * 100);
} else {
data.percentage = -1;
}
},
getBudget: function() {
return {
budget: data.budget,
totalInc: data.totals.inc,
totalExp: data.totals.exp,
percentage: data.percentage
}
},
testing: function() {
console.log(data);
}
}
})();
// 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'
}
return {
getInput: function() {
return {
type: document.querySelector(DOMStrings.inputType).value, // Will be 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 placeholder with actual data
newHtml = html.replace('%id%', obj.id);
newHtml = newHtml.replace('%description%', obj.description);
newHtml = newHtml.replace('%value%', obj.value);
// Insert 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) {
document.querySelector(DOMStrings.budgetLabel).textContent = obj.budget;
document.querySelector(DOMStrings.incomeLabel).textContent = obj.totalInc;
document.querySelector(DOMStrings.expensesLabel).textContent = obj.totalExp;
if (obj.percentage > 0) {
document.querySelector(DOMStrings.percentageLabel).textContent = obj.percentage + '%';
} else {
document.querySelector(DOMStrings.percentageLabel).textContent = '---';
}
},
getDOMStrings: function() {
return DOMStrings;
}
}
})();
// GLOBAL APP CONTROLLER
var controller = (function(budgetCtrl, UICtrl) {
var setupEventListeners = function() {
var DOM = UICtrl.getDOMStrings();
document.querySelector(DOM.inputBtn).addEventListener('click', ctrlAddItem);
document.addEventListener('keypress', function(e) {
if (e.keyCode === 13 || e.which === 13) {
ctrlAddItem();
}
});
document.querySelector(DOM.container).addEventListener('click', ctrlDeleteItem);
}
var updateBudget = function() {
// Calculate budget
budgetCtrl.calculateBudget();
// Return the budget
var budget = budgetCtrl.getBudget();
// Display the budget on the UI
UICtrl.displayBudget(budget);
}
var ctrlAddItem = function() {
var input, newItem;
// Get the input data from the form
input = UICtrl.getInput();
if (input.description !== '' && !isNaN(input.value) && input.value > 0) {
// Create a new object with the data
newItem = budgetCtrl.addItem(input.type, input.description, input.value);
// Add new item to the UI
UICtrl.addListItem(newItem, input.type);
// Clear the fields
UICtrl.clearFields();
// Calculate and update budget
updateBudget();
}
}
var ctrlDeleteItem = function(event) {
var itemID, splitID, type, ID;
itemID = event.target.parentNode.parentNode.parentNode.parentNode.id;
if (itemID) {
splitID = itemID.split('-');
type = splitID[0];
ID = parseInt(splitID[1]);
// Delete item from the data structure
budgetCtrl.deleteItem(type, ID);
// Delete item from the UI
UICtrl.deleteListItem(itemID);
// Update and show the new budget
updateBudget();
}
}
return {
init: function() {
console.log('The application has started.');
setupEventListeners();
UICtrl.displayBudget({
budget: 0,
totalInc: 0,
totalExp: 0,
percentage: -1
});
}
}
})(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 you assign to data.totals[type] = sum; inside the forEach loop in calculateTotal(). If there are no items in the array, the loop never runs, so you never do the assignment.
You don't need to do that assignment each time through the loop, just once after the loop is done calculating the total. So it should be
var calculateTotal = function(type) {
var sum = 0;
data.allItems[type].forEach(function(cur) {
sum += cur.value;
})
data.totals[type] = sum;
}
var calculateTotal = function (type) {
var sum = 0;
data.allItems[type].forEach(function (cur) {
sum += cur.value;
data.totals[type] = sum;
}); };
if the data.allItems doesn't have any value then the total not getting updated.
You have to check if the allItems have 0 then update the totals as 0

How to print out dynamic items from array to the DOM

I am stuck in a logical problem.
I have an array where i am stacking items with array.push() where came up from a user's Input.
Problem is now:
How can i print these items to the DOM? ATM i am doing this,
function getInput(operator, description, value) {
// SAVE IN INCOME IF "+" IS CHOOSEN (DEFAULT)
if (addType.value == 'inc') {
let op = incomeArr['operator'] = operator;
let des = incomeArr['description'] = description;
let val = incomeArr['value'] = value;
incomeArr.push([op, des, val]);
return incomeArr;
}
}
Creating a associative array in the getInput();
First attempt to print this data into the DOM looked like this:
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
for (var i = 0; i < expenseArr.length; i++) {
incomeItemSpan;
incomeList.append(incomeItemSpan);
}
}
My problem here is that my forLoop index condition is messing up because of the "everytime function call" the value which is printed out will be printed twice in the next function call of this. the index start again at 0 and even with a out of function loop counter this will not work.
[![gave-input][1]][1]
So the next attempt was:
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
incomeArr.forEach(() => {
incomeList.innerHTML = incomeItemSpan;
});
}
i tried it with a forEach and the problem here is, i have absolutely no idea how to print out the incomeItemSpan without a innerHTML. I want a list of items in the DOM which are stacked from top down, every line is a new item from the array like i would use item.append(), but HTML wont work in a append().
How can i do this?
/* TODO:
- Add Eventlistener for Submit a +/- Value
- if + {add 1. into INCOME section} + Set INCOME in header to the amount
of all incomes added
if - {same like if+ just for -}
- create a update DOM function to update the visualisation of the calculations
- INCOME AND EXPENSES should be a Array
- add a prototype function to remove entrys from INCOME and EXPENSES, use
indexOf to get the index item and remove with array.splice().
- calc every expense with INCOME to get a % value of how much this entry is
% related to the max INCOME
*/
// VARS:
let addType = document.querySelector('.add__type');
let description = document.querySelector('.add__description');
let addValue = document.querySelector('.add__value');
let incomeArr = [];
let expenseArr = [];
// EVENTLISTENER Constructor:
function EventListner(selector, listner, fnt) {
this.selector = selector;
this.listner = listner;
this.fnt = fnt;
document.querySelector(selector).addEventListener(listner, fnt);
};
// getInput VALUES FROM USER Constructor:
function getInput(operator, description, value) {
// SAVE IN INCOME IF "+" IS CHOOSEN (DEFAULT)
if (addType.value == 'inc') {
let op = incomeArr['operator'] = operator;
let des = incomeArr['description'] = description;
let val = incomeArr['value'] = value;
incomeArr.push([op, des, val]);
// TODO: WHAT AFTER SAVING?
return incomeArr;
}
// SAVE IN EXPENSE IF "-" IS CHOOSEN
if (addType.value == 'exp') {
let op = expenseArr['operator'] = operator;
let des = expenseArr['description'] = description;
let val = expenseArr['value'] = value;
expenseArr.push([op, des, val]);
// TODO: WHAT AFTER SAVING?
return expenseArr;
}
};
// STUCK AS FUCK!
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
incomeArr.forEach(() => {
incomeList.innerHTML = incomeItemSpan;
});
// for (var i = 0; i < incomeArr.length; i++) {
// console.log([i]);
// incomeItemSpan;
// incomeList.append(incomeItemSpan);
// }
// console.log(incomeItemSpan);
//
// incomeList.append(expenseArr);
// incomeArr.toString();
// expenseArr.toString();
// incomeList.innerHTML = incomeItemSpan;
};
const main = (function() {
// EVENTLISTENERS
const clickListener = new EventListner('.add__btn', 'click', () => {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
getInput(addType.value, description.value, addValue.value);
});
const enterKeyListener = new EventListner('.add__value', 'keypress', (e) => {
let testArray = [];
for (var i = 0; i < testArray.length; i++) {
testArray[i] = [i];
console.log(testArray[i]);
}
testArray.push('item');
if (e.keyCode == 13) {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
// ON ENTER SAVE VALUES IN AN ARRAY
// IF PLUS INTO incomeArr, ON MINUS INTO expenseArr
// getInput(addType.value, description.value, addValue.value);
printToDOM(getInput(addType.value, description.value, addValue.value));
}
});
}());
//
/**********************************************
*** 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">+ 0</div>
<div class="budget__income clearfix">
<div class="budget__income--text">Income</div>
<div class="right">
<div class="budget__income--value">+ 0</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">- 0</div>
<div class="budget__expenses--percentage">0%</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"></div>
<div class="right clearfix">
<div class="item__value"></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>
Is that exactly what you're looking for?
const arr = new Array();
function addList() {
const el = document.querySelector("input[name=user]")
const val = el.value
arr.push(val);
el.value = null;
return false;
}
function showList() {
for (let i = 0; i < arr.length; i++) {
const listElement = document.createElement("li");
listElement.textContent = arr[i]
document.body.appendChild(listElement);
}
}
<form onsubmit="return addList()">
<input type="text" name="user">
<button>Add list item</button>
</form><br>
<button class="showList" onclick="showList()">Show list</button>
<ul class="list"></ul>
For creating a list of items, create a ordered/unordered list then append each item to this list in forEach and finally set the html to ordered//unordered list.
Example:
var ol=$('<ol></ol>');
incomeArr.forEach(() => {
ol.append($('<li></li>').text(`${item.description}: ${item.value}`));
});
$('#display').html(ol);

Categories

Resources