I am using the JQuery Datatables plugin (https://datatables.net/).
I would like to be able to reference the original HTML <th></th> column header values.
<table border="0">
<thead>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</thead>
<tbody>
<tr>
<td>data for column 1</td>
<td>data for column 2</td>
<td>data for column 3</td>
</tr>
</tbody>
</table>
I know that columns can be named within the plugin using the columns property:
$('#mytable').DataTable(
{
columns: [
{name: 'column1'},
{name: 'column2'},
{name: 'column3'}
]
}
)
However it would be very useful when dealing with dynamically created HTML to be able to reference the HTML <th> tag in order to find the Datatables index for the column with a specific name.
I have had a look at dataTable.context[0].aoHeader and can see both and idx and innerHTML objets within that, however it looks like table().header() might provide a solution.
You can use the column.name values shown in the question to select the DataTables column index values.
Example:
table.column( 'column2:name' ).index()
Demo:
$(document).ready(function() {
var table = $('#mytable').DataTable(
{
columns: [
{ name: 'column1' },
{ name: 'column2' },
{ name: 'column3' }
]
}
)
let idx = table.column( 'column2:name' ).index();
console.log( idx ); // prints 1 (2nd column's index)
} );
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.0.js"></script>
<script src="https://cdn.datatables.net/1.12.1/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.12.1/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="mytable" class="display dataTable cell-border" style="width:100%">
<thead>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</thead>
<tbody>
<tr>
<td>data for column 1</td>
<td>data for column 2</td>
<td>data for column 3</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
This uses the column() API function, together with a column selector.
There are various different ways you can provide a column selector - one of which is the way I show in the above code:
'YOUR_COL_NAME_HERE:name'
Then the index() function returns the zero-based column index.
If you have draggable columns (i.e. columns can be reordered) then you may also need to add a selector modifier to account for that, depending on what specific index you want - the original one, or the currently displayed one.
Related
I have datatables and I try to export it to CSV. The column 7 is a date.
text: 'Csv',
extend: 'csvHtml5',
filename: "customers",
exportOptions: {
columns: [1, 2, 7,8],
format: {
body: function (data, row, column, node) {
//check if type is input using jquery
console.log(data)
return $(data).is("input") ? $(data).val() : data;
}
}
}
It works all OK if I don't use "format" but with format I have error:
Uncaught Error: Syntax error, unrecognized expression: 13/11/2021
When I log "data" without format, it is all OK. It looks like the slashes in date make problems in "format".
Any help:)
edit:
the date I take from firestore
let options = {
year: 'numeric',
month: '2-digit',
day: 'numeric',
//hour: 'numeric',
// minute: 'numeric'
// second: 'numeric'
};
dataSet.push([
...
oc.data().created ? doc.data().created.toDate().toLocaleDateString("en-GB", options) : "",
...
This is caused by your jQuery $(data) selector when the data variable contains one or more forward slashes. See:
Selector with string space and /; $(" /") throws uncaught error in jquery
Why is the slash character causing my selector to fail?
And probably others.
A simple (but potentially error-prone) approach would be to write some logic like this:
// possible, but not recommended:
data.startsWith('<input') ? $(data).val() : data
Instead of this type of approach, you can use DataTables' support for orthogonal data.
In this new approach we will target the column which contains <input> fields, and specify two different versions of these data values for that column:
The version we want to display in the table (i.e. the actual input HTML).
The version we want to export (i.e. the value contained in the input field).
To do this we have to specify this for the relevant column (or columns). In my test example, this is the second column - so col index 1:
columnDefs: [
{
targets: [1],
render: function (data, type, row) {
return type === 'export' ? $(data).val() : data;
}
}
]
Here, I have specified that there will be a custom data type called export for the 2nd column - and its value will be the result of $(data).val() - and all other built-in orthogonal data types (display, sort, filter) will just use the raw data value.
Now I can use this new orthogonal data type in my export section of the DataTable:
exportOptions: {
columns: [0, 1, 4, 5],
orthogonal: 'export'
}
The overall solution using my test data is as follows:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.23/css/jquery.dataTables.min.css"/>
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.6.5/css/buttons.dataTables.min.css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/dataTables.buttons.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.colVis.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.html5.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.print.min.js"></script>
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td><input type="text" value="System Architect"></td>
<td>Edinburgh</td>
<td>61</td>
<td>13/11/2021</td>
<td>$320,800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td><input type="text" value="Accountant"></td>
<td>Tokyo</td>
<td>63</td>
<td>14/12/2021</td>
<td>$170,750</td>
</tr>
<tr>
<td>Donna Snider</td>
<td><input type="text" value="Dev/Ops"></td>
<td>New York</td>
<td>27</td>
<td>15/01/2022</td>
<td>$112,000</td>
</tr>
</tbody>
</table>
</div>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
columnDefs: [
{
targets: [1],
render: function (data, type, row) {
return type === 'export' ? $(data).val() : data;
}
}
],
dom: 'Brftip',
buttons: [ {
text: 'Csv',
extend: 'csvHtml5',
name: 'testExport',
exportOptions: {
columns: [0, 1, 4, 5],
orthogonal: 'export'
}
} ]
} );
} );
</script>
</body>
</html>
The table looks like this:
And the exported CSV data is:
"Name","Position","Start date","Salary"
"Donna Snider","Dev/Ops","15/01/2022","$112,000"
"Garrett Winters","Accountant","14/12/2021","$170,750"
"Tiger Nixon","System Architect","13/11/2021","$320,800"
The benefit: I do not need any potentially error-prone logic to detect whether my strings contain / characters, or begin with <input... and so on.
I have this code currently:
drawCallback: function () {
let api = this.api();
// hide columns that add up to 0
api.columns().every(function (i) {
let sum = this.data().sum();
if (sum === 0 && typeof sum === 'number' && i !== 0) {
api.column(i).visible(0);
}
});
}
There is an issue with it, however... for one particular column where the values are just a single word, such as "Completed" or "Pending", it seems as though this.data().sum(); will result in 0 rather than NaN.
The first two columns will result in NaN, seemingly because they are multi-word sentences, which is the only difference between these two columns.
This is a sample of the data
<td>Foo Bar Jones Jonesy McFoo Department</td>
<td>ABC CityName Location Code XXFOO</td>
<td data-sort="20201002">10/02/2020</td>
<td>Pending</td>
<td>(a variable number of columns with data that is some number that is possibly zero or greater)</td>
Isn't there some way to check the column type with api.columns().every()?
I am not able to recreate your specific error with single-word cell values vs. multi-word cell values. When I use parseFloat('x') vs. parseFloat('x y z'), they both evaluate to NaN.
(Also, I don't know how you implemented your sum() function, so that may explain what you are seeing.)
I am going to use the following example from here, as a starting point for my sum() - but with a modification:
$.fn.dataTable.Api.register( 'column().data().sum()', function () {
return this.reduce( function (a, b) {
var x = parseFloat( a ); // removed the 'or' operator: || 0;
var y = parseFloat( b ); // removed the 'or' operator: || 0;
return x + y;
} );
} );
My change (removing the "or" operators) ensures NaN values are not set to zero, but remain as NaN - which is what you need for your specialized sum() function.
Now, if I use this with your DataTable code, and some test data based on what you provided, I get the expected outcome:
As a side note, dates may be parsed into numbers OK, depending on the data format - which can explain why date columns can show up OK (they sum to a positive number).
Here is the full standalone demo, in case it helps.
In this example, all the data in the final column ("Val 3") are zeros - so that column is hidden. The column remains hidden if there are non-zero values which sum up to zero, also:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Dept</th>
<th>Loc</th>
<th>Date</th>
<th>Status</th>
<th>Val 1</th>
<th>Val 2</th>
<th>Val 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 1</td>
<td>ABC CityName Location Code XXFOO</td>
<td data-sort="20200119">19/01/2020</td>
<td>Active</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 2</td>
<td>DEF CityName Location Code XXFOO</td>
<td data-sort="20201002">10/02/2020</td>
<td>Pending</td>
<td>0</td>
<td>2</td>
<td>0</td>
</tr>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 3</td>
<td>XYZ CityName Location Code XXFOO</td>
<td data-sort="20201016">16/02/2020</td>
<td>Pending</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
$.fn.dataTable.Api.register( 'column().data().sum()', function () {
return this.reduce( function (a, b) {
var x = parseFloat( a );
var y = parseFloat( b );
return x + y;
} );
} );
$(document).ready(function() {
var table = $('#example').DataTable( {
drawCallback: function () {
let api = this.api();
// hide columns that add up to 0
api.columns().every(function (i) {
let sum = this.data().sum();
if (sum === 0 && typeof sum === 'number' && i !== 0) {
api.column(i).visible(0);
}
});
}
} );
} );
</script>
</body>
</html>
I'm having a problem with my table when I scroll to the right
this is my code
TableComponent.vue
<div id="main-container">
<table class="maint-table">
<thead id="table-header">
<tr>
<th class="dates"> </th>
<th v-for="data in dateHeader">{{data}}</th>
</tr>
<tr>
<th class="title"> </th>
<th v-for="data in dayOfWeek">{{data}}</th>
</tr>
</thead>
<tbody id="table-body" #scroll="fixedScroll">
<table_block :table_data="dataHeader"></table_block>
<table_block :table_data="allData"></table_block>
</tbody>
</table>
</div>
...
...
...
<script>
components: {
table_block
},
methods: {
fixedScroll() {
fixedScroll(event) {
var thead = document.getElementById("table-header");
var tbodyScroll = document.getElementById("table-body").scrollLeft;
thead.scrollLeft = tbodyScroll;
}
</script>
I made a props to pass the data to my TableBlock to loop through the data and display it on the table. This is my TableBlock Code.
TableBlock.vue
<template>
<div>
<tr v-for="row in table_data">
<td>
<div class="drop-down-container"><span class="drop-down-controller"">{{ row.title }}</span></div>
</td>
<td v-for="cel in row.data" class="group-header">{{ cel }}</td>
</tr>
</div>
</template>
When I scroll it to the right, the first column must freeze but it's not.
I tried to create a dummy data inside TableComponent.vue with a normal HTML Table without passing the data to another component using props, it works perfectly. But when I use these codes, it doesn't work correctly.
Scenario
Let say I have 10 columns in the table, when I scroll it to the right, the 1st column will stick which is normal but when the 10th column reach to 1st column, the 1st column will scroll away together with the 10th column.
I'm trying my best to illustrate the scenario but this is the best that I can do. If someone can help, please help me.
I'm trying to optimize the Datatables buttons pdfHtml5 export of a page. The table data contains nested html tags which are creating additional space above and below the cell data, which makes the PDF very long.
The text in my cell is wrapped in two nested <div> and a <p>. In the PDF export, I only need the contents of the <p>
<td>
<div class="flagimg" style="background-image: url(...)">
<div class="flagtext">
<p>name of country</p>
</div>
</div>
</td>
I'm trying to remove nested html tags using exportOptions, but I'm not sure how to write the syntax correctly. Can anyone help me with this?
$(document).ready(function() {
var buttonCommon = {
exportOptions: {
format: {
body: function(data, column, row) {
data = data.replace(/<div class="flagtext"\">/, '');
data = data.replace(/<.*?>/g, "");
return data;
}
}
}
};
var oTable = $('#example').DataTable({
dom: 'Bfrtip',
buttons: [
$.extend( true, {}, buttonCommon, {
extend: 'copyHtml5'
} ),
$.extend( true, {}, buttonCommon, {
extend: 'excelHtml5'
} ),
$.extend( true, {}, buttonCommon, {
extend: 'pdfHtml5'
} )
]
});
})
I finally discovered that the problem is not the nested div after all, but rather that the tags are indented in the code instead of being on one line. I've reported this to Datatables and I'm documenting the problem here, in case anyone else runs into it.
I've built on the fiddle #davidkonrad made to illustrate what's happening.
https://jsfiddle.net/lbriquet/7f08n0qa/
In the first row, the nested tags are indented in the code... this produces extra space above and below the country name data in the PDF export.
In the second row I've put all of the tags in the same line of code... and no extra spacing is produced in the PDF export.
<table id="example" width="100%" border="0" cellspacing="0" cellpadding="0" >
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="myclass">Company name
</div>
</td>
<td>
<div class="flagimg" style="background-image: url(#">
<div class="flagtext">
<p>Country name</p>
</div>
</div>
</td>
<td>
<div class="myclass">Product sold</div>
</td>
</tr>
<tr>
<td>
<div class="myclass">Company name
</div>
</td>
<td><div class="flagimg" style="background-image: url(#)"><div class="flagtext"><p>Country name</p></div></div>
</td>
<td>
<div class="myclass">Product sold</div>
</td>
</tr>
</tbody>
</table>
I have an HTML table in which I have applied the DataTables function to. I use the first row of the table with the class 'template' applied as my template row. Then pick this formatting up and populate all the rows in the table using a JSON feed. The problem is that the pagination provided by DataTables includes this hidden template row so always makes my first page display 1 less row than all the others.
Is there a way to exclude any rows (of which there will only be one) with the class 'template' applied to the tr?
<!-- DataTables CSS -->
<link href="/bower_components/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css" rel="stylesheet">
<!-- DataTables Responsive CSS -->
<link href="/bower_components/datatables-responsive/css/dataTables.responsive.css" rel="stylesheet">
<div class="alert-message"></div>
<div class="dataTable_wrapper">
<table class="loadtable table table-hover table-stripped" id="problem_table" data-page="0" data-params="" data-orderby="p.name" data-orderdir="DESC" data-url="/admin/problem/ajax/tables/problem" cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th class="orderable asc">Name</th>
<th class="orderable no-sort" width="10%">Helpful?</th>
<th class="orderable" width="15%">Created</th>
<th class="orderable c" width="10%">Live?</th>
<th class="r no-sort" width="12%">Actions</th>
</tr>
</thead>
<tbody>
<tr id="problem_#PROBLEMID#" class="template #ROWCLASS#">
<td class="orderable asc">#NAME#</td>
<td class="orderable"><span class="fa fa-thumbs-o-up"> #UP_VOTE#</span> <span class="fa fa-thumbs-o-down"> #DOWN_VOTE#</span></td>
<td class="orderable">#CREATED#</td>
<td class="orderable c"><span class="fa #IS_LIVE#"></span></td>
<td class="r last">#ACTIONS#</td>
</tr>
</tbody>
</table>
</div>
$(document).ready(function() {
delay( function() {
$('#problem_table').DataTable({
responsive: true,
pageLength: 20,
aLengthMenu: [[20, 40, 60, -1], [20, 40, 60, "All"]],
aoColumnDefs : [{ "bSortable" : false, "aTargets" : [ "no-sort" ] }]
});
}, 200 );
});
You can use the good old custum row filter for this :
$.fn.dataTableExt.afnFiltering.push(
function( oSettings, aData, iDataIndex ) {
var row = oSettings.aoData[iDataIndex].nTr;
return $(row).hasClass('template') ? false : true;
}
);
Even though it is pre-1.10.x hungarian notation, it still works with DataTable() instances.
demo -> http://jsfiddle.net/zaxkrc49/