Datatables. Hide / Show multiple rows within loop - javascript

This questions targets to Datatables plug-in for JQuery.
I have HTML table with 3 languages en, ru, fr. For example if English language is chosen French and Russian entries should be hidden, if Russian language is chosen then French and English entries should be hidden and so on.
In the HTML table id looks like:
etc_1_en
etc_1_ru
etc_1_fr
etc_2_en
etc_2_ru
etc_2_fr
etc_3_en
etc_3_ru
etc_3_fr
...
But each entry have the same class: row
HTML table like this:
<table>
<thead>
<tr>
<th>Header content 1</th>
<th>Header content 2</th>
</tr>
</thead>
<tbody>
<tr id="etc_1_en" class="row">
<td>Etc 1</td>
<td>Etc 2</td>
</tr>
<tr id="etc_1_ru" class="row">
<td>Etc 3</td>
<td>Etc 4</td>
</tr>
<tr id="etc_1_fr" class="row">
<td>Etc 5</td>
<td>Etc 6</td>
</tr>
<tr id="etc_2_en" class="row">
<td>Foo 1</td>
<td>Foo 2</td>
</tr>
<tr id="etc_2_ru" class="row">
<td>Foo 3</td>
<td>Foo 4</td>
</tr>
<tr id="etc_2_fr" class="row">
<td>Foo 5</td>
<td>Foo 6</td>
</tr>
</tbody>
</table>
There are 3 buttons with ID like:
btn_id_en
btn_id_ru
btn_id_fr
But each button have the same class: btn
Here is JQuery code:
tbl = $('#myTable').DataTable();
var rows = tbl.rows().data();
// there looping through all entries
rows.each(function (row, index) {
var row = tbl.row(index);
var data = row.data();
var id = row.id();
// there trying to assign each row to child variable (using Datatable)
var child = row.child;
if (/* some conditions */) { // if / else conditions is working, tested with console.log();
// this part not working, something wrong with child maybe
child.show();
} else {
// this part not working, something wrong with child maybe
child.hide();
}
}
row.id() returning correct ID of each row (tested via console.log(id);). Problem is that hide not working. I'm thinking that something wrong with declaring child.
Maybe I have to use remove() instead of hide(), but in this case how could I restore it after another button will be clicked?
Have you any ideas?

If you're use case is simply to have buttons then this should work:
var lang = ""
$.fn.dataTable.ext.search.push(
function(settings, data, dataIndex) {
var thisId = Object.keys(settings.aIds)[dataIndex].split("_")[2]
return thisId === lang;
}
);
var table = $('#example').DataTable({
"language": {
"infoFiltered": ""
}
});
$(".filter").click(function(e) {
lang = $(e.target).attr("id").split("_")[2];
table.draw();
});
Working JSFiddle here: https://jsfiddle.net/annoyingmouse/opu869ko/

That was fun!
You need a custom filter:
$.fn.dataTable.ext.search.push(
function(settings, data, dataIndex) {
var langs = new Set();
$(".filter").each(function(k, v) {
if ($(v).is(':checked')) {
langs.add($(v).attr("id").split("_")[2]);
}
});
var thisId = Object.keys(settings.aIds)[dataIndex].split("_")[2]
return langs.has(thisId);
}
);
And I'm wary of using buttons as you'd probably need to show the hidden rows at some point so I've replaced them with checkboxes:
<label for="btn_id_en">
<input class="filter" id="btn_id_en" type="checkbox" checked="checked">
English
</label>
<label for="btn_id_ru">
<input class="filter" id="btn_id_ru" type="checkbox" checked="checked">
Russian
</label>
<label for="btn_id_fr">
<input class="filter" id="btn_id_fr" type="checkbox" checked="checked">
French
</label>
<hr>
<table id="example" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Header content 1</th>
<th>Header content 2</th>
</tr>
</thead>
<tbody>
<tr id="etc_1_en" class="row">
<td>Etc 1</td>
<td>Etc 2</td>
</tr>
<tr id="etc_1_ru" class="row">
<td>Etc 3</td>
<td>Etc 4</td>
</tr>
<tr id="etc_1_fr" class="row">
<td>Etc 5</td>
<td>Etc 6</td>
</tr>
<tr id="etc_2_en" class="row">
<td>Foo 1</td>
<td>Foo 2</td>
</tr>
<tr id="etc_2_ru" class="row">
<td>Foo 3</td>
<td>Foo 4</td>
</tr>
<tr id="etc_2_fr" class="row">
<td>Foo 5</td>
<td>Foo 6</td>
</tr>
</tbody>
</table>
Working JSFiddle here.
Hope that helps.

child is not a variable but a function. Replacing:
var child = row.child;
with
var child = row.child();
should fix it.
See here: https://datatables.net/reference/api/row().child().hide()

Related

Parse HTML payload to JSON

I am facing a problem with the following payload.
The payload I am receiving on my Node.js backend on API call is as follows.
{
"id": "1",
"content":{
"text": "
<body>
<div>
<table>
<thead>
<tr>
<th><strong>Header 1</strong></th>
<th><strong>Header 2</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>question 1</td>
<td>answer 1</td>
</tr>
<tr>
<td>question 2</td>
<td>answer 2</td>
</tr>
<tr>
<td>question 3</td>
<td>answer 3</td>
</tr>
</tbody>
</table>
</div>
</body>
"
}
}
so here I am storing the response as following:
var response = data
i.e. data is the JSON response
And I am saving the HTML data as follows
var HTMLContentText = response.content.text
As a result I will get this:
<body>
<div>
<table>
<thead>
<tr>
<th><strong>Header 1</strong></th>
<th><strong>Header 2</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>question 1</td>
<td>answer 1</td>
</tr>
<tr>
<td>question 2</td>
<td>answer 2</td>
</tr>
<tr>
<td>question 3</td>
<td>answer 3</td>
</tr>
</tbody>
</table>
</div>
</body>
Here I want to perform the following operations
parse the HTML text into an object
get the table from the response
i.e. select("table").first();
get the rows of the table
i.e. select("tr")
I am having the same code in JAVA.
Here it is just for reference. Here, I used Jsoup parser for doing all the operations. I want to perform all in Javascript now.
// HTML as text (from JSON)
String HtmlFormattedText = (String)((JSONObject)JsonObject.get("content")).get("text");
// parse the html text into an object
Document HtmlFormattedDocumentObject = Jsoup.parse(HtmlFormattedText);
// get the table from the response
Element allRowsFromTable = HtmlFormattedDocumentObject.select("table").first();
// get the rows of the table
return allRowsFromTable.select("tr");
I created a snippet for this. The return value of the function contains all tr elements of the table - you don't need to select the table first.
const response = {
"id": "1",
"content": {
"text": `
<body>
<div>
<table>
<thead>
<tr>
<th><strong>Header 1</strong></th>
<th><strong>Header 2</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>question 1</td>
<td>answer 1</td>
</tr>
<tr>
<td>question 2</td>
<td>answer 2</td>
</tr>
<tr>
<td>question 3</td>
<td>answer 3</td>
</tr>
</tbody>
</table>
</div>
</body>
`
}
}
// logging the result of the function
console.log(parseTable(response.content.text))
function parseTable(string) {
// creating an HTML element
const el = document.createElement('html')
// adding the string to the HTML element
el.innerHTML = string
// selecting and returning all tr elements
return el.getElementsByTagName('tr')
}
Note: I changed the single quote to backtick in your data, as it allows multiline strings.

Getting value from another column when clicking targeted column JQuery

When the user clicks on the 'Number' column I want to be able to get the 'Name' column value in the same row. So for example if I clicked on '999' I would want to be able to get David.
$('#table').on('click', 'td:nth-child(2)', function()
{
row = $(this).text();
alert(row);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border id='table'>
<thead>
<td>Name</td>
<td>Number</td>
<td>Address</td>
</thead>
<tbody>
<tr>
<td>David</td>
<td>999</td>
<td>Street 1</td>
</tr>
<tr>
<td>Bob</td>
<td>555</td>
<td>Street 3</td>
</tr>
<tr>
<td>Jessica</td>
<td>068</td>
<td>Street 5</td>
</tr>
</tbody>
</table>
You can use $(this).closest('tr').find('td:first').text() to get the closest first td and its text.
If the wanted item is inside of the tr but you dont know the place or you want it dynamically, you can add a class or id to the name td and change the code to this $(this).closest('tr').find('#WantedRowId').text()
$('#table').on('click', 'td:nth-child(2)', function() {
alert($(this).closest('tr').find('td:first').text());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border id='table'>
<thead>
<td>Name</td>
<td>Number</td>
<td>Address</td>
</thead>
<tbody>
<tr>
<td>David</td>
<td>999</td>
<td>Street 1</td>
</tr>
<tr>
<td>Bob</td>
<td>555</td>
<td>Street 3</td>
</tr>
<tr>
<td>Jessica</td>
<td>068</td>
<td>Street 5</td>
</tr>
</tbody>
</table>
You can find below a more appropriate solution that dynamically checks what is the "Name" column index and retrieves accordingly.
As such, if you insert other columns, like ID and what not, it'll still work without updating this specific logic.
$('#table').on('click', 'td:nth-child(2)', function()
{
$td = $(this);
indexCol = $td.closest('table').find('td:contains(Name)').index()
alert( $td.closest('tr').find('td').eq(indexCol).text());
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border id='table'>
<thead>
<td>Name</td>
<td>Number</td>
<td>Address</td>
</thead>
<tbody>
<tr>
<td>David</td>
<td>999</td>
<td>Street 1</td>
</tr>
<tr>
<td>Bob</td>
<td>555</td>
<td>Street 3</td>
</tr>
<tr>
<td>Jessica</td>
<td>068</td>
<td>Street 5</td>
</tr>
</tbody>
</table>

Hide table rows based on date AND click a button to show them again

With help from this forum I created a table which hides groups of rows based on the current date. Which is great. I am now looking to combine this with a toggle button that shows the hidden dates. But with my very limited knowledge of Javascript I cannot get it done. Any ideas? This is what I have so far:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
$("p").slideToggle();
});
});
$(document).ready(function(e) {
$('.event_display_table tbody').each(function(index, element) {
event_day = $(this).attr('data-date');
event_day = new Date(event_day);
today = new Date();
if(event_day.getTime() < today.getTime())
{
$(this).css('display','none');
}
console.log($(this).attr('data-date'));
});
});
</script>
</head>
And for the body:
<body>
<p>This is a paragraph which can be hidden by clicking the button.</p>
<button>Toggle between slide up and slide down for a p element, but I want it to toggle 'show previous/hidden' dates</button>
<p>This table below shows only event dates in the future. Which is what I want. But I also want to have a toggle button option which does show the previous events. Is this possible?</p>
<div class="table-responsive">
<table class="event_display_table" border="1" >
<thead>
<tr>
<th>Date</th>
<th>Event</th>
</tr>
</thead>
<tbody data-date="2018-03-13">
<tr>
<td>2018-03-13</td>
<td>My event detail 1</td>
</tr>
<tr>
<td>2018-05-14</td>
<td>My event detail 2</td>
</tr>
</tbody>
<tbody data-date="2018-05-13">
<tr>
<td>2018-05-13</td>
<td>My event detail 3</td>
</tr>
</tbody>
<tbody data-date="2018-05-15">
<tr>
<td>2018-05-15</td>
<td>My event detail 4</td>
</tr>
<tr>
<td>2018-06-27</td>
<td>My event detail 5</td>
</tr>
</tbody>
<tbody data-date="2018-06-15">
<tr>
<td>2018-06-15</td>
<td>My event detail 6</td>
</tr>
</tbody>
</table>
</div>
I would change the processing function to add a .in-past class to every <tbody> which has date's in the past. Those date's then can be hidden or shown based on the class of the table. Here's a snippet:
$(document).ready(function() {
$("button").click(function() {
$('.event_display_table').toggleClass('future-only');
});
});
$(document).ready(function(e) {
$('.event_display_table tbody').each(function(index, element) {
var event_day = $(this).data('date');
event_day = new Date(event_day);
today = new Date();
if (event_day.getTime() < today.getTime()) {
$(this).addClass('in-past');
}
});
});
.event_display_table.future-only .in-past {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>This is a paragraph which can be hidden by clicking the button.</p>
<button>Toggle between slide up and slide down for a p element, but I want it to toggle 'show previous/hidden' dates</button>
<p>This table below shows only event dates in the future. Which is what I want. But I also want to have a toggle button option which does show the previous events. Is this possible?</p>
<div class="table-responsive">
<table class="event_display_table future-only" border="1">
<thead>
<tr>
<th>Date</th>
<th>Event</th>
</tr>
</thead>
<tbody data-date="2018-03-13">
<tr>
<td>2018-03-13</td>
<td>My event detail 1</td>
</tr>
<tr>
<td>2018-05-14</td>
<td>My event detail 2</td>
</tr>
</tbody>
<tbody data-date="2018-05-13">
<tr>
<td>2018-05-13</td>
<td>My event detail 3</td>
</tr>
</tbody>
<tbody data-date="2118-05-15">
<tr>
<td>2018-05-15</td>
<td>My event detail 4</td>
</tr>
<tr>
<td>2118-06-27</td>
<td>My event detail 5</td>
</tr>
</tbody>
<tbody data-date="2018-06-15">
<tr>
<td>2018-06-15</td>
<td>My event detail 6</td>
</tr>
</tbody>
</table>
</div>
I had to restructure your HTML table to make the appropriate js work:
$(document).ready(function(){
$("button").click(function(){
$("p").slideToggle();
$('.hidden').toggle()
});
});
$(document).ready(function(e) {
$('.event_display_table tbody tr td:first-child').each(function(index, element) {
var event_day = $(this).text();
event_day = new Date(event_day);
// I added this which will set the time of "event_day" to 00:00:00
event_day.setHours(0,0,0,0)
var today = new Date();
// I added this which will set the time of "today" to 00:00:00
today.setHours(0,0,0,0)
if(event_day < today) {
$(this).parent().hide()
$(this).parent().addClass('hidden');
}
});
});
Edited HTML:
<table class="event_display_table" border="1" >
<thead>
<tr>
<th>Date</th>
<th>Event</th>
</tr>
</thead>
<tbody>
<tr>
<td>2018-03-13</td>
<td>My event detail 1</td>
</tr>
<tr>
<td>2018-05-14</td>
<td>My event detail 2</td>
</tr>
<tr>
<td>2018-05-13</td>
<td>My event detail 3</td>
</tr>
<tr>
<td>2018-05-15</td>
<td>My event detail 4</td>
</tr>
<tr>
<td>2018-06-27</td>
<td>My event detail 5</td>
</tr>
<tr>
<td>2018-06-15</td>
<td>My event detail 6</td>
</tr>
</tbody>
</table>

How to build simple accordion using only specific table rows?

I have a HTML table listing various items. Every <tr> has a unique title element, but some items are related and those rows share the same title. I'm trying to write jQuery code that would parse this table and convert only those items with the same title into a simple accordion, then inject a new clickable row just above them that will use that shared title as a content, and toggle those related items on click.
This is an example table, before conversion where "Orange" items are related:
<table>
<tr title="Banana">
<td>Document 1</td>
</tr>
<tr title="Apple">
<td>Document 2</td>
</tr>
<tr title="Orange">
<td>Document 3</td>
</tr>
<tr title="Orange">
<td>Document 4</td>
</tr>
<tr title="Orange">
<td>Document 5</td>
</tr>
<tr title="Lemon">
<td>Document 6</td>
</tr>
<tr title="Cherry">
<td>Document 7</td>
</tr>
</table>
This is what I'm trying to convert this table to. Basically I'm hiding related items and I'm using a newly created row above them to show/hide:
<table>
<tr title="Banana">
<td>Document 1</td>
</tr>
<tr title="Apple">
<td>Document 2</td>
</tr>
<tr title="Orange">
<td>Orange (click to toggle)</td>
</tr>
<tr title="Orange" style="display:none;">
<td>Document 3</td>
</tr>
<tr title="Orange" style="display:none;">
<td>Document 4</td>
</tr>
<tr title="Orange" style="display:none;">
<td>Document 5</td>
</tr>
<tr title="Lemon">
<td>Document 6</td>
</tr>
<tr title="Cherry">
<td>Document 7</td>
</tr>
</table>
Note 1: The title can be anything. I'm just using "Orange" as an example here, but in reality the title can be even multiple words.
Note 2: Related items will be stacked one after another.
What would be the easiest way to accomplish this?
you can start from here
$(document).ready(function(){
var titles_arr = [] , create_list = [];
$('tr[title]').each(function(){
var title = $(this).attr('title').trim();
if($.inArray( title , titles_arr) > -1){
if($.inArray( title , create_list) <= -1){
create_list.push(title);
}
}else{
titles_arr.push(title);
}
});
//console.log(create_list[0]);
for(var i = 0 ; i < create_list.length ; i++){
var newtrTitle = create_list[i];
var html = '<tr title="'+newtrTitle+'" class="clcToggle"><td>'+newtrTitle+' (Click To Toggle)</td></tr>';
$(html).insertBefore('tr[title="'+newtrTitle+'"]:eq(0)').promise().done(function(){
$('tr[title="'+newtrTitle+'"]').not(':eq(0)').hide();
});
}
$('#MainTable').on('click', '.clcToggle',function(){
var title = $(this).attr('title').trim();
$('tr[title="'+title+'"]').not(':eq(0)').slideToggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="MainTable">
<tr title="Banana">
<td>Document 1</td>
</tr>
<tr title="Apple">
<td>Document 2</td>
</tr>
<tr title="Orange">
<td>Document 3</td>
</tr>
<tr title="Orange">
<td>Document 4</td>
</tr>
<tr title="Orange">
<td>Document 5</td>
</tr>
<tr title="Lemon">
<td>Document 6</td>
</tr>
<tr title="Lemon">
<td>Document 6</td>
</tr>
<tr title="Cherry">
<td>Document 7</td>
</tr>
<tr title="Cherry">
<td>Document 8</td>
</tr>
</table>
Note : this code will work just fine if you have the same tr title behind
each other .. need a little bit of work to arrange them if its
randomly arranged
If I can assume that all item of the same type is grouped together:
// Step 1: Code to add an extra row if there are multiple occurrences of an item
// This is not dynamic yet
if($("tr[title='Orange']").length>1){ // if there is more than one occurance
$("tr[title='Orange']").first().before('<tr title="Orange"><td>Orange (click to toggle)</td></tr>')
}
// Step 2: Loop through the items to make the above code dynamic
var ftypes = [];
$("table tr").each(function(idx)){
// get one of each type of fruit
if(ftypes.indexOf($(this).attr('title'))<0) ftypes.push($(this).attr('title'));
});
ftypes.forEach(function(fruit){
// if each fruit have more than one occurrence then add a row above
if($("tr[title='"+fruit+"']").length>1){
$("tr[title='"+fruit+"']").first().before('<tr title="'+fruit+'"><td>Orange (click to toggle)</td></tr>')
}
});
I added an extra row to double check that it was working correctly.
I hope the comments help enough.
function click_handler(e) {
var $this = $(this);
//I am too lazy to write $(this) over and over again
$('tr[title="' + $this.prop('title') + '"]').not($this).toggle();
//First Part: gets all TR elements with same title
//Next Part: but not this element
//Toggle their visibility;
}
$(document).ready(function() {
var prev; //Previous Element.
var prev_i = 0; //previous level.
$('tr').each(function(i, el) {
el = $(el);
if (prev != null) {
//So we are not at the first element.
if (prev.prop('title') == el.prop('title')) {
//The previous title and the current el title are the same.
if (prev_i == 0) {
prev.text(prev.text() + "(click to toggle)");
//If we are at previous level 0, meaning we have not encountered this portion of the loop before, we need to add the "click to toggle" text.
prev.on('click', click_handler.bind(prev));
//Add the click_handler, bound to the previous element (first).
}
el.hide(); //Hide any not-first element with the same title
++prev_i;
} else {
prev_i = 0;
//If the titles are different, we reset the previous level.
}
} //end prev!=null
prev = el;
//set previous element
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr title="Banana">
<td>Document 1</td>
</tr>
<tr title="Apple">
<td>Document 2</td>
</tr>
<tr title="Orange">
<td>Orange</td>
</tr>
<tr title="Orange">
<td>Document 3</td>
</tr>
<tr title="Orange">
<td>Document 4</td>
</tr>
<tr title="Orange">
<td>Document 5</td>
</tr>
<tr title="Lemon">
<td>Document 6</td>
</tr>
<tr title="Lemon">
<td>Document 7</td>
</tr>
<tr title="Cherry">
<td>Document 8</td>
</tr>
</table>

Break a HTML Table to two tables dynamically in javascript

I am using an user control in asp.net in which I am creating a html table from code behind. Initial structure is given below.
<table>
<tr>
<td>Property1</td>
<td>Property2</td>
<td>Property3</td>
<td>Property4</td>
</tr>
<tr>
<td>Value1</td>
<td>Value2</td>
<td>Value3</td>
<td>Value4</td>
</tr>
</table>
My problem is I want to restructure it according to the size of container it is going to be placed [as it is a user control]. For Ex If width of each cell is 20px and container width is 40px then structure will be as given below;
<table>
<tr>
<td>Property1</td>
<td>Property2</td>
</tr>
<tr>
<td>Value1</td>
<td>Value2</td>
</tr>
</table>
<table>
<tr>
<td>Property3</td>
<td>Property4</td>
</tr>
<tr>
<td>Value3</td>
<td>Value4</td>
</tr>
</table>
Thanks for the suggestion #user2167382.
Finally I have solved it though I had to use jquery.
Below is my html.
<table id="sourceT">
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td>Col 1 - value</td>
<td>Col 2 - value</td>
<td>Col 3 - value</td>
</tr>
</table>
<table id="targetT">
</table>
Here is the jquery logic.
var $target = $("#targetT");
$("#sourceT tr").each(function() {
var $tds = $(this).children(),
$row = $("<tr></tr>");
var i=1;
while(i<3)
{
alert(i) ; $row.append($tds.eq(i).clone()).appendTo($target);
i++;
}
});
Hope it will help somebody else.

Categories