DotNetCurry Logo

Table Manipulation using jQuery - Performance Tips and Best Practices

Posted by: Suprotim Agarwal , on 11/17/2013, in Category jQuery and ASP.NET
Views: 88087
Abstract: Some performance tips and best practices while manipulating HTML Tables in jQuery

HTML Tables can be pretty boring to look at! Although you can add a dash of CSS and beautify them, users demand more interactivity by representing and manipulating tables, at runtime.

Some common manipulation tasks performed with tabular data is adding and deleting rows, sorting and paginating data. JavaScript is the obvious choice to achieve these operations, but many lines of code need to be written in plain JavaScript, to traverse and manipulate the DOM tree. DOM operations can get tricky at times. To add to our woes, writing code that works cross-browser is tedious, especially when performing advanced manipulations. This is where a JavaScript library like jQuery comes in handy.

jQuery makes DOM operations less scary. It provides an abstraction layer and allows you to work with the DOM, without having to know every little thing about it. One of the biggest benefits of using jQuery is that it handles a lot of cross-browser issues for you.

In this article, I will share some jQuery techniques to manipulate Table Data. I will also share performance tips about jQuery selectors, caching selectors, and writing terse, efficient code. By using these tips, your code will be more effective. I am assuming you have a little experience working with jQuery. If not, Learning jQuery is a good place to start with

This article is based on my upcoming jQuery Book The Absolutely Awesome jQuery CookBook where I share similar self-contained recipes that you can easily incorporate in your websites or projects.

 

Defining the Table Structure

The first step to write effective jQuery is to write well-formed HTML. Here’s a subset of a well-defined HTML Table markup:


   

       

           

           

           

           

           

       

   

   

       

           

       

   

   

       

           

           

           

           

           

       

       

           

           

           

           

           

       

        ...
   

EmpIdFirst NameLast NameEmailAge

                http://www.jquerycookbook.com">The Absolutely Awesome jQuery CookBook
           
E342BillEvansBill@devcurry.com35
E343LauraMattlaura@devcurry.com26

Observe how we have declared , , and tags in your table. We have also declared Id's and Classes where required, to allow jQuery selectors to have direct access to the DOM elements.

With a dash of CSS, our sample table looks like the following:

table-sample

Tip 1: Insert a New Row as the Last Row of a Table

With the table in place, write the following code to insert a new row as the last row of the table

$(function () {
    newRow = "" +
        "E333" +
        "Fujita" +
        "Makoto" +
        "fujita@devcurry.com" +
        "52" +
    "";
    $('#someTable > tbody > tr:last').after(newRow);
});

When the page is rendered, you should see the newly added row.

new-row

We are using the jQuery selector extension :last to select the last matched row in our table. The after() method inserts the new row after the set of matched elements; in our case, after the last row.

Note: The above example works well for smaller tables, however in a large table, using the :last selector may not give you the best performance. As per the jQuery documentation, :last is a jQuery extension and not part of the CSS specs and hence cannot take advantage of the powerful native DOM methods that can parse any CSS selector, like querySelectorAll.

To achieve better performance, we can rewrite our code as:

$('#someTable > tbody > tr').filter(":last").after(newRow);

The code first selects rows using a pure CSS selector #someTable > tbody > tr and then uses filter(":last") to match the last row of the table.

Note: There are multiple ways in jQuery to achieve a certain requirement. A point to always remember is that jQuery will always use a native method (in our case we discussed querySelectorAll) if available, as it’s much quicker at getting elements and gives a notable performance with complex and large sets of data. jsperf.com is your friend to run tests when you are in doubt of which selectors or methods to use in your code, for the browsers you are supporting.

Tip 2: Insert a New Row in a Table at a Certain Position

Now let’s say you want to insert a new row as the 2nd row in a table. Use the following code:

var index = 2;
newRow = "" +
                "E333" +
                "Fujita" +
                "Makoto" +
                "fujita@devcurry.com" +
                "52" +
            "";

