Home > OS >  Table performs a calculation only in the first row
Table performs a calculation only in the first row

Time:07-06

I've build an interactive table where you can manually add and delete rows (with bootstrap). This all works fine!

Now comes the problem: I have some input fields with an automatic calculation. But the problem is, the table only performs the calculation in the standard row, and not in the addable row.

This is the .HTML and .JS of the table:

jQuery(document).delegate('a.add-record_venr', 'click', function(e) {
    e.preventDefault();
    var content = jQuery('#sample_table_venr tr'),
    size = jQuery('#tbl_posts_venr >tbody >tr').length   1,
    element = null,
    element = content.clone();
    element.attr('id', 'rec_venr-' size);
    element.find('.delete-record_venr').attr('data-id', size);
    element.appendTo('#tbl_posts_body_venr');
    element.find('.sn').html(size);
    });
    jQuery(document).delegate('a.delete-record_venr', 'click', function(e) {
    e.preventDefault();
    var didConfirm = confirm("Ventilatierooster verwijderen?");
    if (didConfirm == true) {
    var id = jQuery(this).attr('data-id');
    var targetDiv = jQuery(this).attr('targetDiv');
    jQuery('#rec_venr-'   id).remove();
    
    //regnerate index number on table
    $('#tbl_posts_body_venr tr').each(function(index) {
    //alert(index);
    $(this).find('span.sn').html(index 1);
    });
    return true;
    } else {
    return false;
    }
    });
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

  

<div >
<a  data-added="0"><i ></i> Toevoegen </a>
            </div>

            <div  style="margin-left:1px ;">
                  <table  id="tbl_posts_venr">
                    <thead style="background-color:#c7c8cc; width: 100% !important;">
                      <tr>
                        <th style="width:30px ;">Merk</th>
                        <th >Lengte rooster</th>
                        <th >Type rooster</th>
                        <th >Capaciteit [dm3/s]</th>
                        <th >Max. capaciteit [dm3/s]</th>
                        <th >Verblijfsgebied</th>
                        <th >Verblijfsruimte</th>
                        <th >Verwijderen</th>
                      </tr>
                    </thead>
                    <tbody id="tbl_posts_body_venr">
                      <tr id="rec_venr-1">
                        <td><input  type="text" name="" placeholder="Merk"> </td>
                        <td><input  type="text" name="lrooster" id="lrooster" placeholder="Lengte rooster"></td>
                        <td><input  type="text" name="" placeholder="Type rooster"></td>
                        <td><input  type="text" name="caprooster" id="caprooster" placeholder="Capaciteit [dm3/s]"></td>
                        <td><input  type="text" name="maxcaprooster" id="maxcaprooster" placeholder="Max. capaciteit [dm3/s]"></td>
                        <td><input  type="text" name="" placeholder="Verblijfsgebied"></td>
                        <td><input  type="text" name="" placeholder="Verblijfsruimte"></td>
                        <td><a  data-id="1"><i ></i></a></td>
                      </tr>
                    </tbody>
                  </table>
                </div>


              <div style="display:none;">
              <table id="sample_table_venr">
                <tr id="">
                  <td><input  type="text" name="" placeholder="Merk"> </td>
                  <td><input  type="text" name="lrooster" id="lrooster" placeholder="Lengte rooster"></td>
                  <td><input  type="text" name="" placeholder="Type rooster"></td>
                  <td><input  type="text" name="caprooster" id="caprooster" placeholder="Capaciteit [dm3/s]"></td>
                  <td><input  type="text" name="maxcaprooster" id="maxcaprooster" placeholder="Max. capaciteit [dm3/s]"></td>
                  <td><input  type="text" name="" placeholder="Verblijfsgebied"></td>
                  <td><input  type="text" name="" placeholder="Verblijfsruimte"></td>
                  <td><a  data-id="1"><i ></i></a></td>
               </tr>
             </table>
             </div>
          </div>
      </div>
      </table>

This is the JavaScript of the calculation:

$('#lrooster, #caprooster').keyup(function(){
    var lrooster = parseFloat($('#lrooster').val());
    var caprooster = parseFloat($('#caprooster').val());

    $('#maxcaprooster').val(lrooster * caprooster );
});

