2
\$\begingroup\$

I have this function that allows the user to supply a number and that many rows are appended to the existing table:

$('#add-row').click(function() {
    if($('#insert-rows-amnt').val() < 1) {
        alert('Please enter number of rows to insert');
    } else {
        for(i = 1; i <= $('#insert-rows-amnt').val(); i++) {
            $('table tr:last').clone(true).appendTo("tbody");
        }
    }
});

Where #insert-rows-amnt is an input field and in this case contains the number 5000.

This currently takes 48 seconds on my machine to add 5,000 additional rows - is there a quicker way of doing this?

I'm not against putting a loading message somewhere if there isn't a way to significantly increase the speed. I'm just curious if this is the most efficient way.

\$\endgroup\$
4
\$\begingroup\$

I'd do it like this:

$('#add-row').click(function() {
  var $tbody, $row, additionalRows;
  var numNewRows = parseInt($('#insert-rows-amnt').val(), 10);

  if (isNaN(numNewRows) || numNewRows <= 0) {
    alert('Please enter number of rows to insert');
  } else {

    $tbody = $('table tbody');
    $row = $tbody.find('tr:last');

    additionalRows = new Array(numNewRows + 1).join($row[0].outerHTML);

    $tbody.append(additionalRows);
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <thead>
    <th>Col1</th>
    <th>Col2</th>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>2</td>
    </tr>
  </tbody>
</table>
<input type="number" id="insert-rows-amnt" />
<button id="add-row" type="button">Add Rows</button>

Firstly, don't trust user input, verify you actually have a number, try to parse it and be explicit. Always use the radix, parseInt() can try to be too clever for its own good. The result could be NaN or a number, so check for these:

var numNewRows = parseInt($('#insert-rows-amnt').val(), 10);

if (isNaN(numNewRows) || numNewRows <= 0) {

If you're using JQuery, cache found objects, finding them isn't free, a search is done every time you pass a string selector, so if you've already found it, use it. This ins't going to set the world on fire, but it's a good habit to get into.

Onto the meat of the method, there are two methods for inserting HTML quickly. HTML updates are like Excel Worksheet updates, no small amount of processing goes on when you change the dom. Most of the time this is negligible, but if you're inserting a huge amount of html it's something to be aware of.

  • You could create a document fragment and append nodes to it. This gives the benefit of working with objects rather than strings so is less error prone. The essence of it is you create the fragment in memory and insert it once.

  • You can create the html as a string and insert it, it's a single dom update, but more error prone as you're string building. This case is simple enough for it to be a good option though.


new Array(numNewRows + 1).join($row[0].outerHTML);

This replicates a string x number of times. It works by initialising an array of x dimensions and then joining it using a given string as the text between the elements.

\$\endgroup\$
1
\$\begingroup\$

Storing $('#insert-rows-amt').val() in a variable should make it faster. Also, you could generate an HTML string in the loop and then append that HTML to the tbody at the end.

$('#add-row').click(function() {
var numRows = $('#insert-rows-amnt').val(); 
if(numRows < 1) {
    alert('Please enter number of rows to insert');
} else {
    var htmlString = ''; 
    var clonedRow = $('table tr:last')[0].outerHTML; 
    for(var i = 1; i <= numRows; i++) {
        htmlString += clonedRow; 
    }
    htmlString.appendTo("tbody"); 
}
});
\$\endgroup\$
  • \$\begingroup\$ Tried saving the number to variable instead but it made no difference unfortunately, I can't create a HTML string either because the table layout is dynamic and may change so I don't know what the html will be at the time the user presses the button \$\endgroup\$ – Sam Dec 7 '16 at 15:17
  • \$\begingroup\$ Can you do something like var clonedRow = $('table tr:last').clone(true); \$\endgroup\$ – Michael Auger Dec 7 '16 at 15:22
  • \$\begingroup\$ And then in your loop do a htmlString += clonedRow \$\endgroup\$ – Michael Auger Dec 7 '16 at 15:23
  • \$\begingroup\$ Edited the answer with the code above. \$\endgroup\$ – Michael Auger Dec 7 '16 at 15:30
  • \$\begingroup\$ would clonedRow be implicitly cast to a string though in order to append it to htmlString? Or can you apply some sort of .toString() method in there? \$\endgroup\$ – Sam Dec 7 '16 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.