hubschrauber
hubschrauber

Reputation: 55

Only last element of array works

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.

Default value

When the portions are changed

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

Answers (3)

Mister Jojo
Mister Jojo

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

Invizi
Invizi

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

Dragos Podaru
Dragos Podaru

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

Related Questions