CRAL Civitavecchia
CRAL Civitavecchia

Reputation: 13

Dynamic sum of elements inside a loop

I have a problem with my wp site... I have a post-type called "sezione". For each new post I create, a row is automatically added in a specific page. Each row has three input fields: in the first one I have to write number of males, in the second the number of females, while the third one is a readonly field with an automatic sum. I'm using a while loop like this:

<?php
    query_posts( array(
        'post_type' => 'sezione',
        'orderby' => 'meta_value_num',
        'meta_key' => 'n-sez',
        'order' => 'ASC'
    ) );   

    if( have_posts() ) : while( have_posts() ) : the_post();
?>

    <?php global $post; ?>
    <?php
    $nsez = get_post_meta($post->ID, 'n-sez', true);
    ?>

    <div class="row-100perc-bord">
    Sezione <?php echo $nsez; ?>
    <div class="affl-box">

    <input type="text" name="uomini<?php echo $nsez; ?>" id="uomini" class="field-50-u" value="<?php if ( isset ( $aff1t_stored_meta['uomini' . $nsez] ) ) echo $aff1t_stored_meta['uomini' . $nsez][0]; ?>" onblur="recalculateSummf();" />

    <input type="text" name="donne<?php echo $nsez; ?>" id="donne" class="field-50-d" value="<?php if ( isset ( $aff1t_stored_meta['donne' . $nsez] ) ) echo $aff1t_stored_meta['donne' . $nsez][0]; ?>" onblur="recalculateSummf();" />

    <input readonly type="text" name="totmf<?php echo $nsez; ?>" id="totmf" class="field-50-mf" value="<?php if ( isset ( $aff1t_stored_meta['totmf' . $nsez] ) ) echo $aff1t_stored_meta['totmf' . $nsez][0]; ?>" />

The loop works correctly. What I'm trying to do is to make the third field in each row display the sum of the values of the other two. I'm using this function:

function recalculateSummf()
{
    var num1 = parseInt(document.getElementById("uomini").value);
    var num2 = parseInt(document.getElementById("donne").value);
    document.getElementById("totmf").value = num1 + num2;

}

But it works just in the first row. How can make this script works in all rows? May anybody help me in solving this issue? Thanks a lot!​

Upvotes: 0

Views: 82

Answers (1)

Just a student
Just a student

Reputation: 11040

Problem

Main issue: you should give an ID only once to any element on any page, ever.

If I understand you correctly, your HTML will look something like this.

<div class="row-100perc-bord">Sezione X
  <div class="affl-box">
    <input type="text" name="uominiX" id="uomini" class="field-50-u"
           value="" onblur="recalculateSummf();" />
    <input type="text" name="donneX" id="donne" class="field-50-d"
           value="" onblur="recalculateSummf();" />
    <input readonly type="text" name="totmfX" id="totmf" class="field-50-mf"
           value="" />
  </div>
</div>
<!-- ... more of the above, but with Sezione Y, Z, ... -->

Now, all of those have ID uomini, donne and totmf. Using getElementById to find them will always return the first ones.

Solution 1: different IDs

Easy fix: add $nsez to the IDs and use that. You will have to add $nsez as an argument to your call of recalculateSummf in the onblur if you want to do this.

function recalculateSummf(sezione) {
  var m = parseInt(document.getElementById('uomini' + sezione).value),
      f = parseInt(document.getElementById('donne' + sezione).value);
  document.getElementById('totmf' + sezione).value = m + f;
}

for (let sezione of ['X', 'Y', 'Z']) {
  recalculateSummf(sezione);
}
<div class="row-100perc-bord">Sezione X
  <div class="affl-box">
    <input type="text" name="uominiX" class="field-50-u" id="uominiX"
           value="10" onblur="recalculateSummf('X');" />
    <input type="text" name="donneX" class="field-50-d" id="donneX"
           value="42" onblur="recalculateSummf('X');" />
    <input readonly type="text" name="totmfX" class="field-50-mf" id="totmfX" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Y
  <div class="affl-box">
    <input type="text" name="uominiY" class="field-50-u" id="uominiY"
           value="16" onblur="recalculateSummf('Y');" />
    <input type="text" name="donneY" class="field-50-d" id="donneY"
           value="64" onblur="recalculateSummf('Y');" />
    <input readonly type="text" name="totmfY" class="field-50-mf" id="totmfY" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Z
  <div class="affl-box">
    <input type="text" name="uominiZ" class="field-50-u" id="uominiZ"
           value="1" onblur="recalculateSummf('Z');" />
    <input type="text" name="donneZ" class="field-50-d" id="donneZ"
           value="9" onblur="recalculateSummf('Z');" />
    <input readonly type="text" name="totmfZ" class="field-50-mf" id="totmfZ" />
  </div>
