0

I am using datatables and when row is clicked, I want to show details table just below. Code works fine, but I am trying to populate the Details Table (when row clicked) with data from a field in the row which I need splitted.

Here is the code... The var names = d[11] is the field which I need splitted then shown below. The code below works fine, but the details table created is just dummy data... I have not been able to create the loop to put the different 'charges' within the splitted string

In summary, each string which needs to be splitted, needs to be separated first by | character. Then each 'charge' needs to be separated by the ^ character and put on each table cell to display it like the current dummy data. Each record row/string to split can have a different number of charges (separated by |). So not all have the same (some have 2 charges, other 5 charges, other 10 charges)

Look forward to your comments.

code:

<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<link href="https://nightly.datatables.net/css/jquery.dataTables.css" rel="stylesheet" type="text/css" />
<script src="https://nightly.datatables.net/js/jquery.dataTables.js"></script>
<meta Content-Type: text/html; charset=ISO-8859-1>
<title>INTERMODAL</title>
</head>
<BODY>
<style>

td.dt-control {
  background: url("https://www.datatables.net/examples/resources/details_open.png") no-repeat center center;
  cursor: pointer;
}
 
tr.dt-hasChild td.dt-control {
  background: url("https://www.datatables.net/examples/resources/details_close.png") no-repeat center center;
}


.dataTables_wrapper {float: none; text-align:left; font-family: Verdana, Geneva, Tahoma; font-size: 13px;}
</style>
<h3 style='font-family:arial,georgia,garamond,serif;'>DATABASE (06-mar-23 15:12)</h3>
<h4 style='font-family:arial,georgia,garamond,serif;'>You can search by Location Name or ZipCode</h4>
<div class='demo-html' style=width:70%>
<table id='example' width='100%' class='table compact stripe'></table>
</div>

<script>


/* Formatting function for row details - modify as you need */
function format ( d ) {

    //  HERE IS AN EXAMPLE OF WHAT THE STRING THAT NEEDS TO BE SPLITTED FIRST BY |THEN BY ^ LOOKS KIKE....
    //d[11] = 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'
   
    var names = d[11]
    var nameArr = names.split('|');
    var names2 =nameArr[1]
    var nameArr2 = names2.split('^');    

    // LOOP NEEDED TO LOOP BY NameArr, then again by NameArr2 and add to table

    return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:750px;">'+
    
// this table creation is dummy code, which I tried several way but couldn't work it out

    
    '<tr>'+
            '<td style="width:150px;bold:true;">Charge Description</td>'+
        '<td style="width:50px;">CUR</td>'+
            '<td style="width:50px;">20GE</td>'+
        '<td style="width:50px;">40GE</td>'+
        '<td style="width:50px;">40HC</td>'+
            '<td style="width:50px;">basis</td>'+
        '</tr>'+    
        '<tr>'+         
        '<td style="width:150px;">Freight</td>'+
        '<td style="width:50px;">USD</td>'+
            '<td style="width:50px;">1500</td>'+
        '<td style="width:50px;">2500</td>'+
        '<td style="width:50px;">2500</td>'+
        '<td style="width:50px;">CNTR</td>'+
        '</tr>'+
        '<tr>'+
            '<td>Origin Charge</td>'+
        '<td style="width:50px;">USD</td>'+
            '<td>150</td>'+
            '<td>300</td>'+
            '<td>300</td>'+
        '<td style="width:50px;">CNTR</td>'+
        '</tr>'+
        '<tr>'+
            '<td>Middle Charges</td>'+
'<td style="width:50px;">EUR</td>'+
            '<td>150</td>'+
            '<td>300</td>'+
            '<td>300</td>'+     
        '<td style="width:50px;">CNTR</td>'+       
        '</tr>'+
    '<tr>'+
             '<td>Other Charges</td>'+
'<td style="width:50px;">USD</td>'+
            '<td>150</td>'+
            '<td>300</td>'+
            '<td>300</td>'+
        '<td style="width:50px;">CNTR</td>'+
        '</tr>'+
    '<tr>'+
            '<td>'+nameArr2[0]+'</td>'+
        '<td>'+nameArr2[4]+'</td>'+
            '<td>'+nameArr2[1]+'</td>'+
            '<td>'+nameArr2[2]+'</td>'+
            '<td>'+nameArr2[3]+'</td>'+
        '<td style="width:50px;">CNTR</td>'+
        '</tr>'+

    '</table>';
}      

