4

I have a table with 20 columns and about a thousand rows. I want to show/hide different columns based on filters, i.e. each filter displays columns that are relevant to that filter and hides columns that aren't.

I have tried two approaches:

1) Add a "hide" class to the THs and TDs based on the index of the column using jQuery. This is incredibly slow as the class has to be added to each table cell to be hidden.

2) Add a "hide" class to the COLs within a COLGROUP at the top of the table. The problem here is that when style rules like "display: none" or "visibility: collapse" are added to COLs, not all browsers apply those rules to the corresponding columns within the table because the cells are not children of COLs.

Any suggestions?

dylanmac
  • 353
  • 1
  • 7
  • 19

5 Answers5

3

In my opinion easiest way to do this is to creating style element on fly with following CSS rule inside in jQuery:

td:nth-child(n)
{
  display:none;
}

Here is DEMO.

Here is sample code you can improve it i just created very simple example here:

var myStyle = 'td:nth-child(column_index){display:none;}', 
    $myStyleElement = $(document.createElement('style'));

$myStyleElement.attr('id', 'dynamic_style');
$('head').append($myStyleElement);

$('#hide_column').click(function(e){
    $('style#dynamic_style').html(myStyle.replace('column_index', '3'));
});

$('#show_column').click(function(e){
    $('style#dynamic_style').html('');
});

Please, notice by creating style element you are forcing rendering page by browser it self which works by more optimal way I guess than any javascript implementation. I mean there is no iteration in script over elements. However I did not know what is going on inside of browser.

Khamidulla
  • 2,927
  • 6
  • 35
  • 59
  • That's a brilliant approach. OP says the table has about 1000 rows in the question (it is spelled out in text -- probably why you missed it). – Katie Kilian Jan 08 '14 at 00:27
  • @CharlieKilian i created test case [here](http://jsfiddle.net/Antindexer/7n2wg/3/) with 1320 elements works pretty fast in linux chrome environment. – Khamidulla Jan 08 '14 at 01:11
  • 1
    This is efficient. If you combine this with my solution you can do away with the odd dynamic CSS generation. I'm not a big fan of the `nth-child` notation (it lacks expressiveness) but it's a reasonable trade-off to make. – Halcyon Jan 08 '14 at 02:59
  • @FritsvanCampen You approach the best if all filters can be predefined. So look [here](http://stackoverflow.com/questions/20985465/javascript-advantages-and-disadvantages-of-dynamically-on-fly-creating-style) for other case. – Khamidulla Jan 08 '14 at 05:31
3

To hide whole columns you can use a stacking definition:

// HTML
<table id="my-table" class="no-filter">
  <tr>
    <td class="column column1">c1</td>
    <td class="column column2">c2</td>
    <td class="column column3">c3</td>
  </tr>
  // etc x1000
</table>

// CSS
table td.column { display: none; } /* by default all columns are hidden */
table.no-filter td.column { display: block; } /* no-filter shows _all_ columns */
table.filter1 td.column1 { display: block; }
table.filter2 td.column2 { display: block; }
table.filter3 td.column3 { display: block; }

If you want to show just column1:

$("#my-table").removeClass("no-filter").addClass("filter1");

If you want to show column1 and column3:

$("#my-table").removeClass("no-filter").addClass("filter1").addClass("filter3");

If you need one-to-many filters

table.filter4 td.column4,
table.filter4 td.column5,
table.filter4 td.column99 { display: block; }
/* filter 4 shows column 4 5 and 99 */

Filters can overlap:

table.filter5 td.column5 { display: block; }
table.filter6 td.column5,
table.filter6 td.column6 { display: block; }

This assumes your filters are pre-defined and you know the filter-to-column mapping.

Minor note: I haven't tested this, there might be precedence issues. If the filters aren't applying properly change td.columnX to td.column.columnX

Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • +1 For answer good approach for this case. However if you look back my question about dynamically adding css rule. You can see some different case where I use css rules. – Khamidulla Jan 08 '14 at 05:25
  • I have made a small change about this(change the filter to hide column) and test this,it's very fast and certainly is the best answer so far. – Jarvan Jan 08 '14 at 06:06
  • Yup this is the way to go. Not to take anything away from Frits but I hit on a similar idea after I posted. Just got through implementing and it works great. Thanks to all. – dylanmac Jan 08 '14 at 18:53
  • Very nice solution. Anyway me i had some problems with the `display:block;` part of the code, probably because i have several rowspan and colspan, i've just leave it blank when to show and `display:none` when hidden and it works better – Tiziano Mischi Jun 21 '16 at 14:37
1

You could split each column into its own table. Then, you can set display: none on the entire table that represents the column, and that 'column' will be hidden. I'm guessing that would perform better, though it feels rather hackish.

Katie Kilian
  • 6,815
  • 5
  • 41
  • 64
  • 3
    This is admittedly pretty kludgy. If it were my code, I wouldn't like it either. I'm interested to see if there are any better answers. – Katie Kilian Jan 08 '14 at 00:16
  • 3
    Downvotes for an answer that works, that I fully admitted was kludgy? Seems harsh. – Katie Kilian Jan 08 '14 at 00:23
  • Up-voted since it *is* a solution that can be used if desperate. :-) – cosjav Jan 08 '14 at 00:24
  • You know, I kind of like this answer. This places some constraints on your table, ie. you have to fix the dimension of every cell. But if you're doing that then why not go the full mile and do away with tables all together? `div`s are superior :P – Halcyon Jan 08 '14 at 02:54
0

What about giving all cells from a particular column a class: class="column1" on all cells from column 1, etc, for all columns. Then: $('.column1').hide();

Fabien Warniez
  • 2,731
  • 1
  • 21
  • 30
  • This is going to suffer the same peformance issues that any jQuery does. The real problem is that jQuery is going to have to search for the rows, no matter what selector you use. – Katie Kilian Jan 08 '14 at 00:26
  • What about doing this, but writing CSS rules like .table_hideCol-1 > td:first-child { display: none; } .table_hideCol-2 > td:nth-child(2) { display: none; } .table_hideCol-3 > td:nth-child(3) { display: none; } {...} then, add class .table_hideCol-n with jQuery/Javascript? – Álvaro Martínez Jan 08 '14 at 01:12
0

Simply using jquery to loop all trs and hide the column seems not so slow as you guys think...

Here is i'm testing base on @Phoenix's demo:

jsfiddle

and here is the hide and show code:

$('#hide_column').click(function(e){
    $("tr").each(function(){$(this).children(":eq(2)").hide();})
});

$('#show_column').click(function(e){
    $("tr").each(function(){$(this).children(":eq(2)").show();})
});

This code's performance even faster then @Phoenix's answer though i think he's answer looks better,may be we are thinking too much...

Jarvan
  • 1,135
  • 8
  • 22
  • 1
    Maybe Phoenix' solution is slower because of the dynamic CSS generation? The CSS can be added statically if you predefine the filter-to-column mapping (which is a reasonable assumption). My instinct tells me your solution should be slower. Both perform extremely well here by the way, I see no immediate need to optimize. – Halcyon Jan 08 '14 at 03:03
  • @jarvaJiang show me the proof how much better? Do you have comparison? If it is fast enough I will up vote for sure. – Khamidulla Jan 08 '14 at 05:42
  • @Phoenix see this jsfiddle:http://jsfiddle.net/7n2wg/6/ , it's base on your jsfiddle demo,in my environment(window server2008 x64,chrome) yours is about half second slower than mine.But both are slower than Frits van Campen's,your way is right but maybe the dynamic CSS is not. – Jarvan Jan 08 '14 at 05:58