Reputation: 55
I am currently trying to add a function to the ingredients section of a recipe that calculates the values based on the portions (It's for a cooking site). My problem is only the last item of the array doing this. The others aren't changing their values.
My JS looks like this:
var refresh = document.getElementById("refresh-portion");
var zutatendata = document.getElementsByClassName("zutaten-data");
for (var i = 0; i < zutatendata.length; i++)
{
var array = [zutatendata[i]]; console.log(zutatendata[i]);
array.forEach(function(elem)
{
var initial = elem.innerHTML;
refresh.onclick = function()
{
var input = document.getElementById("custom-portion").value;
var result = initial * Number(input);
if (result.toString().length > 5) result = result.toFixed(1);
elem.innerHTML = result + " ";
}
})
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" >
<p id="portions">
Für
<input type="text" value="1" id="custom-portion">
<button id="refresh-portion">
<i class="fas fa-sync"></i>
</button>
Portionen
</p>
<div class="grid">
<div class="item">Möhren</div>
<div class="item">
<p class="zutaten-data">0.33</p>kg
</div>
<div class="item">Kartoffeln</div>
<div class="item">
<p class="zutaten-data">2</p>Stück
</div>
<div class="item">Brühe</div>
<div class="item">
<p class="zutaten-data">1</p>l
</div>
<div class="item">Butter</div>
<div class="item">
<p class="zutaten-data">5</p>kg
</div>
</div>
</div>
It would be very nice if you could tell me why the other values aren't getting multiplied.
Upvotes: 0
Views: 116
Reputation: 22355
I imagine you are looking for something like this?
const
customPortion = document.querySelector('#custom-portion')
, refreshBt = document.getElementById('refresh-portion')
, zutatendata = document.querySelectorAll('.zutaten-data')
, portionTxt = document.querySelector('#refresh-portion').nextSibling
, gFract = (()=>
{
let fractions = [3,6,7,8,9].map(x=>({t:x,f:1/x}))
// allows to establish the useful fraction
return (str) => // ex 2.33 => {value: 2, tenths: 3}
{ // where 3 is the divisor of 1
let // (0.33 == 1/3)
value = Number(str.trim())
, intVal = Math.floor(value)
, decim = value - intVal
, delta = 10
, tenths = 0
;
for (let ref of fractions)
{
let d = Math.abs(ref.f - decim)
if (d < delta)
{
delta = d
tenths = ref.t
}
}
if (delta < 0.01) value = intVal
else tenths = 0
return ({ value, tenths })
} }
)()
, setTenths = el =>
{ // subtract fractions to zero
let
n = Number(el.textContent)
, i = Math.floor(n)
, d = n - i
;
if (d===0) el.textContent = i
// el.textContent = i
// el.dataset.tenths = d===0 ? '' : d.toFixed(2).slice(-3)
};
// init : adding def property to each zutatendata
zutatendata.forEach( zd =>
{
zd.def = gFract(zd.textContent)
setTenths(zd)
})
customPortion.value = '1'
portionTxt.textContent = ' portion'
customPortion.oninput = () => // input controling for only integer value
{
let portions = parseInt(customPortion.value.replace(/\D/g,''), 10) || 1
if (portions < 0) portions = 1
customPortion.value = portions
portionTxt.textContent = (portions > 1) ? ' portionen' : ' portion'
}
refreshBt.onclick = () =>
{
zutatendata.forEach(zd=>
{
let val = zd.def.value * customPortion.valueAsNumber
if (zd.def.tenths > 0) val += customPortion.valueAsNumber / zd.def.tenths
zd.textContent = val.toFixed(2)
setTenths(zd)
})
}
:root{
--nice_bg : #cbdbf0af;
}
* {
font-family : Arial, Helvetica, sans-serif;
font-size : 20px;
}
#portions input {
width : 4em;
padding : .1em .4em;
text-align : right;
box-sizing : border-box;
direction : rtl;
border : none;
background : var(--nice_bg);
}
#portions button {
cursor : pointer;
background : transparent;
color : green;
border : none;
padding : .2em .4em;
}
#portions button:hover {
background : var(--nice_bg);
}
.grid {
display: grid;
grid-template-columns: 8em 8em;
}
.grid div {
margin-top : .2em;
height : 1.8em;
line-height : 1.8em;
background : var(--nice_bg);
padding-left : .3em;
}
div.item > p {
display : inline-block;
padding : 0 .1em;
margin : 0;
font-weight : bold;
width : 4em;
text-align : right;
}
/*
.zutaten-data::after {
content : attr(data-tenths);
display : inline-block;
width : 1.7em;
text-align : left;
}
*/
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" >
<p id="portions">
Für
<input type="number" value="1" id="custom-portion" min="1" step="1" >
<button id="refresh-portion">
<i class="fas fa-sync"></i>
</button>
Portionen
</p>
<div class="grid">
<div class="item">Möhren</div>
<div class="item">
<p class="zutaten-data">0.33</p>kg
</div>
<div class="item">Kartoffeln</div>
<div class="item">
<p class="zutaten-data">2</p>Stück
</div>
<div class="item">Brühe</div>
<div class="item">
<p class="zutaten-data">1</p>l
</div>
<div class="item">Butter</div>
<div class="item">
<p class="zutaten-data">5</p>kg
</div>
</div>
Upvotes: 1
Reputation: 1298
Cleaned up your html and added the dafault values as data attributes.
Also removed the refresh button, since the values are updated on change.
The input's minimum value is 0, so you don't get wierd values.
const portion = document.querySelector("#portion");
portion.addEventListener("change", (e) => {
const amounts = document.querySelectorAll(".amount")
.forEach(amount => amount.textContent = Number(amount.dataset.default) * e.target.value);
});
#portions {
display: flex;
height: 2rem;
padding: 0 1rem;
align-items: center;
}
#portions>* {
margin: 0 1rem;
}
.ingredients {
display: flex;
flex-direction: column;
padding: 0 2rem;
}
.item {
display: flex;
justify-content: space-between;
}
.amount {
text-align: right;
}
<div id="portions">
<span>Für</span>
<input id="portion" type="number" value="1" min="0" />
<p>Portionen</p>
</div>
<ul class="ingredients">
<li class="item">
<div class="title">Mohren</div>
<div class="amount" data-default="0.33">0.33</div>
<span class="unit">kg</span>
</li>
<li class="item">
<div class="title">Kartoffeln</div>
<div class="amount" data-default="2">2</div>
<span class="unit">Stück</span>
</li>
<li class="item">
<div class="title">Bruhe</div>
<div class="amount" data-default="1">1</div>
<span class="unit">l</span>
</li>
<li class="item">
<div class="title">Butter</div>
<div class="amount" data-default="5">5 kg</div>
<span class="unit">kg</span>
</li>
</ul>
Upvotes: 1
Reputation: 452
You need to go over the items when you click. Keep in mind that without storing the values initially somewhere in an array or an object will make this function work properly just one. Since you are reading values form html and thus for example 1st one will be 2 -> 0.66, but then if you click again it will become 0.66 * 2, and that is not correct.
var refresh = document.getElementById("refresh-portion");
refresh.onclick = function () {
var input = document.getElementById("custom-portion").value;
var zutatendata = document.getElementsByClassName("zutaten-data");
for (var i = 0; i < zutatendata.length; i++) {
var array = [zutatendata[i]];
console.log(zutatendata[i]);
array.forEach(function (elem) {
var initial = elem.innerHTML;
var result = initial * Number(input);
if (result.toString().length > 5) {
result = result.toFixed(1);
}
elem.innerHTML = result + " ";
});
}
};
You can store the base values in an object and then use those values to multiply by the input value.
Upvotes: 0