I'm trying to create a table where more details can be seen when the plus-image is clicked - similar to the DataTables hidden row details example
Unfortunately there is a warning being printed as JavaScript alert and also the table header is misplaced - as if there would be too many or not enough table cells in it:
I have prepared a simple test case, which will work instantly, when you save it to a file and open it in a browser:
<!DOCTYPE HTML>
<html>
<head>
<link type="text/css" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css">
<link type="text/css" rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>
<script type="text/javascript">
var data = [
{"Total":17,"A":0,"B":0,"Details":{"BSN":"1147387861","ProjectName":"R127","StationName":"D"},"C":0,"D":17,"Test":"GSM_1900_GMSK_TXPOWER_HP_H","Measurement":"MEASUREMENT"},
{"Total":8,"A":0,"B":0,"Details":{"BSN":"1147387861","ProjectName":"R127","StationName":"D"},"C":0,"D":8,"Test":"TX_PWR_64_54","Measurement":"POWER"}
];
$(function() {
function fnFormatDetails(oTable, nTr) {
var aData = oTable.fnGetData(nTr);
var sOut = '<table bgcolor="yellow" cellpadding="8" border="0" style="padding-left:50px;">';
sOut += '<tr><td>BSN:</td><td>' + aData['Details']['BSN'] + '</td></tr>';
sOut += '<tr><td>Station:</td><td>' + aData['Details']['StationName'] + '</td></tr>';
sOut += '<tr><td>Project:</td><td>' + aData['Details']['ProjectName'] + '</td></tr>';
sOut += '</table>';
return sOut;
}
var fails = $('#fails').dataTable({
bJQueryUI: true,
sPaginationType: 'full_numbers',
aaData: data,
aaSorting: [[2, 'desc']],
aoColumns: [
{ mDataProp: 'Test', bSearchable: true, bSortable: true },
{ mDataProp: 'Measurement', bSearchable: true, bSortable: true },
{ mDataProp: 'Total', bSearchable: false, bSortable: true },
{ mDataProp: 'A', bSearchable: false, bSortable: true },
{ mDataProp: 'B', bSearchable: false, bSortable: true },
{ mDataProp: 'C', bSearchable: false, bSortable: true },
{ mDataProp: 'D', bSearchable: false, bSortable: true },
]
});
var th = document.createElement('th');
var td = document.createElement('td');
td.innerHTML = '<img src="http://www.datatables.net/release-datatables/examples/examples_support/details_open.png" class="details">';
$('#fails tbody th').each(function() {
this.insertBefore(th, this.childNodes[0]);
});
$('#fails tbody tr').each(function() {
this.insertBefore(td.cloneNode(true), this.childNodes[0]);
});
$('#fails tbody').on('click', 'td img.details', function() {
var nTr = $(this).parents('tr')[0];
if (fails.fnIsOpen(nTr)) {
this.src = 'http://www.datatables.net/release-datatables/examples/examples_support/details_open.png';
fails.fnClose(nTr);
} else {
this.src = 'http://www.datatables.net/release-datatables/examples/examples_support/details_close.png';
fails.fnOpen(nTr, fnFormatDetails(fails, nTr), 'details');
}
});
});
</script>
</head>
<body>
<table id="fails" cellspacing="0" cellpadding="4" width="100%">
<thead>
<tr>
<th>Test</th>
<th>Measurement</th>
<th>Total</th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</html>
Does anybody please have an idea, how to fix this?
I've tried adding/removing <th>Details</th>
in the HTML body, but it didn't help.
I've also asked this question at the DataTables forum.
UPDATE:
I've received helpful comments by DataTables author and have decided to just prepend the plus-image to the contents of the first cell in each row - instead of adding a new cell to each row.
Unfortunately I have a new problem: the plus-image is displayed, but the orinigal text (the Test name) is gone:
Here is my new code (the plus-image is prepended by propTest
):
<!DOCTYPE HTML>
<html>
<head>
<link type="text/css" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css">
<link type="text/css" rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables_themeroller.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js"></script>
<script type="text/javascript">
var data = [
{"Total":17,"A":0,"B":0,"Details":{"BSN":"1147387861","ProjectName":"R127","StationName":"D"},"C":0,"D":17,"Test":"GSM_1900_GMSK_TXPOWER_HP_H","Measurement":"MEASUREMENT"},
{"Total":8,"A":0,"B":0,"Details":{"BSN":"1147387861","ProjectName":"R127","StationName":"D"},"C":0,"D":8,"Test":"TX_PWR_64_54","Measurement":"POWER"}
];
function propTest(data, type, val) {
if (type === 'set') {
console.log(val); // for some reason prints "null"
data.name = val;
data.display = '<img src="http://www.datatables.net/release-datatables/examples/examples_support/details_open.png" width="20" height="20" class="details"> ' + val;
return;
}
if (type === 'display') {
return data.display;
}
// 'sort', 'type', 'filter' and undefined
return data.name;
}
$(function() {
function fnFormatDetails(oTable, nTr) {
var aData = oTable.fnGetData(nTr);
var sOut = '<table bgcolor="yellow" cellpadding="8" border="0" style="padding-left:50px;">';
sOut += '<tr><td>BSN:</td><td>' + aData['Details']['BSN'] + '</td></tr>';
sOut += '<tr><td>Station:</td><td>' + aData['Details']['StationName'] + '</td></tr>';
sOut += '<tr><td>Project:</td><td>' + aData['Details']['ProjectName'] + '</td></tr>';
sOut += '</table>';
return sOut;
}
var fails = $('#fails').dataTable({
bJQueryUI: true,
sPaginationType: 'full_numbers',
aaData: data,
aaSorting: [[2, 'desc']],
aoColumns: [
{ mData: propTest, bSearchable: true, bSortable: true },
{ mData: 'Measurement', bSearchable: true, bSortable: true },
{ mData: 'Total', bSearchable: false, bSortable: true },
{ mData: 'A', bSearchable: false, bSortable: true },
{ mData: 'B', bSearchable: false, bSortable: true },
{ mData: 'C', bSearchable: false, bSortable: true },
{ mData: 'D', bSearchable: false, bSortable: true }
]
});
$('#fails tbody').on('click', 'td img.details', function() {
var nTr = $(this).parents('tr')[0];
if (fails.fnIsOpen(nTr)) {
this.src = 'http://www.datatables.net/release-datatables/examples/examples_support/details_open.png';
fails.fnClose(nTr);
} else {
this.src = 'http://www.datatables.net/release-datatables/examples/examples_support/details_close.png';
fails.fnOpen(nTr, fnFormatDetails(fails, nTr), 'details');
}
});
});
</script>
</head>
<body>
<table id="fails" cellspacing="0" cellpadding="4" width="100%">
<thead>
<tr>
<th>Test</th>
<th>Measurement</th>
<th>Total</th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</html>
There are several ways to achieve this type of behavior with DataTables (DT). You can achieve your original goal or use the same column for the expander and the Test
data.
I am taking the time to show several options, as I am hoping that this could serve as reference to others asking to do something similar.
For all of the following, your table should contain all of the desired columns prior to the DT's creation (either statically or dynamically).
This is the option that requires the least amount of modification to your existing example and is probably the easiest. Just add a sDefaultContent
option for your extra column:
{
mData: null,
bSearchable: false,
bSortable: false,
sDefaultContent: '<div class="expand /">',
sWidth: "30px"
},
The mData
property is set to null
, as it its content has nothing to do with the row's data array. This makes DT to use the value in sDefaultContent
.
I took the liberty of using CSS for the expander button, as this makes the example simpler and also decouples behavior and presentation.
The CSS I used:
tr div.expand {
width: 20px;
height: 20px;
background-image: url('http://www.datatables.net/release-datatables/examples/examples_support/details_open.png');
}
tr div.open {
background-image: url('http://www.datatables.net/release-datatables/examples/examples_support/details_close.png');
}
and the expander function:
$('#fails tbody').on('click', 'td div.expand', function () {
var nTr = $(this).parents('tr')[0];
if (fails.fnIsOpen(nTr)) {
$(this).removeClass('open');
fails.fnClose(nTr);
} else {$.fn.dataTableExt.sErrMode = 'throw' ;
$(this).addClass('open');
fails.fnOpen(nTr, fnFormatDetails(fails, nTr), 'details');
}
});
We target a div
with an expand
class, and toggle its open
class as needed.
JSBin example.
You can specify an aoColumnDefs
config array, which is more powerful and flexible than the aoColumns
config.
Specifying a renderer function for that column is done via themRender
config property.
The relevant column's settings:
aoColumnDefs: [{
aTargets: [0],
mData: 'Test',
bSearchable: true,
bSortable: true,
mRender: expandRenderer
},
...
]
That function is called multiple times in different contexts and should tell the DT how to display the data in the table itself, as well as help determine the type of data for sorting and filtering purposes.
Renderer:
function expandRenderer(data, type, full) {
switch (type) {
case 'display':
return '<div class="expand-wrapper">'+
'<span class="expand"></span>'+
'<span class="data">'+
data+
'</span></div>';
case 'type':
case 'filter':
case 'sort':
return data;
}
}
It takes 3 arguments:
data
: The data from the row's array, in this case, the string stored in Test
.type
: The type of request. It denotes what the DT expects to get back:
'display'
: what will be displayed in the cell itself.'type'
: what returned from the function will be used to determine the row's data type.'filter'
, 'sort'
: The data to be used in filration (search) and sorting of the table.full
: The complete data for this row.This time, I decided to use span
s wrapped by a div
.
In order to center the icon vertically and have the table keep everything in one line, I used the following CSS:
tr span.expand {
width: 20px;
height: 20px;
background-image: url('http://.../details_open.png');
display:inline-block;
vertical-align: middle;
margin-right: 5px;
}
div.expand-wrapper {
white-space:nowrap;
}
vertical-align: middle
here works as intended, since this is an inline
display (inline-block
, specifically).
Without this CSS, DT wouldn't handle the column widths as gracefully, as the first column is not really aware of the extra space taken by the expander.
Without this CSS:
With this CSS:
And the corresponding JSBin example.
I dislike the use of alert()
to display errors. For DT, you can change this behavior by changing its error mode setting:
$.fn.dataTableExt.sErrMode = 'throw' ;
This will throw an exception, so you can catch it JIT using your console, or have it logged if uncaught.
DataTable column options.
DataTable options quick reference.
PS,
Further customization can be done by passing a function as the mData
configuration parameter, but this is beyond the scope of this answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With