If the linked code doesnt work good, here 's a fiddle

thanks in advance!!

CodePudding user response:

The main issue in your code is because you're using id attributes in the HTML of each row which you clone. This causes duplicates which is invalid - id attribute values must be unique within the DOM. To fix this use common class attributes instead, to group elements by behaviour.

That said, there's also several other issues in your code which need to be addressed:

  • Don't generate incremental id attributes at runtime. It's an anti-pattern which causes more bugs, additional complexity and makes the code more difficult to maintain. User common classes and DOM traversal methods to relate elements to each other as required.
  • The version of jQuery you're using is outdated. Update to 3.6.0
  • Bootstrap is outdated too, however I'll leave this as an exercise for the OP to update as there may be some breaking changes in moving to v5.
  • Don't use inline styling on elements - use a stylesheet.
  • delegate() was deprecated from jQuery a long time ago. Use on() instead.
  • Use the input event instead of keyup. The latter does not listen for content added to the form control using the mouse, the former does.
  • Use the $ variable, where available to make your code more succinct. You can alias it through the document.ready handler if necessary.
  • Use a <template /> element to store HTML which will be used to create dynamic content at runtime.
  • Give all form controls a name, even if you're submitting the form via AJAX.
  • Use data() to access data attributes, not attr().
  • Create delegated event handlers for all dynamic content (ie. the lrooster and caprooster inputs)

With all those issues corrected, here's a working example:

jQuery($ => {
  let rowHtml = $('#sample_table_venr').html();
  let appendRow = () => $('#tbl_posts_body_venr').append(rowHtml);

  appendRow(); // add a row on page load

  $(document).on('click', 'a.add-record_venr', e => {
    e.preventDefault();
    appendRow(); // add a row on button click
  });

  $(document).on('click', 'a.delete-record', e => {
    e.preventDefault();

    if (confirm("Ventilatierooster verwijderen?")) {
      $(e.target).closest('tr').remove();
    }
  });

  $(document).on('input', '.lrooster, .caprooster', e => {
    let $row = $(e.target).closest('tr');
    let lrooster = parseFloat($row.find('.lrooster').val()) || 0;
    let caprooster = parseFloat($row.find('.caprooster').val()) || 0;
    $row.find('.maxcaprooster').val(lrooster * caprooster);
  });
});
.container-fluid {
  margin-left: 1px;
}

#tbl_posts_venr thead {
  background-color: #c7c8cc;
  width: 100% !important;
}

#tbl_posts_venr thead th:first-child {
  width: 30px;
}


