This is a follow-up article to jQuery DataTables – How to add a checkbox column describing a simple solution to add checkboxes to a table. However proposed solution worked for a table using client-side processing mode only. This article offers universal solution that would work both in client-side and server-side processing modes.
It is loosely based on DataTables example – Row selection but adds extra functionality such as ability to use checkboxes for row selection and other minor improvements.
Example
Example below shows a data table using client-side processing mode where data is received from the server using Ajax. However the same code could be used if data table is switched into server-side processing mode with 'serverSide': true
initialization option.
//
// Updates "Select all" control in a data table
//
function updateDataTableSelectAllCtrl(table){
var $table = table.table().node();
var $chkbox_all = $('tbody input[type="checkbox"]', $table);
var $chkbox_checked = $('tbody input[type="checkbox"]:checked', $table);
var chkbox_select_all = $('thead input[name="select_all"]', $table).get(0);
// If none of the checkboxes are checked
if($chkbox_checked.length === 0){
chkbox_select_all.checked = false;
if('indeterminate' in chkbox_select_all){
chkbox_select_all.indeterminate = false;
}
// If all of the checkboxes are checked
} else if ($chkbox_checked.length === $chkbox_all.length){
chkbox_select_all.checked = true;
if('indeterminate' in chkbox_select_all){
chkbox_select_all.indeterminate = false;
}
// If some of the checkboxes are checked
} else {
chkbox_select_all.checked = true;
if('indeterminate' in chkbox_select_all){
chkbox_select_all.indeterminate = true;
}
}
}
$(document).ready(function (){
// Array holding selected row IDs
var rows_selected = [];
var table = $('#example').DataTable({
'ajax': {
'url': '/lab/articles/jquery-datatables-checkboxes/ids-arrays.txt'
},
'columnDefs': [{
'targets': 0,
'searchable': false,
'orderable': false,
'width': '1%',
'className': 'dt-body-center',
'render': function (data, type, full, meta){
return '<input type="checkbox">';
}
}],
'order': [[1, 'asc']],
'rowCallback': function(row, data, dataIndex){
// Get row ID
var rowId = data[0];
// If row ID is in the list of selected row IDs
if($.inArray(rowId, rows_selected) !== -1){
$(row).find('input[type="checkbox"]').prop('checked', true);
$(row).addClass('selected');
}
}
});
// Handle click on checkbox
$('#example tbody').on('click', 'input[type="checkbox"]', function(e){
var $row = $(this).closest('tr');
// Get row data
var data = table.row($row).data();
// Get row ID
var rowId = data[0];
// Determine whether row ID is in the list of selected row IDs
var index = $.inArray(rowId, rows_selected);
// If checkbox is checked and row ID is not in list of selected row IDs
if(this.checked && index === -1){
rows_selected.push(rowId);
// Otherwise, if checkbox is not checked and row ID is in list of selected row IDs
} else if (!this.checked && index !== -1){
rows_selected.splice(index, 1);
}
if(this.checked){
$row.addClass('selected');
} else {
$row.removeClass('selected');
}
// Update state of "Select all" control
updateDataTableSelectAllCtrl(table);
// Prevent click event from propagating to parent
e.stopPropagation();
});
// Handle click on table cells with checkboxes
$('#example').on('click', 'tbody td, thead th:first-child', function(e){
$(this).parent().find('input[type="checkbox"]').trigger('click');
});
// Handle click on "Select all" control
$('thead input[name="select_all"]', table.table().container()).on('click', function(e){
if(this.checked){
$('#example tbody input[type="checkbox"]:not(:checked)').trigger('click');
} else {
$('#example tbody input[type="checkbox"]:checked').trigger('click');
}
// Prevent click event from propagating to parent
e.stopPropagation();
});
// Handle table draw event
table.on('draw', function(){
// Update state of "Select all" control
updateDataTableSelectAllCtrl(table);
});
// Handle form submission event
$('#frm-example').on('submit', function(e){
var form = this;
// Iterate over all selected checkboxes
$.each(rows_selected, function(index, rowId){
// Create a hidden element
$(form).append(
$('<input>')
.attr('type', 'hidden')
.attr('name', 'id[]')
.val(rowId)
);
});
});
});
In addition to the above code, the following Javascript library files are loaded for use in this example:
//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js
table.dataTable.select tbody tr,
table.dataTable thead th:first-child {
cursor: pointer;
}
The following CSS library files are loaded for use in this example to provide the styling of the table:
//cdn.datatables.net/1.10.7/css/jquery.dataTables.min.cssOther examples
Problem
The problem with handling checkboxes varies based on DataTables initialization settings. In server-side processing mode ('serverSide':true
) elements <input type="checkbox">
would exist for current page only. Once page is changed, the checked state of the checkboxes would not be preserved. In client-side processing mode, the checked state of checkbox is preserved, but only current page is accessible in DOM, all other pages has to be accessible through DataTables API.
Solution
The solution is to create a global variable (rows_selected
in our example) to store a list of selected row IDs and use it to display checkbox state and highlight selected rows.
Highlights
Javascript
-
Storing selected row IDs
// Array holding selected row IDs var rows_selected = [];
Define array holding selected row IDs.
-
Columns definition
'columnDefs': [{ 'targets': 0, 'searchable': false, 'orderable': false, 'width': '1%', 'className': 'dt-body-center', 'render': function (data, type, full, meta){ return '<input type="checkbox">'; } }],
Option columnsDef is used to define appearance and behavior of the first column (
'targets': 0
).Searching and ordering of the column is not needed so this functionality is disabled with searchable and orderable options.
To center checkbox in the cell, internal DataTables CSS class
dt-body-center
is used.Option render is used to prepare the checkbox control for being displayed in each cell of the first column.
-
Initial sorting order
'order': [[1, 'asc']],
By default, DataTables sorts table by first column in ascending order. By using order option we select another column to perform initial sort.
-
Row draw callback
'rowCallback': function(row, data, dataIndex){ // Get row ID var rowId = data[0]; // If row ID is in the list of selected row IDs if($.inArray(rowId, rows_selected) !== -1){ $(row).find('input[type="checkbox"]').prop('checked', true); $(row).addClass('selected'); }
Callback function rowCallback will be called before each row draw and is useful to indicate the state of the checkbox and row selection. We use internal DataTables CSS class
selected
.Important
Please note that in the example above row ID is stored as first element of the row data array and is being retrieved by using the following code.
// Get row ID var rowId = data[0];
If you’re using data structure other than described in the article, adjust this and other similar lines accordingly.
-
Form submission
// Handle form submission event $('#frm-example').on('submit', function(e){ var form = this; // Iterate over all selected checkboxes $.each(rows_selected, function(index, rowId){ // Create a hidden element $(form).append( $('<input>') .attr('type', 'hidden') .attr('name', 'id[]') .val(rowId) ); }); });
When table is enhanced by jQuery DataTables plug-in, only visible elements exist in DOM. That is why by default form submits checkboxes from current page only.
To submit selected checkboxes from all pages we need to iterate over
rows_selected
array and add a hidden<input>
element to the form with some name (id[]
in our example) and row ID as a value. This will allow all the data to be submitted.For more information on submitting the form elements in a table powered by jQuery DataTables, please see jQuery DataTables: How to submit all pages form data.
HTML
<table id="example" class="display select" cellspacing="0" width="100%">
Additional CSS class select
is used to change cursor when hovering over table rows for specific tables only.
CSS
table.dataTable.select tbody tr,
table.dataTable thead th:first-child {
cursor: pointer;
}
Display cursor in the form of a hand for table rows and first cell in the table heading where “Select all” control is located.
Thanks for this! But it’s not quite working with server-side processing mode. Do you have a solution / fix for that as well?
Sergiu, example above shows working solution with Ajax-sourced data. Do you have a link to a page to demonstrate an error?
Thanks for your reply! I am afraid I don’t have a quick demo (as it’s in my admin side of the website) but basically I am using this bit:
https://www.datatables.net/examples/server_side/simple.html
and I’ve tried your implementation however when I click on page 2 or other pages all the rows get selected.
I appreciate you’re using Ajax sourced data in your demo but it’s loaded once and then split pages while in server-side mode you load data on every page if you know what I mean.
This is a snipped from my code: http://pastie.org/10322051
Do you have an idea?
See this jsFiddle, you need to use
data.id
instead ofdata[0]
because your data consists of objects and you need to store IDs in the array.Thanks, Michael! It worked with
data['id']
too, but will try withdata.id
to see how it goes. Thanks a lot for your help.Thank you very much for this solution. It worked. But for some reason this is not working when I add ‘Select All’ on the top column, I appreciate if you could help me. Again thank you very much.
I may add code for “Select all” checkbox in a few days.
Thank you Michael for you response… Nice to hear that you will add a code for select all.. Again thank you very much in advance.
James, I added solution for “Select all” checkbox and example on jsFiddle.
Thank you very much for your immediate update. Really appreciate your help. More power & God bless…
Hi Michael,
if you use the
sScrollX
property of the DataTables, the header is duplicated by default and the second header is hidden.In this scenario, the checkbox
select_all
doesn’t work because the click event is attached on the hidden checkbox.So, to make it work all again, in the js add to the selector
input[name="select_all"]
the:first
, like this:input[name="select_all"]:first
This tells that the event handler is applied only on the first occurrence of the checkbox.
Thank you for your suggestion! I will test and update the code if necessary.
I also found this problem,but I don’t have a solution. I add
"sScrollX": "100%"
on the 40th line, then I use select_all don’t work. The test code is http://jsfiddle.net/abhbs4x8/342/. I hope you can help me.Alan, see updated jsFiddle. The solution is to use
$('thead input[name="select_all"]', $('#example').DataTable().table().container())
selector instead.Thank you very much!
Could you provide above functionality by taking some sample text for
tbody
without Ajax callingSee this jsFiddle for the example with HTML sourced data.
I am not getting checkbox for every row. I placed input type checkbox for every row then select all check box is not working. If possible please provide me the code in zip file.
I am trying to use the code mentioned in http://jsfiddle.net/gyrocode/5bmx7ejw/ and am unable to get check boxes for any of the rows! Could you share the code (html) that you have. Moreover, I am looking to implement this in Salesforce, wherein I wouldn’t be needing tag in this instance.
All the code including HTML is available in the jsFiddle.
Hi Michael,
The fiddle (http://jsfiddle.net/gyrocode/5bmx7ejw/) you posted does not work the same as the sample in your article.
In this fiddle when you click the checkbox in the header while on the first page, ALL items in ALL pages are checked, then when you click it again, only the current page is affected.
I got to your article while looking for a working solution for this problem while using AngularJS together with DataTables and angular-datatables – the ones I was able to find do not work as advertised.
Any chance of you creating a version for use with angular-datatables? 🙂
There was an error in my jsFiddle which is now corrected, thank you for pointing that out. I’ll take a look at angular-datatables. Also with jQuery DataTables 1.10.8 release there is now Select extension, that allows row selection with pseudo checkboxes, see Checkbox selection example.
Hi Michael,
How can I achieve the column width as yours. The width of my checkbox column, as well as other column with few words are quite large. Hoping to hear from u. Thanks!
Try using columns.width option to force minimal width for the checkbox column, for example:
How can I download the source?
I tried implemented this for my app but the submit is not showing items checked and the click on individual checkbox not working on opera.
Full source code is available in jsFiddle mentioned in the article. Just tried Opera 32 and it works fine. Can you share a link to your app or create an example on jsFiddle to demonstrate the problem?
Hi Michael, Thanks a lot for sharing your work!
The select all works only for the current page and not for all the pages. Is it meant to do that ?
Yes, that was intentional, because I was trying to come up with universal solution. For client-side processing mode it’s possible to select checkboxes on all pages with some minor modifications of the code in the article. However the code for server-side processing mode would be completely different. Maybe I will update the article to cover that as well.
Hi Michael,
Thanks for your very detailed tutorial, it really helped me setup data tables.
I do have one issue though.
When submitting the form, I want to append some info in addition to the hidden field.
However, it seems the index is not properly referenced in the loop:
The
rowId
shows the proper selected rows but theindex
isn’t.I don’t really get what I’m doing wrong (the rest of the code is what you did)
Thanks!
– Vincent
You’re trying to retrieve data for the whole row with
row().data()
API method and then access second column withdata[1]
. I don’t understand the purpose of your code?If you want to access row data later with
linksTable.row(rowId).data()
, store row index instead of ID, for examplevar rowId = $('#example').DataTable().row(row).index();
.Alternatively, you can use
filter()
API method to locate row by value of one of the columns. For example:Hi Michael,
Really appreciate your super job with DataTable.
I have 2 questions need you help.
1. In ajax option (server side processing case), can we replace the url by calling the custom function which returns set of data as a promise? I am using Angular factory to write a service to get the data by passing paging number and some filter parameters. How can I apply the service call in this case?
2. Can we make the table become responsive? So that in small device I’d like to hide couple of columns, and showing the details info when clicking the plus(+) icon instead.
If you have time, could you please help me with a fiddle for the demo? Thanks a lot in advance.
I wanted to implement this library in my project, but I make a mistake I tell my JSON has this structure
SEND ME A MISTAKE BUT NOT UNDERSTAND WHAT CAN BE HAPPENING THE ERROR IS
Uncaught TypeError : Can not read property ' length' of undefined or your defeto also sends TypeError : f is undefined.
How could I resolve this error?
First of all, your JSON is invalid, you can check it at jsonlint.com. Secondly, you need to use ajax.dataSrc option as follows
dataSrc: ""
.sorry I went brackets to json is so
Hi Michael,
I use the
sScrollX
property of the DataTables,then the checkboxselect_all
doesn’t work. See http://jsfiddle.net/abhbs4x8/343Dear Michael,
Thanks a lot for the great job and sharing your knowledge !!
I’ve one very simple question … would it be possible to have a multi row selector ?
Example, I would like to have all the Checkboxes checked from Row 3 to Row 7 (by doing a Multi Select Style: OS and Items: row)
Best regards,
David
David, I think you’re referring to Select extension which didn’t exist at the time I wrote my article. You can achieve the same effect using that extension, see Checkbox selection example or Get selected item example.
Thanks for this beautiful tutorial, help me lot to integrate checkbox functionality on my datatable.
Hi, I have copied your code and trying to run with .json file in ajax call, i am not getting data columns, only checkbox column is coming. Ajax response is coming but not rendering on UI.
/nosuchpage
<= where is it?In the example the form is not actually submitted. Submission is prevented for demonstration purposes by
e.preventDefault();
. In production, you need to specify a path to your actual script that will be processing the request.Hey there. Can it be done if the data is from Oracle?
There shouldn’t be any restrictions as long as your server-side script returns data in the correct format, see this page for more details.
Thank you for this great tutorial.
I have applied your solution in the server processing mode, but I have an issue when I paginate, the previous selected ones become deselected.
Thoughts?
Alex
I’ve added server-side example, but couldn’t reproduce the problem, the code works fine. You can send me a link to your page, I will see if there is something wrong.
Thank you for this tutorial. Concept well explained. I am not using JQuery DataTables presently but I am using the idea here to implement my solution. I have tested your code, the plugin is awesome. Will use it in another solution.