views:

74

answers:

6

Hi,

I have html table with about 30 columns and somewhere between 10 to 500 ish rows. I would like to show/hide a set of columns o a button click.

I have tried 2 approaches

  1. iterate through table's thead th and do .show() or .hide() on the TH and TD.
  2. iterate through table's thead th and change class to show/hide the TH and TD.

the function is implemented as following snippet. However, the performance is not that great. Show/Hide say 20 columns takes about 5~10 seconds on maybe 80~120 rows of data.

i am just wondering if there is anything we can do to make it go faster.

thanks a lot

function ToggleHeadVisibility(showHide) {

        var index = 0;

        $('#' + gridViewName + ' thead th').each(function(index) {
            index++;
            if (showHide == "SHOW") {
                /*
                $('#' + gridViewName + ' th:nth-child(' + (index) + ')').show();
                $('#' + gridViewName + ' td:nth-child(' + (index) + ')').show();
                */
                $('#' + gridViewName + ' th:nth-child(' + (index) + ')').removeClass('columnHide');
                $('#' + gridViewName + ' td:nth-child(' + (index) + ')').removeClass('columnHide');
            }
            else if (showHide = "HIDE") {
                /*
                //if (showColumnArray.has($(this).get(0).innerHTML)) {
                if (showColumnArray.has($(this).attr('title'))) {
                $('#' + gridViewName + ' th:nth-child(' + (index) + ')').show();
                $('#' + gridViewName + ' td:nth-child(' + (index) + ')').show();
                }
                else {
                $('#' + gridViewName + ' th:nth-child(' + (index) + ')').hide();
                $('#' + gridViewName + ' td:nth-child(' + (index) + ')').hide();
                }
                */
                if (showColumnArray.has($(this).attr('title'))) {
                    $('#' + gridViewName + ' th:nth-child(' + (index) + ')').removeClass('columnHide');
                    $('#' + gridViewName + ' td:nth-child(' + (index) + ')').removeClass('columnHide');
                }
                else {
                    $('#' + gridViewName + ' th:nth-child(' + (index) + ')').addClass('columnHide');
                    $('#' + gridViewName + ' td:nth-child(' + (index) + ')').addClass('columnHide');
                }

            }
        });
    }
A: 

Evidently, this alternative is a little snappier for showing and hiding elements:

.css({'display':'none'}) & .css({'display':'block'});

http://www.learningjquery.com/2010/05/now-you-see-me-showhide-performance

But I suspect your real problem is that loop.

AndrewDotHay
it perform ok with 20 ~40 ish rows. I am thinking the looping of the 30ish TH is not really an big issue. It's probably more on the "th:nth-child" selector. There are just too many of them.Maybe there is another approach?
Eatdoku
A: 

Hi there, here is the peace of code i used to hide "nth" column in my grid...

  if (true) {
      $('th:nth-child(' + c + ')').show();
      $('td:nth-child(' + c + ')').show();                           
  }
  else {
      $('th:nth-child(' + c + ')').hide();
      $('td:nth-child(' + c + ')').hide();                          
  }

Very similar to yours, except i used jQuery toggle "show/hide";

it seems to show/hide column of 400 rows under 1second...

Borik

Borik
I tried it with the jQuery show() and hide() instead of add/remove class as you can see in the code snippet (the commented out code).the performance is about the same.
Eatdoku
A: 

You could do a lot in the area of caching. For starters, cache your gridView container:

var gridView = $('#' + gridViewName);

And subsequently a row can be cached:

var row[0] = gridView.find('tr:nth-child(0)'); // Not sure the path is right, but you get the idea...

Also, perform the actual hide using the set, rather than a .each()

row[0].addClass('columnHide'); // Calls addClass() on each element in the set

Caching sets of elements up front rather than repeatedly querying the DOM with $, and performing actions on sets of elements rather than loops can go a long way in performance.

jshalvi
+1  A: 

Some suggestions:

  1. While building a table, add css classes like col1, col2, col3 etc. to header and data cells. Then you could just do $("td.col1").hide(); to hide the respective column. It is faster than the n-th child selector.

  2. In IE and Firefox, you could set a visibility: collapse to a col element to collapse the whole column. This will be much faster. Unfortunately not supported in Webkit browsers http://www.quirksmode.org/css/columns.html. You could branch your code based on browser so that it is fast at least in IE and Firefox.

  3. If your table has a table-layout: fixed, it may significantly improve performance because your browser doesn't have to keep calculating the widths of columns every time you touch the table as in the auto mode.

  4. Consider removing the table from DOM tree (via .remove()), do the bulk show/hide operation and insert it back in. This is a general rule whenever you want to do bulk operation on DOM tree.

Chetan Sastry
A: 

Iteration through the rows and columns is always going to slow things down. Try manipulating CSS rules directly to avoid the iteration in your JavaScript and force the browser to do it for you.

Checkout the plugins jQueryRule and jQueryCSSRule.

Manipulating CSS rules directly can be beneficial if you combine all rules. Here's a quick test with 500 rows, and 50 columns. The majority of the time is spent re-rendering, and time spent within the JavaScript function gives me on average 200-300 ms on Chrome, and 0 ms on Firefox. Currently it's using standard APIs, but it's trivial to extend this to IE.

It works by creating a new <style> node within the document, and adding all column manipulations there. The key idea is to combine all rules into one when hiding certain columns. So instead of doing:

table tr :nth-child(1) { display: none; }
table tr :nth-child(4) { display: none; }
table tr :nth-child(7) { display: none; }

it does:

table tr :nth-child(1), table tr :nth-child(4), table tr :nth-child(7) {
    display: none;
}

When all columns must be displayed back, delete this one rule above that is hiding particular columns.

Anurag
A: 

Might I suggest something like this?

$(function() {
    $('#show').click(function() {
        var i;
        for (i = 0; i < titles.length; i++)
        {
            ToggleHeadVisibility('SHOW', titles[i]);
        }
    });

    $('#hide').click(function() {
        var i;
        for (i = 0; i < titles.length; i++)
        {
            ToggleHeadVisibility('HIDE', titles[i]);
        }
    });
});

var titles = ['one', 'three', 'five'];

function ToggleHeadVisibility(showHide, title)
{
    var x = $('th[title=' + title + ']').index();
    var selectString = 'th:nth-child(' + (x + 1) + '), td:nth-child(' + (x + 1) + ')';
    var $set = $(selectString);

    if (showHide === "SHOW")
    {
        $set.show();
    }
    else if (showHide === "HIDE")
    {
        $set.hide();
    }
}

I think it is, in fact, your loop that's the trouble. You're iterating over every th in the table. If you only want to find specific ones, why not just iterate over the ones you want to find?

So, what happens here is exactly that. On clicking the "show" (or "hide") button, we iterate over the titles array, calling ToggleHeadVisibility.

In that function, we get the index of the first element with the given title, and then show or hide the nth-child(x) nodes.

I've run it on a table with 6 columns, showing and hiding 3 at a time, and over 1000 rows. It's pretty quick, for what it's doing.

Do note that if your *title*s aren't unique, it will only find the first one in the table.

Ryan Kinal