2

I have created a form where you can add or delete table rows using javascript and jQuery. I would like to know how I can obtain and set the index for each table row such that sequence is maintained even if I were to delete and element from the middle of the table. The table is of the form:

<thead>
    <tr>
        <th>Index</th>
        <th>Name</th>
        <th>Property</th>
        <th>Edit/Delete</th>
    </tr>
</thead>
<tbody>
    <tr>
        <td class="index">Index goes here (1)</td>
        <td>NameOne</td>
        <td>PropOne</td>
        <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
    </tr>
    <tr>
        <td class="index">2</td>
        <td>NameTwo</td>
        <td>PropTwo</td>
        <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
    </tr>
    <tr>
        <td class="index">3</td>
        <td>NameThree</td>
        <td>PropThree</td>
        <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
    </tr>
</tbody>

Now what I want to achieve is if I were to delete the second row, the index of the previous third row should automatically change to 2 and if I were to add new element it should automatically take the index value of 3 and so on.

I tried to achieve this with:

function setIndex(){
   $("td.index").each(function(index) {
       $(this).text(++index);
   });
}

But when I used the above function although the initial index when the elements were added printed properly the index wouldn't update properly when I called the function again after deleting or editing a row( I deleted the row using jQuery remove).

Also I am creating the new table rows with jQuery append().

I think that although I used remove() they don't get deleted completely as when I used a console.log("test") statement inside the setIndex() although "test" was only supposed to be printed twice(I had initially created 3 rows and deleted one of them) it go printed thrice signifying that there were 3 tr.index's.

Please help me solve the same.

Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
Nagarjun Prasad
  • 802
  • 3
  • 17
  • 31

3 Answers3

10

You can use the CSS counter-reset and content properties.

The counter-reset property allows for automatic numbering of elements.
It works on any element.

The counter-reset property is used to reset a CSS counter to a given value. It sets a named counter to a specific value.

body{
  counter-reset: Serial;           /* Set the Serial counter to 0 */
}

table{
  border-collapse: collapse;
}

tr td:first-child:before{
  counter-increment: Serial;      /* Increment the Serial counter */
  content:counter(Serial);        /* Display the counter */
}
<table border="1">
  <thead>
    <tr>
      <th>Automatic Serial number</th>
      <th>Column 1</th>
      <th>Column 2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
    <tr>
      <td></td>
      <td>Column 1</td>
      <td>Column 2</td>
    </tr>
  </tbody>
</table>
  • 2
    Needs explanations... But yeah. Interesting CSS way. Don't just dump code like this. Explanations are the main reason why people come on StackOverflow. – Louys Patrice Bessette Jan 09 '18 at 05:04
  • I flagged your answer as "Very low quality"... And, if you don't edit with some explanations, It has all chances to be removed from review. Welcome to SO! -- That would be sad, since your answer is better than mine. – Louys Patrice Bessette Jan 09 '18 at 05:16
  • 1
    Okay... You edited. Your answer **really is better than mine** and you're new to SO... So I will help you a bit. With your agreement (if you answer me yes in comment) I will edit your answer very slightly. And please, don't refer to W3schools as the site is outdated (or completely wrong) most of the time. – Louys Patrice Bessette Jan 09 '18 at 05:30
  • ;) @Will. That's is why I like SO so much. – Louys Patrice Bessette Jan 09 '18 at 06:00
4

EDIT

Praven Prajapati's answer surprised me.
I didn't know that very cool CSS feature.

Praven's answer really is better.

And if you need to refer to that number in a JS/jQuery code... And can't get it because it's a not in DOM pseudo-element... Then use .index() for that particular part of your code! Let CSS work on the rest.




jQuery way:

You need to refer to a row index... Use the .index() method.

Then on .delete click (I'm sure you can delete the row), just call a function to update the row index cell using that method.

Same after appending a new row...

Important
Use delegation with .on() on the classes present in your table rows, since you add new rows that are not present on page load code parsing. ;)

That is a Will's catch. (See his answer)

function updateRowCount(){
  $("table tbody tr").each(function(){
    $(this).find(".index").html($(this).index());
  });
}

// Run on load
updateRowCount();