$('#someTable > tbody > tr').eq(index-1).before(newRow);

Refresh the page and you will see the new row gets added as the 2nd row.

s3-add-row-index

Since indexes are zero based and we are passing index=2, .eq(index) would mean .eq(2) i.e. the 3rd row. So to add this to the 2nd row of a table, you first need to do index-1 and then you need to go back to the 2nd row of the table and insert .before() that, so that this new row now becomes the 2nd row of the table.

Alternatively to insert a row as the 2nd row in a table, you can also do

$('#someTable > tbody > tr:first').after(newRow);

which uses the :first selector to match the first row and insert a row after() it.

Here again just like we saw earlier for the last selector, for large tables, you will get performance benefits by using the filter :first.

Similarly you can also explore other child filter selectors like first-child, nth-child and so on from the jQuery documentation at api.jquery.com/category/selectors/child-filter-selectors/

To become a better developer, I cannot emphasize the fact enough that you should take out some time and go through the jQuery documentation. It’s probably one of the most well written documentation of any JavaScript library out there and getting familiar with difference selectors and API’s, will save you tons of time and frustration in a project.

Tip 3: Remove all Rows Except Header

If your table is well defined and contains a , this piece of code will work to remove all the rows, except the header row

$('#someTable tbody tr').remove();

This code uses the remove() method to remove a set of rows inside tbody. Since we haven’t supplied the remove() method with any selector as a parameter, the above code removes all rows, as well as all elements, events and data in the rows.

If you want to remove all rows without removing data and events, use detach() instead of remove()

In case you do not have a defined and want to remove all rows except the first one (assuming first row is meant to be a header), use this code

$('#someTable tr:gt(0)').remove();

where the selector gt(0) selects all rows at an index greater than zero, within the matched rows. Similarly if you want to keep the first two rows and remove all others, change the above code to this

$('#someTable tr:gt(1)').remove();

Note: The jQuery documentation says “Because :gt() is a jQuery extension and not part of the CSS specification, queries using :gt() cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. For better performance in modern browsers, use $("your-pure-css-selector").slice(index) instead”

As seen and discussed earlier, in our case, a pure css selector would be $('#someTable tr'). All you need to do is use it with slice() to remove all except the first row.

$('#someTable tr').slice(1).remove();

Slice() is zero based and takes two arguments, start and end. Since we are supplying 1 as the first parameter to slice(), the above statement will return a new jQuery object that selects from the first row, to the end. Calling remove() removes this set of matched element returned by slice and you are left with only the first row.

Tip 4: Dynamically Add Thousands of New rows to a Table with Performance

jQuery gives us great power when it comes to manipulating the DOM, and with Great power, comes Great responsibility! Think about some of these functions that you can perform very easily using jQuery

  • hide/delete/insert/update elements
  • resize elements/change dimensions
  • move/animate elements

and so on..

All these cause what is known as a reflow operation in the browser. Google Developers documentation defines reflow as “Reflow is the name of the web browser process for re-calculating the positions and geometries of elements in the document, for the purpose of re-rendering part or all of the document”.

Reflows can be very expensive if not done correctly.

Note: If you plan on becoming a serious front end engineer, I would advise you to spend some time reading about reflow and repaint operations.

To understand this better, let’s take an example where we have to dynamically insert 1000’s of rows in a table. We will make use of $.append() to add 5000 rows using two approaches. In the first approach, we will append new rows to the table, every time the loop iterates. In the second approach, we will construct a string with the new rows and then append the string only once after the loop is completed. We will then compare the two approaches and derive our conclusion as to which one of them is better and why.

To iterate the loop, I will be using the for loop rather than $.each.

Although a comparison of for vs $.each and performance vs readability is beyond the scope of this article, in my jQuery Book, I have included a chapter on using jsperf and demonstrated how for a large table, using our old for loop outperforms $.each.

