163

I have an HTML table with several columns and I need to implement a column chooser using jQuery. When a user clicks on a checkbox I want to hide/show the corresponding column in the table. I would like to do this without attaching a class to every td in the table, is there a way to select an entire column using jQuery? Below is an example of the HTML.

<table>
    <thead>
        <tr><th class="col1">Header 1</th><th class="col2">Header 2</th><th class="col3">Header 3</th></tr>
    </thead>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
</table>

<form>
    <input type="checkbox" name="col1" checked="checked" /> Hide/Show Column 1 <br />
    <input type="checkbox" name="col2" checked="checked" /> Hide/Show Column 2 <br />
    <input type="checkbox" name="col3" checked="checked" /> Hide/Show Column 3 <br />
</form>
Majid Basirati
  • 2,665
  • 3
  • 24
  • 46
Brian Fisher
  • 23,519
  • 15
  • 78
  • 82
  • 2
    I hope the folowing site would help: http://www.fiendish.demon.co.uk/html/javascript/hidetablecols.html –  May 18 '10 at 13:22
  • I implemented this solution using jQuery, and it worked perfectly for me: [http://www.devcurry.com/2009/07/hide-table-column-with-single-line-of.html](http://www.devcurry.com/2009/07/hide-table-column-with-single-line-of.html) – Aaron Jul 06 '10 at 17:23
  • 1
    Per user344059's comment, here's the web archive for the broken link [http://www.fiendish.demon.co.uk/html/javascript/hidetablecols.html](https://web.archive.org/web/20150725122312/http://www.fiendish.demon.co.uk/html/javascript/hidetablecols.html) – KyleMit May 11 '18 at 00:40

9 Answers9

272

One line of code using jQuery which hides the 2nd column:

$('td:nth-child(2)').hide();

If your table has header(th), use this:

$('td:nth-child(2),th:nth-child(2)').hide();

Source: Hide a Table Column with a Single line of jQuery code

jsFiddle to test the code: http://jsfiddle.net/mgMem/1/


If you want to see a good use case, take a look at my blog post:

Hide a table column and colorize rows based on value with jQuery.

Matthew Lock
  • 13,144
  • 12
  • 92
  • 130
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
  • 1
    As an aside as they're not in the example, is there not an issue that this ignores colspans? Good if you're not using them though. There's another question related: http://stackoverflow.com/questions/1166452/finding-column-index-using-jquery-when-table-contains-column-spanning-cells/1166639#comment9684733_1166639 – Kim R Nov 02 '11 at 10:45
  • 2
    I had to add the tbody to the selector to avoid hiding the footer with the pager: $('.webgrid tbody td:nth-child(1), .webgrid th:nth-child(1)').hide(); – Alex Jan 12 '12 at 22:54
  • @KimR This may help for colspan issues http://stackoverflow.com/questions/9623601/how-to-use-class-attribute-in-html-col/9623761#9623761 – yunzen Sep 26 '12 at 10:13
  • I don't know exactly why, but I had to use 'nth-of-type' instead, to make it work. For example: $('table td:nth-of-type(2)').hide(); – Leopoldo Sanczyk Oct 06 '14 at 02:21
  • @LeopoldoSanczyk were you using Safari? Seems like it needs nth-of-type – ykay says Reinstate Monica Jun 14 '15 at 14:23
  • @ykay I forgotten to mention I was using Firefox. Judging by the date, 32.0.X or a close version. – Leopoldo Sanczyk Jun 16 '15 at 18:56
  • @LeopoldoSanczyk Ok, I asked because I just ran into that when a client asked for his site to work on safari. Perhaps the same for firefox as well – ykay says Reinstate Monica Jun 17 '15 at 19:01
  • Careful: when getting the index with `$('.mycol').closest('th').index()` you have to add 1 because `nth-child` starts at 1!!! so `colIdx = $('.mycol').closest('th').index() + 1` – olidem Oct 17 '18 at 09:49
  • does this affect every table in the page? – golimar Oct 30 '20 at 15:21
  • 1
    @golimar yes... to restrict that, you need to add a CSS class to the table you want to target. Something as: `.my-table-class td:nth-child(2)` – Leniel Maccaferri Oct 30 '20 at 16:24
88

I would like to do this without attaching a class to every td

Personally, I would go with the the class-on-each-td/th/col approach. Then you can switch columns on and off using a single write to className on the container, assuming style rules like:

table.hide1 .col1 { display: none; }
table.hide2 .col2 { display: none; }
...

This is going to be faster than any JS loop approach; for really long tables it can make a significant difference to responsiveness.

If you can get away with not supporting IE6, you could use adjacency selectors to avoid having to add the class attributes to tds. Or alternatively, if your concern is making the markup cleaner, you could add them from JavaScript automatically in an initialisation step.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 7
    Thanks for the advice, I had wanted to keep the HTML cleaner, but performance definitely became an issue as the table size approached 100 rows. This solution, provided a 2-5x performance improvement. – Brian Fisher Jan 23 '09 at 07:04
  • 2
    This approach worked wonders for me, performance-wise. Thanks! – axelarge Apr 01 '12 at 20:28
  • 4
    I added this to my css `.hidden {display:none;}` and used `.addClass('hidden')` and `.removeClass('hidden')` which was a little faster than `.hide()` and `.show()`. – styfle Dec 28 '12 at 02:04
19

Here's a little more fully featured answer that provides some user interaction on a per column basis. If this is going to be a dynamic experience, there needs to be a clickable toggle on each column that indicates the ability to hide the column, and then a way to restore previously hidden columns.

That would look something like this in JavaScript:

$('.hide-column').click(function(e){
  var $btn = $(this);
  var $cell = $btn.closest('th,td')
  var $table = $btn.closest('table')

  // get cell location - https://stackoverflow.com/a/4999018/1366033
  var cellIndex = $cell[0].cellIndex + 1;

  $table.find(".show-column-footer").show()
  $table.find("tbody tr, thead tr")
        .children(":nth-child("+cellIndex+")")
        .hide()
})

$(".show-column-footer").click(function(e) {
    var $table = $(this).closest('table')
    $table.find(".show-column-footer").hide()
    $table.find("th, td").show()

})

To support this, we'll add some markup to the table. In each column header, we can add something like this to provide a visual indicator of something clickable

<button class="pull-right btn btn-default btn-condensed hide-column" 
            data-toggle="tooltip" data-placement="bottom" title="Hide Column">
    <i class="fa fa-eye-slash"></i>  
</button>

We'll allow the user to restore columns via a link in the table footer. If it's not persistent by default, then toggling it on dynamically in the header could jostle around the table, but you can really put it anywhere you'd like:

<tfoot class="show-column-footer">
   <tr>
    <th colspan="4"><a class="show-column" href="#">Some columns hidden - click to show all</a></th>
  </tr>
</tfoot>

That's the basic functionality. Here's a demo below with a couple more things fleshed out. You can also add a tooltip to the button to help clarify its purpose, style the button a little more organically to a table header, and collapse the column width in order to add some (somewhat wonky) css animations to make the transition a little less jumpy.

Demo Screengrab

Working Demo in jsFiddle & Stack Snippets:

$(function() {
  // on init
  $(".table-hideable .hide-col").each(HideColumnIndex);

  // on click
  $('.hide-column').click(HideColumnIndex)

  function HideColumnIndex() {
    var $el = $(this);
    var $cell = $el.closest('th,td')
    var $table = $cell.closest('table')

    // get cell location - https://stackoverflow.com/a/4999018/1366033
    var colIndex = $cell[0].cellIndex + 1;

    // find and hide col index
    $table.find("tbody tr, thead tr")
      .children(":nth-child(" + colIndex + ")")
      .addClass('hide-col');
      
    // show restore footer
    $table.find(".footer-restore-columns").show()
  }

  // restore columns footer
  $(".restore-columns").click(function(e) {
    var $table = $(this).closest('table')
    $table.find(".footer-restore-columns").hide()
    $table.find("th, td")
      .removeClass('hide-col');

  })

  $('[data-toggle="tooltip"]').tooltip({
    trigger: 'hover'
  })

})
body {
  padding: 15px;
}

.table-hideable td,
.table-hideable th {
  width: auto;
  transition: width .5s, margin .5s;
}

.btn-condensed.btn-condensed {
  padding: 0 5px;
  box-shadow: none;
}


/* use class to have a little animation */
.hide-col {
  width: 0px !important;
  height: 0px !important;
  display: block !important;
  overflow: hidden !important;
  margin: 0 !important;
  padding: 0 !important;
  border: none !important;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/paper/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>




<table class="table table-condensed table-hover table-bordered table-striped table-hideable">

  <thead>
    <tr>
      <th>
        Controller
        <button class="pull-right btn btn-default btn-condensed hide-column" data-toggle="tooltip" data-placement="bottom" title="Hide Column">
          <i class="fa fa-eye-slash"></i>  
        </button>
      </th>
      <th class="hide-col">
        Action
        <button class="pull-right btn btn-default btn-condensed hide-column" data-toggle="tooltip" data-placement="bottom" title="Hide Column">
          <i class="fa fa-eye-slash"></i>  
        </button>
      </th>
      <th>
        Type
        <button class="pull-right btn btn-default btn-condensed hide-column" data-toggle="tooltip" data-placement="bottom" title="Hide Column">
          <i class="fa fa-eye-slash"></i>  
        </button>
      </th>
      <th>
        Attributes
        <button class="pull-right btn btn-default btn-condensed hide-column" data-toggle="tooltip" data-placement="bottom" title="Hide Column">
          <i class="fa fa-eye-slash"></i>  
        </button>
      </th>
  </thead>
  <tbody>

    <tr>
      <td>Home</td>
      <td>Index</td>
      <td>ActionResult</td>
      <td>Authorize</td>
    </tr>

    <tr>
      <td>Client</td>
      <td>Index</td>
      <td>ActionResult</td>
      <td>Authorize</td>
    </tr>

    <tr>
      <td>Client</td>
      <td>Edit</td>
      <td>ActionResult</td>
      <td>Authorize</td>
    </tr>

  </tbody>
  <tfoot class="footer-restore-columns">
    <tr>
      <th colspan="4"><a class="restore-columns" href="#">Some columns hidden - click to show all</a></th>
    </tr>
  </tfoot>
</table>
KyleMit
  • 30,350
  • 66
  • 462
  • 664
  • you are hiding nearest 1 column,how to hide nearest 3 column ? – Nirmal Goswami Jun 15 '18 at 04:38
  • check my table - https://i.stack.imgur.com/AA8iZ.png and question which contain table html - https://stackoverflow.com/questions/50838119/how-to-loop-multiple-table-th-in-angularjs button will come after A,B and C – Nirmal Goswami Jun 15 '18 at 04:51
  • sorry to revive such an old answer, but is there an easy way of setting certain columns to hidden by default? I'm trying to do so with `$(document).ready` but having no luck – RobotJohnny Nov 22 '18 at 12:24
  • 1
    @RobotJohnny, good question. This is using the class `.hide-col` to remove columns, but it can also be used to indicate state as well, so you could either - add `.hide-col` to each `td` & `tr` when rendering the html and be done. Or if you wanted to add it in fewer places, put it in the header (that state will have to go somewhere), and on init, use that to hide that column index across children. Currently, the code is just listening for the the position on click, but it could be modified to look for class position as well. Also, happy turkey day – KyleMit Nov 22 '18 at 13:13
  • 1
    @RobotJohnny, I updated the code sample to include initialization handling as well. Just drop `class='hide-col'` anywhere you want in your html (probably in in the `thead > tr > th` makes the most sense and it will pick up to make sure it hides all cells in that column and dynamically show the restore footer as well – KyleMit Nov 23 '18 at 14:46
  • How can I keep column width fixed when any column is hidden? – sjain Apr 05 '19 at 07:07
  • @sjain, the table in the demo has the width set to `100%` so when columns are removed, the others will occupy that space. One way is to not have tables fill 100% width, and then each of the columns will size independently and retain their width no matter which are visible. Here's an [example of that approach in jsFiddle](https://jsfiddle.net/KyleMit/rr96p4vv/386/) – KyleMit Apr 05 '19 at 15:41
12

you could use colgroups:

<table>
    <colgroup>
       <col class="visible_class"/>
       <col class="visible_class"/>
       <col class="invisible_class"/>  
    </colgroup>
    <thead>
        <tr><th class="col1">Header 1</th><th class="col2">Header 2</th><th class="col3">Header 3</th></tr>
    </thead>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
    <tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
</table>

your script then could change just the desire <col> class.

Luis Melgratti
  • 11,881
  • 3
  • 30
  • 32
  • I seem to remember colgroup not having cross browser support is that no longer true? – Brian Fisher Jan 18 '09 at 22:03
  • Maybe. i'm using this method for hilight columns, (firefox, safari, chrome works fine) never tried it in IE. – Luis Melgratti Jan 18 '09 at 22:12
  • @Brian - IE8 does not work and IE8 with IE7 enabled seems to be working. – Nordes Aug 23 '10 at 07:43
  • 4
    This seems not to work anymore in modern browser (Chrome and Firefox) – JBE Feb 28 '13 at 14:57
  • 1
    @JBE: to be precise, this *does* work in modern browsers *to some extent*. Using the `$('table > colgroup > col.yourClassHere')` jQuery selector, you still can set smth like the background color of the whole column, but you're no longer able to toggle column visibility. Browsers tested were MSIE **11**, Safari **5**, Chromium **44**, Opera **12**, Mozilla SeaMonkey **2.40**, Mozilla Firefox **43**. "Most of the attributes in HTML 4.01 are not supported in HTML5" -- see [here](http://www.w3schools.com/tags/tag_colgroup.asp). – Bass Apr 28 '16 at 16:41
11

The following should do it:

$("input[type='checkbox']").click(function() {
    var index = $(this).attr('name').substr(2);
    $('table tr').each(function() { 
        $('td:eq(' + index + ')',this).toggle();
    });
});

This is untested code, but the principle is that you choose the table cell in each row that corresponds to the chosen index extracted from the checkbox name. You could of course limit the selectors with a class or an ID.

Eran Galperin
  • 86,251
  • 24
  • 115
  • 132
5

And of course, the CSS only way for browsers that support nth-child:

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

This is for IE9 and newer.

For your usecase, you'd need several classes to hide the columns:

.hideCol1 td:nth-child(1) { display: none;}
.hideCol2 td:nth-child(2) { display: none;}

ect...

ProblemsOfSumit
  • 19,543
  • 9
  • 50
  • 61
2

The following is building on Eran's code, with a few minor changes. Tested it and it seems to work fine on Firefox 3, IE7.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
                "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<script>
$(document).ready(function() {
    $('input[type="checkbox"]').click(function() {
        var index = $(this).attr('name').substr(3);
        index--;
        $('table tr').each(function() { 
            $('td:eq(' + index + ')',this).toggle();
        });
        $('th.' + $(this).attr('name')).toggle();
    });
});
</script>
<body>
<table>
<thead>
    <tr>
        <th class="col1">Header 1</th>
        <th class="col2">Header 2</th>
        <th class="col3">Header 3</th>
    </tr>
</thead>
<tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
<tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
<tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
<tr><td>Column1</td><td>Column2</td><td>Column3</td></tr>
</table>

<form>
    <input type="checkbox" name="col1" checked="checked" /> Hide/Show Column 1 <br />
    <input type="checkbox" name="col2" checked="checked" /> Hide/Show Column 2 <br />
    <input type="checkbox" name="col3" checked="checked" /> Hide/Show Column 3 <br />
</form>
</body>
</html>
Paolo Bergantino
  • 480,997
  • 81
  • 517
  • 436
1
<p><input type="checkbox" name="ch1" checked="checked" /> First Name</p>
.... 
<td class="ch1">...</td>

 <script>
       $(document).ready(function() {
            $('#demo').multiselect();
        });


        $("input:checkbox:not(:checked)").each(function() {
    var column = "table ." + $(this).attr("name");
    $(column).hide();
});

$("input:checkbox").click(function(){
    var column = "table ." + $(this).attr("name");
    $(column).toggle();
});
 </script>
lahbib
  • 119
  • 1
  • 6
0

Without class? You can use the Tag then:

var tds = document.getElementsByTagName('TD'),i;
for (i in tds) {
  tds[i].style.display = 'none';
}

And to show them use:

...style.display = 'table-cell';            
Gustavo Ruiz
  • 655
  • 5
  • 4