var IntermodalSet = [
['Algeciras', '(CADIZ)', 'ES-11201;ES-11202;ES-11203;ES-11204;ES-11205;ES-11206;ES-11207;ES-11390;ES-11391', '5', 'ALGECIRAS', 'MAIN PORT', 'EUR', '€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'], 
['SAN ROQUE ', '(CADIZ)', 'ES-11310;ES-11311;ES-11312;ES-11313;ES-11314;ES-11360;ES-11368;ES-11369', '15', 'ALGECIRAS', 'MAIN PORT', 'EUR', '€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'],
['CASTELLAR DE LA FRONTERA', '(CADIZ)', 'ES-11350', '26', 'ALGECIRAS', 'MAIN PORT', 'EUR', '€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'],
['LOS BARRIOS', '(CADIZ)', 'ES-11370;ES-11379', '21', 'ALGECIRAS', 'MAIN PORT', 'EUR','€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'],
['JIMENA DE LA FRONTERA', '(CADIZ)', 'ES-11320;ES-11330;ES-11339;ES-11340', '42', 'ALGECIRAS', 'MAIN PORT', 'EUR', '€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD'],
['CASARES', '(MALAGA)', 'ES-29690;ES-29692', '55', 'ALGECIRAS', 'MAIN PORT', 'EUR', '€100', '€100' , '€100', '', 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD|Additional Charges^30^60^60^USD'],
];
  
    
$(document).ready(function () {
var table =$('#example').DataTable({
'bFilter':true,
'pageLength': 20, 
data: IntermodalSet,
'order': [[ 2, 'asc' ], [ 8, 'asc' ]], 
columns: [
    { title: 'LOCATION' },
    { title: '(PROVINCE)' },
    { title: 'ZIPCODE' },
    { title: 'KMS' },
    { title: 'PORT' },
    { title: 'MAIN' },
    { title: 'CUR' },
    { title: 'PRICE1' },
    { title: 'PRICE2' },
    { title: 'PRICE3' },
    { title: 'TOLL' },
    { title: 'ALLIN' },
    { title: 'Details', className: 'dt-control', orderable: false, data: null, defaultContent: '',},
    
 ],
'bAutoWidth': false, 
'dom': 'Qlfrtip',

'columnDefs': [{searchBuilder: {defaultCondition: '='},targets:[0]}],

'columnDefs': [

               {'width':'20%'  , visible: true, 'targets':[0], 'searchable': true }, 
               {'width':'100px', visible: true, 'targets':[1],  'searchable': false}, 
               {'width':'23%'  , visible: true, 'targets': [2], 'searchable': true},  
               {'width':'25px' , visible: true, 'targets':[3],  'searchable': false, className:'dt-right'},  
               {'width':'0px'  , visible: false, 'targets':[6], 'searchable': false, className:'dt-right'} , 
               {'width':'100px', visible: true,  'targets':[4], 'searchable': false} , 
               {'width':'0px'  , visible: false, 'targets':[5],  'searchable': false} , 
               {'width':'65px' , visible: true,  'targets': [7,8,9,10], 'searchable': false, className:'dt-right'},  
           {'width':'1px' , visible: false, 'targets': [11], 'searchable': false, className:'dt-right'}, 
        
            
], 
 });

 // Add event listener for opening and closing details
    $('#example tbody').on('click', 'td.dt-control', function () {
        
        var tr = $(this).closest('tr');
        var row = table.row(tr);

        if (row.child.isShown()) {
            // This row is already open - close it
            row.child.hide();
            tr.removeClass('shown');
        } else {            
            // Open this row
            row.child(format(row.data())).show();
            tr.addClass('shown');
        }
    });
   

});    

</script>

</body>
</html>
user1135218
  • 393
  • 1
  • 8
  • 26
  • There are now [better ways](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks) to construct HTML content dynamically than how your `format()` function does it. – jarmod Mar 25 '23 at 16:23

1 Answers1

2

Here is an example which shows one approach, using forEach():

// in your case, `sampleInput` will be `d[11]`:
var sampleInput = 'Taxes and Duties^150^150^150^USD|Handling Charge^111^111^111^USD|Destination Charges^111^111^111^USD';

// each new subtable starts with this HTML (header row):
var subTable = '<table><tr>' +
  '<td style="width:150px;bold:true;">Charge Description</td>' +
  '<td style="width:50px;">CUR</td>' +
  '<td style="width:50px;">20GE</td>' +
  '<td style="width:50px;">40GE</td>' +
  '<td style="width:50px;">40HC</td>' +
  '<td style="width:50px;">basis</td>' +
  '</tr>';

// the `slice` function ignores the first entry in the array (index 0) and only reads the array from index 1 onwards:
sampleInput.split('|').slice(1).forEach((charge) => {
  // take each charge line item and create an array:
  var chargeItem = charge.split('^');

  // use the line item fields - the same as you already do:
  subTable = subTable + '<tr>' +
    '<td>' + chargeItem[0] + '</td>' +
    '<td>' + chargeItem[4] + '</td>' +
    '<td>' + chargeItem[1] + '</td>' +
    '<td>' + chargeItem[2] + '</td>' +
    '<td>' + chargeItem[3] + '</td>' +
    '<td style="width:50px;">CNTR</td>' +
    '</tr>';
});

subTable = subTable + '</table>';

// This is what you need to `return` from your `format` function:
console.log(subTable);

I have added comments in the code.

The code generates HTML as follows (formatted for readability):

<table>
    <tr>
        <td style="width:150px;bold:true;">Charge Description</td>
        <td style="width:50px;">CUR</td>
        <td style="width:50px;">20GE</td>
        <td style="width:50px;">40GE</td>
        <td style="width:50px;">40HC</td>
        <td style="width:50px;">basis</td>
    </tr>
    <tr>
        <td>Handling Charge</td>
        <td>USD</td>
        <td>111</td>
        <td>111</td>
        <td>111</td>
        <td style="width:50px;">CNTR</td>
    </tr>
    <tr>
        <td>Destination Charges</td>
        <td>USD</td>
        <td>111</td>
        <td>111</td>
        <td>111</td>
        <td style="width:50px;">CNTR</td>
    </tr>
</table>

The only other change I made was to use UTF-8 here:

<meta Content-Type: text/html; charset=UTF-8>

Without this my Euro symbols were not rendered correctly in the main DataTable.


Update

I'd expect the table cell construction code such as '' + chargeItem[0] + '' to be vulnerable to HTML injection attacks.

Yes, I failed to mention that.

DataTables has a render function:

render: DataTable.render.text()

This can be used to:

"ensure that any potentially dangerous HTML in the source data will not be executed by escaping the HTML entities."

However, there are 2 caveats:

  1. This can be applied to DataTables column data, but not to data returned by functions which build child data (as used in this question).

  2. The code behind the render function basically converts selected HTML reserved characters to HTML entitites:

d.replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;') 

For all I know there may be loopholes in the above approach.

For what it's worth (given caveat 2 above) my approach could be improved by using a function:

function escape( d ) {
  return d.replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
}

And then using it as follows:

subTable = subTable + '<tr>' +
    '<td>'+ escape(chargeItem[0]) +'</td>' +
    '<td>'+ escape(chargeItem[4]) +'</td>' +
    '<td>'+ escape(chargeItem[1]) +'</td>' +
    '<td>'+ escape(chargeItem[2]) +'</td>' +
    '<td>'+ escape(chargeItem[3]) +'</td>' +
    '<td style="width:50px;">CNTR</td>' +
    '</tr>';

I would be interested in know if this approach has weaknesses and if there are more robust solutions.

andrewJames
  • 19,570
  • 8
  • 19
  • 51
  • 1
    I'd expect the table cell construction code such as `'' + chargeItem[0] + ''` to be vulnerable to HTML injection attacks. – jarmod Mar 25 '23 at 16:19
  • wow, what can I say? I am in tears. That was an elegant, short, simple to follow answer! Works perfectly, and it was exactly what I was after. Cannot thank you enough – user1135218 Mar 25 '23 at 16:50
  • 1
    @jarmod - Your point is well taken - thank you. I have added some notes to the answer - but I realize they are almost certainly not a definitive solution - and there are many (many!) alternatives - for example [Can I escape HTML special chars in JavaScript?](https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript). – andrewJames Mar 25 '23 at 17:35
  • 1
    I appreciate the additional information about the injection issues. I have used the function escape and works perfectly. Again, cannot thank you enough. – user1135218 Mar 25 '23 at 17:37