Search code examples
javascriptjquerydatatablesfilteringvisibility

jQuery Datatables: Combining column visibility with individual column filters (text inputs)?


I am using basic column visibility and individual column searching (text inputs).

The problem is that when the user adds a previously-hidden column to the table, the text field box for that column does not appear. Thus, the user cannot filter that column.

Does anyone know how to enable filters for hidden columns as well? Ideally, this would not cause a byproduct of clearing the text in the other filters (if the user did enter text in the other filters).

Here is my filtering code:

<script type="text/javascript">
$(document).ready(function() {
    // Setup - add a text input to each footer cell
    $('#tableID tfoot th').each( function () {
        var title = $(this).text();

        if ((title != '') && !(title.includes("$"))) {
            // Then the current column is *not* the Action column.          
            $(this).html( '<span style="color: #515151; font-size:15px;"><i>Filter</i></span> <br> <input type="text"  style="margin-top:10px;" placeholder="'+title+'" /> ' );
        }
    } );

    var table = $('#tableID').DataTable();
    // Apply the search
    table.columns().every( function () {
        var that = this;

        $( 'input', this.footer() ).on( 'keyup change', function () {
            if ( that.search() !== this.value ) {
                that
                .search( this.value )
                .draw();
            }
        });
    } );

} );    
</script>  

I am using this line to hide the columns that I want to be hidden from view by default:

(table.column('.hideCol')).visible(false);

Solution

  • There's a custom column-visibility event in DataTables. So, you may revise your <input> elements visibility based on current status of the column.

    E.g. you have <input> rendering function, like that:

    //function to render input elements
    const renderTfootInputs =  () => {
            //grab previous inputs into array
            const prevInputs = [];
            dataTable.columns().every(function(){
                prevInputs.splice(this.index(), 1, $(`#example tfoot [colindex="${this.index()}"]`).val());
            });
            //purge <tfoot> row contents
            $('#example tfoot tr').empty()
            //iterate through table columns
            dataTable.columns().every(function(){
                //if the column is visible
                this.visible() ?
                //append corresponding footer <input>
                $('#example tfoot tr').append(`<th><input colindex="${this.index()}" placeholder="${$(this.header()).text()}" value="${prevInputs[this.index()] || ''}"></input></th>`) :
                true;
            });
    };
    

    Than, you may call it upon column visibility changes:

    $('#example').on('column-visibility.dt', renderTfootInputs);
    

    Complete demo of this approach might look as follows:

    //sample data source
    const dataSrc = [
    	{id: 1, title: 'apple', cat: 'fruit'},
    	{id: 2, title: 'pear', cat: 'fruit'},
    	{id: 3, title: 'banana', cat: 'fruit'},
    	{id: 4, title: 'carrot', cat: 'vegie'},
    	{id: 5, title: 'eggplant', cat: 'vegie'}
    ];
    //datatables initialization
    const dataTable = $('#example').DataTable({
    	data: dataSrc,
    	dom: 'Bfrtip',
    	buttons: ['colvis'],
    	columns: ['id','title','cat'].map(header => ({title: header, data: header})),
    	columnDefs: [
    		{targets: 0, visible: false}
    	]
    });
    //append blank footer to the table
    $('#example').append('<tfoot><tr></tr></tfoot>');
    //function to render input elements
    const renderTfootInputs =  () => {
    	//grab previous inputs into array
    	const prevInputs = [];
    	dataTable.columns().every(function(){
    		prevInputs.splice(this.index(), 1, $(`#example tfoot [colindex="${this.index()}"]`).val());
    	});
    	//purge <tfoot> row contents
    	$('#example tfoot tr').empty()
    	//iterate through table columns
    	dataTable.columns().every(function(){
    		//if the column is visible
    		this.visible() ?
    		//append corresponding footer <input>
    		$('#example tfoot tr').append(`<th><input colindex="${this.index()}" placeholder="${$(this.header()).text()}" value="${prevInputs[this.index()] || ''}"></input></th>`) :
    		true;
    	});
    };
    //initial call of above function
    renderTfootInputs();
    //call that function each time upon column visibility changes
    $('#example').on('column-visibility.dt', renderTfootInputs);
    //individual search
    $('#example').on('keyup', 'tfoot input', function(event){
    	dataTable.column($(event.target).attr('colindex')).search($(event.target).val()).draw();
    });
    tfoot input {
      display: block;
    }
    
    tfoot th {
      padding-left: 10px !important;
    }
    <!doctype html>
    <html>
    <head>
      <script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
      <script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
      <script type="application/javascript" src="https://cdn.datatables.net/buttons/1.5.6/js/dataTables.buttons.min.js"></script>
      <script type="application/javascript" src="https://cdn.datatables.net/buttons/1.5.6/js/buttons.colVis.min.js"></script>
      <script type="application/javascript" src="test.js"></script>
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.5.6/css/buttons.dataTables.min.css">
    </head>
    	<body>
    		<table id="example"></table>
    	</body>
    </html>