$(document).on("click",".delete",function(){  // Use delegation here!
  $(this).parents("tr").remove();
  updateRowCount();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table>
  <thead>
      <tr>
          <th>Index</th>
          <th>Name</th>
          <th>Property</th>
          <th>Edit/Delete</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td class="index"></td>
          <td>NameOne</td>
          <td>PropOne</td>
          <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
      </tr>
      <tr>
          <td class="index"></td>
          <td>NameTwo</td>
          <td>PropTwo</td>
          <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
      </tr>
      <tr>
          <td class="index"></td>
          <td>NameThree</td>
          <td>PropThree</td>
          <td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
      </tr>
  </tbody>
</table>
Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
4

I'd suggest using the the CSS/Counter answer by @pravin-prajapati because it requires no JS overhead and will scale easily.

Was interested in what the problem was with your code because it looked fine to me so rebuilt it. Seemed to be working fine.

I'm guessing the problem is actually in the way you're attaching code to the .delete click, especially if you're adding new rows or recreating rows after an edit.

If you add new rows after the initial document.ready (or window.onload...) has attached the callback to the existing .delete elements, it will not automatically attach the callback to the new .delete elements.

In other words, don't do this in your init:

$('.delete').on('click', function(){
  // do stuff
});

because that will only work for .delete elements that exist during the init. There are a few ways around this but an easy way is to listen for click events on a parent of the rows and then filter them to your actual target before running the callback. jQuery's on method makes this easy.

Below is an example with the table as the event listener.

EDIT:
If, for some reason, this isn't possible, you might look into using jQuery.clone() and setting withDataAndEvents and/or deepWithDataAndEvents to true like $('tr.template').clone(true, true);. This will copy the <tr> and any event handlers attached to it (first 'true') and any event handlers attached to any of its child elements (second 'true'). jQuery Clone Docs

$(document).ready(function(){

  // your function, copied 100%
  function setIndex(){
   $("td.index").each(function(index) {
       $(this).text(++index);
   });
  }

  // set the index to begin with. Note the last three 
  // row indexes are actually empty in the sample HTML
  setIndex();

  // Move the click listener to the table. 
  $('table').on('click', '.delete', function(){    
    // remove the tr...
    $(this).parents('tr').remove();
    //... and reset the index
    setIndex();  
  })
  
});
table {
  font-family: sans-serif;
  margin: 10px;
}
table td {
  border: 1px solid #ccc;
  padding: 10px;
}
.delete {
  color: red;
  cursor: pointer;
  font-size: 80%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <thead>
    <tr>
        <th>Index</th>
        <th>Name</th>
        <th>Property</th>
        <th>Edit/Delete</th>
    </tr>
</thead>
<tbody>
    <tr>
        <td class="index">Index here</td>
        <td>NameOne</td>
        <td>PropOne</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
    <tr>
        <td class="index">2</td>
        <td>NameTwo</td>
        <td>PropTwo</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
    <tr>
        <td class="index">3</td>
        <td>NameThree</td>
        <td>PropThree</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
      <tr>
        <td class="index"></td>
        <td>Name 4</td>
        <td>Prop 4</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
      <tr>
        <td class="index"></td>
        <td>Name 5</td>
        <td>Prop 5</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
      <tr>
        <td class="index"></td>
        <td>Name 6</td>
        <td>Prop 6</td>
        <td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
    </tr>
</tbody>
</table>
Will
  • 4,075
  • 1
  • 17
  • 28
  • Hey... lollll!! That's soooo `true`! I'm amazed about the situation. Nice catch. – Louys Patrice Bessette Jan 09 '18 at 06:26
  • @LouysPatriceBessette :). Edited with a new stab at the actual problem he's having. Reckon it's event delegation. – Will Jan 09 '18 at 06:31
  • Ho Man... That was deeply hidden. Why didn't I have think about it?!? I voted x questions for a close because it's a dup of [that](https://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements)! --- I'm **sure** you finally found OP's problem. I just can't upvote more than once. ;) – Louys Patrice Bessette Jan 09 '18 at 06:42
  • The fun think is that I assumed its delete function... And made the exact same error because I did not care about the appending... I will edit again lol! – Louys Patrice Bessette Jan 09 '18 at 06:49