To keep it simple for this article, let’s proceed with the for loop to perform iterations.

Use this piece code. I have declared some additional variables like t1, t2, t1t2 to measure the time differences while using the two approaches.

$(function () {
    var $tbl = $("#someTable");

    // Approach 1: Using $.append() inside loop
    var t1 = new Date().getTime();
    for(i=0; i < 5000; i++){
        rows = "" + i + "FNameLName";
        $tbl.append(rows);
    }
    var t2 = new Date().getTime();
    var t1t2 = t2-t1;
    $('#result').append("Approach 1: Append Inside Loop took " + t1t2 + " milliseconds" + "
");


    // Approach 2: Using $.append() outside loop
    var newrows;
    var t3 =  new Date().getTime();
    for(i=0; i < 5000; i++){
        newrows += "" + i + "FNameLName";
    }
    $tbl.append(newrows);
    var t4 = new Date().getTime();
    var t3t4 = t4 - t3;
    $('#result').append("Approach 2: Append Once Outside Loop " + t3t4 + " milliseconds" + "
");
});

As discussed, we have two sets of code. Approach 1 calls $.append on each iteration of the loop whereas Approach 2 constructs a string (using +=) with the new rows and calls $.append only once after the loop iteration.

The difference is considerable, especially on IE and Safari.

s3-table-performance

Our example took just 2 columns and some fixed length data. Imagine in a real world scenario, where there are multiple columns, with variable data; the results would be dramatic.

In Approach 1, every time you are adding a new row to the table inside the loop, you are causing a reflow and the entire page geometry gets calculated every time with the new DOM change. In Approach 2, theoretically speaking, the reflow occurs only once, since the rows are constructed and added outside the loop. That’s why it’s very important for a front-end engineer to understand and evaluate the difference between the two approaches. Jsperf.com is your friend and use it whenever you can.

Off topic, in Approach 2, you could squeeze additional performance by not concatenating the string, but rather adding it as individual elements of an array and then use join outside the loop to construct the entire string in one go. If you know the length of the array beforehand, that will help too. Check this link to learn more about the same.

So these were some performance tips while working with HTML Tables. If you liked them, do check out my upcoming jQuery Book which is full of recipes demonstrating how to use jQuery to the best of its ability.

Download the entire source code from our GitHub Repository at bit.ly/dncm9-jqperf

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and a new one recently at The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for nine times in a row now. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that represents premium web sites and digital publications comprising of Professional web, windows, mobile and cloud developers, technical managers, and architects.

Get in touch with him on Twitter @suprotimagarwal, LinkedIn or befriend him on Facebook



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by srinivas dandamudi on Wednesday, December 18, 2013 2:42 AM
Hi Suprotim,  Actually we are using 1st approach in our project. we are getting performance issues. This is very helpful to us. Thanks for the article.
Comment posted by srinivas dandamudi on Wednesday, December 18, 2013 4:28 AM
Hi Suprotim,  Actually we are using 1st approach in our project. we are getting performance issues. This is very helpful to us. Thanks for the article.
Comment posted by grgfrgr on Tuesday, April 1, 2014 6:29 AM
ghfdgfdgfdgfdg
Comment posted by Felix on Friday, May 30, 2014 12:05 AM
Such a nice article. Thanks
Comment posted by Olu on Friday, November 21, 2014 6:42 AM
Very helpful article, thanks for this.
Comment posted by Everardo T Cunha on Tuesday, March 10, 2015 7:04 AM
Great article. I just bought your  second book; well written and very practical.
How do i allow the user to edit table rows? Do you have any examples?
Thanks,
Everardo
Comment posted by Suprotim Agarwal on Thursday, March 12, 2015 5:52 AM
@Everardo Thank you. I appreciate the feedback and am glad you are enjoying reading the book.

For CRUD operations on Tables, get some ideas over here http://www.dotnetcurry.com/showarticle.aspx?ID=1006