/* only for this demo, to help the content fit in the snippet preview */
input {
  width: 75px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

<div >
  <a  data-added="0">
    <i ></i> Toevoegen
  </a>
</div>
<div >
  <table  id="tbl_posts_venr">
    <thead>
      <tr>
        <th>Merk</th>
        <th>Lengte rooster</th>
        <th>Type rooster</th>
        <th>Capaciteit [dm3/s]</th>
        <th>Max. capaciteit [dm3/s]</th>
        <th>Verblijfsgebied</th>
        <th>Verblijfsruimte</th>
        <th>Verwijderen</th>
      </tr>
    </thead>
    <tbody id="tbl_posts_body_venr"></tbody>
  </table>
</div>

<template id="sample_table_venr">
  <tr>
    <td><input  type="text" name="merk" placeholder="Merk"> </td>
    <td><input  type="text" name="lrooster" placeholder="Lengte rooster"></td>
    <td><input  type="text" name="typerooster" placeholder="Type rooster"></td>
    <td><input  type="text" name="caprooster" placeholder="Capaciteit [dm3/s]"></td>
    <td><input  type="text" name="maxcaprooster" placeholder="Max. capaciteit [dm3/s]"></td>
    <td><input  type="text" name="verblijfsgebied" placeholder="Verblijfsgebied"></td>
    <td><input  type="text" name="verblijfsruimte" placeholder="Verblijfsruimte"></td>
    <td>
      <a  data-id="1">
        <i ></i>
      </a>
    </td>
  </tr>
</template>

CodePudding user response:

Problem

#ids Must be Unique, Duplicate #ids Invalidates HTML

Never clone (or replicate) any element with an #id. In the OP (Original Post), there are 3 #ids being duplicated each time a row is added, which are #lrooster, #caprooster, and #maxcaprooster. When the browser is directed to find an #id, it naturally assume there's only one #id to find and it stops once the first one is found. That is why the first row is the only one that works.

Solution

Avoid using #id and use .class instead. This is especially important to remember if you are using jQuery because of it's versatility. In fact, you are more than likely to hinder the functionality of your code should you use #id so generously.

Changes

Upgrades

  • jQuery 3.6.0 Slim
  • Bootstrap 5.1.3 CSS and JavaScript
  • Bootstrap Icons 1.8.3 CSS

Replacements

  • .delegate() is now .on()
  • #ids are now .classes
  • #lrooster, #caprooster, and #maxcaprooster are now .lrooster, .kaprooser, and .maxkaprooster and each are type="number"
  • confirm() is now a Bootstrap modal-dialog
  • Use of data-* to determine index position for serializing #ids is a lot of bookkeeping, so it is now a single .active class assigned to a <tr>.
  • BS classes are assigned throughout the HTML.

Details are commented in example

// Hide button.remove of the first row
$('.remove').hide();

// Click button.add...
$('.add').on('click', function(e) {
  // Clone the first row of tbody
  let copy = $('tbody tr:first').clone(true, true);
  // Clear all of it's inputs
  copy.find('input').val('');
  // Reset number inputs to 0
  copy.find('[type="number"]').val('0');
  // Show button.remove 
  copy.find('.remove').show()
  // Add clone to tbody
  $('tbody').append(copy);
});

// Click button.remove...
$('.remove').on('click', function(e) {
  // Add .active to the <tr> that contains clicked button
  $(this).closest('tr').addClass('active');
});

// Click button.confirm...
$('.confirm').on('click', function(e) {
  // Remove tr.active
  $('.active').remove();
});

// Click button.close...
$('.close').on('click', function(e) {
  // Remove .active from <tr>
  $('tr').removeClass('active');
});

// Enter data in any number input...
$('[type="number"]').on('input', function() {
  /*
  Reference <tr> that contains input that the user is currently
  interacting with
  */
  const $row = $(this).closest('tr');
  // Convert the values of .lrooster and .kaprooster of current row
  let L = parseFloat($row.find('.lrooster').val());
  let K = parseFloat($row.find('.kaprooster').val());
  /*
  If the product of .lrooster and .kaprooster equals NaN, retrun 0
  otherwise the product
  */
  let rooster = NaN ? 0 : L * K;
  // Display result in .maxkaprooster
  $row.find('.maxkaprooster').val(rooster);
});
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css" rel="stylesheet" />

<main >
  <section class='row'>
    <menu class='d-flex flex-row-reverse mt-1 mb-1'>
      <button  style='width: max-content;'><i ></i> Toevoegen </button>
    </menu>
    <table >
      <thead class='table table-dark'>
        <tr>
          <th>Brand</th>
          <th>Grille Length</th>
          <th>Grille Type</th>
          <th>Capacity [dm³/s]</th>
          <th>Max. [dm³/s]</th>
          <th>Home</th>
          <th>Room</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><input  type="text"></td>
          <td><input name="lrooster"  type="number" value='0'></td>
          <td><input  type="text"></td>
          <td><input name="kaprooster"  type="number" value='0'></td>
          <td><input name="maxkaprooster"  type="number" readonly value='0'></td>
          <td><input  type="text"></td>
          <td><input  type="text"></td>
          <td><button  data-bs-toggle="modal" data-bs-target="#modal"><i ></i></button></td>
        </tr>
      </tbody>
    </table>

  </section>

  <aside id='modal'  tabindex="-1">
    <dialog  open>
      <article >
        <header >
          <h5 >Confirm Row Removal</h5>
          <button  data-bs-dismiss="modal">X</button>
        </header>
        <section >
          <p>Ventilatierooster verwijderen?</p>
        </section>
        <footer >
          <button  data-bs-dismiss="modal">Cancel</button>
          <button  data-bs-dismiss="modal">Remove Row</button>
        </footer>
      </article>
    </dialog>
  </aside>

</main>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

  • Related