</div>

Solution 2: update everything all the time

Alternative, that will update all sums on the page, is the following.

Loop over all .row-100perc-bord containers and fix within that container.

function recalculateSummf() {
  var containers = document.querySelectorAll('.row-100perc-bord');
  for (let container of containers) {
    let m = parseInt(container.querySelector('.field-50-u').value),
        f = parseInt(container.querySelector('.field-50-d').value);
    container.querySelector('.field-50-mf').value = m + f;
  }
}
  
recalculateSummf();
<div class="row-100perc-bord">Sezione X
  <div class="affl-box">
    <input type="text" name="uominiX" class="field-50-u"
           value="10" onblur="recalculateSummf();" />
    <input type="text" name="donneX" class="field-50-d"
           value="42" onblur="recalculateSummf();" />
    <input readonly type="text" name="totmfX" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Y
  <div class="affl-box">
    <input type="text" name="uominiY" class="field-50-u"
           value="16" onblur="recalculateSummf();" />
    <input type="text" name="donneY" class="field-50-d"
           value="64" onblur="recalculateSummf();" />
    <input readonly type="text" name="totmfY" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Z
  <div class="affl-box">
    <input type="text" name="uominiZ" class="field-50-u"
           value="1" onblur="recalculateSummf();" />
    <input type="text" name="donneZ" class="field-50-d"
           value="9" onblur="recalculateSummf();" />
    <input readonly type="text" name="totmfZ" class="field-50-mf" />
  </div>
</div>

Solution 3: pass context

If you add a this argument to your recalculateSummf() calls, you receive a reference to the input element that was blurred in your function. You can use this to update only the inputs that were changed.

function recalculateSummf(input) {
  var container = input.parentNode,
      m = parseInt(container.querySelector('.field-50-u').value),
      f = parseInt(container.querySelector('.field-50-d').value);
  container.querySelector('.field-50-mf').value = m + f;
}
  
var inputs = document.querySelectorAll('.field-50-u');
for (let input of inputs) {
  recalculateSummf(input);
}
<div class="row-100perc-bord">Sezione X
  <div class="affl-box">
    <input type="text" name="uominiX" class="field-50-u"
           value="10" onblur="recalculateSummf(this);" />
    <input type="text" name="donneX" class="field-50-d"
           value="42" onblur="recalculateSummf(this);" />
    <input readonly type="text" name="totmfX" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Y
  <div class="affl-box">
    <input type="text" name="uominiY" class="field-50-u"
           value="16" onblur="recalculateSummf(this);" />
    <input type="text" name="donneY" class="field-50-d"
           value="64" onblur="recalculateSummf(this);" />
    <input readonly type="text" name="totmfY" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Z
  <div class="affl-box">
    <input type="text" name="uominiZ" class="field-50-u"
           value="1" onblur="recalculateSummf(this);" />
    <input type="text" name="donneZ" class="field-50-d"
           value="9" onblur="recalculateSummf(this);" />
    <input readonly type="text" name="totmfZ" class="field-50-mf" />
  </div>
</div>

Solution 4: derive context

When you attach your event handlers programmatically, your function will receive an event argument by default, that carries the target of the event, the input element that was blurred, with it. You can use this if you want, so you don't need to add an onblur in your HTML at all.

function recalculateSummf(event) {
  var input = event.target,
      container = input.parentNode;
  update(container);
}

function update(container) {
  var m = parseInt(container.querySelector('.field-50-u').value),
      f = parseInt(container.querySelector('.field-50-d').value);
  container.querySelector('.field-50-mf').value = m + f;
}

// attach event handlers
var inputs = document.querySelectorAll('.field-50-u, .field-50-d');
for (let input of inputs) {
  input.addEventListener('blur', recalculateSummf);
}

// update values initially
var containers = document.querySelectorAll('.row-100perc-bord');
for (let container of containers) {
  update(container);
}
<div class="row-100perc-bord">Sezione X
  <div class="affl-box">
    <input type="text" name="uominiX" class="field-50-u"
           value="10" />
    <input type="text" name="donneX" class="field-50-d"
           value="42" />
    <input readonly type="text" name="totmfX" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Y
  <div class="affl-box">
    <input type="text" name="uominiY" class="field-50-u"
           value="16" />
    <input type="text" name="donneY" class="field-50-d"
           value="64" />
    <input readonly type="text" name="totmfY" class="field-50-mf" />
  </div>
</div>
<div class="row-100perc-bord">Sezione Z
  <div class="affl-box">
    <input type="text" name="uominiZ" class="field-50-u"
           value="1" />
    <input type="text" name="donneZ" class="field-50-d"
           value="9" />
    <input readonly type="text" name="totmfZ" class="field-50-mf" />
  </div>
</div>

Upvotes: 1

Related Questions