Datatables with editable child tables using a hidden table to coordinate column widths
This is a nested table with shared headers, with a parent row that uses colspan for the wide message field, a child row for the wide pattern field, and another child row with a table for the details.
The width of the top-level headers, and of the detail table headers are set by finding the longest strings from each column, adding them to a hidden datatable, drawing the table, checking the sizes, then updating the top-level column and detail table columns to match the hidden table.
A bit convoluted, and very hackish implementation, but it works.
by Christopher Austin
December 08, 2016
HTML
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.1/css/jquery.dataTables.css">
<script src="https://cdn.datatables.net/1.10.1/js/jquery.dataTables.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.6.2/jquery.mockjax.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<table id="messages" class="table compact row-border order-column" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Group</th>
<th>Key</th>
<th>Message</th>
<th>Category</th>
<th>Type</th>
<th>Action</th>
<th>Object</th>
<th>Context</th>
<th>Status</th>
<th>Reason</th>
</tr>
</thead>
</table>
<table id="width-table" class="table compact row-border order-column" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Group</th>
<th>Key</th>
<th>Category</th>
<th>Type</th>
<th>Action</th>
<th>Object</th>
<th>Context</th>
<th>Status</th>
<th>Reason</th>
</tr>
</thead>
</table>
JavaScript
console.clear()
var dataSet = [
{
"id": 9973,
"vendorCategory": "Security-Auditing",
"key": "4776",
"message": "The domain controller attempted to validate the credentials for an account.\n\nAuthentication Package: %1\nLogon Account: %2\nSource Workstation: %3\nError Code: %4",
"patterns": [
{
"id": 5237,
"idx": 1,
"category": "access",
"type": "user",
"action": "login",
"status": "success",
"pattern": "(?<detail>The domain controller attempted to validate the credentials for an account)\\. Authentication Package: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 (?:Logon Account: (?<user>.+?)|Logon Account:) Source Workstation: (?:(?<source>.*?) )?Error Code: (?<result>0x[0-9A-Fa-f]+)",
"fieldConditions": [
{
"field":"result",
"match":"0x0",
"status":"success"
},
{
"field":"result",
"match":"0xC0000064",
"status":"failure",
"reason":"invalid-user"
},
{
"field":"result",
"match":".+",
"status":"failure"
}
]
},
],
"DT_RowId": "row_1"
},
]
$.mockjax({
url: 'data.json',
responseTime: 10,
responseText: {
"data": dataSet
}
});
var dt
/*
* Join the sorted set of distinct field values from row using the provided separator
*/
function separatedFieldValues(row, field, sep) {
return jQuery.unique(
row.patterns.map( function(o) {return o[field]})
.filter(function(o) {return o != null})
.sort()
).join(sep)
}
function encoded(x) {
if (x === null) {
return ''
}
return $('<div/>').text(x).html()
}
function recordLongest(key, data, longest) {
if (!data[key]) {
return
}
if (!longest[key]) {
longest[key] = data[key]
}
else {
if (longest[key].length < data[key].length) {
longest[key] =...