How can I post-process my html to collapse large tables? - javascript

I have a placeholder on my webpage that is filled with a html table of matches/mismatches between files. The problem is that there's a lot of data that fills the page which may be a lot to scroll through and I'd like to make each table collapsible.
Perhaps post processing on the html table to collapse all rows under a header and just have a "+" button or something to show the rows if the user wants.
I'd like to avoid adding non-native css dependencies if possible.
<br>
<label style="font-weight:bold;color:#c9d3e0"> Header1 </label>
<table>
<tbody>
<tr class="Header Row">
<td> (bunch of td cells for length of columns) </td>
</tr>
<tr class="Matched Row">
<td> (bunch of td cells for length of columns) </td>
</tr>
<tr class="Mismatched Row">
<td> (bunch of td cells for length of columns) </td>
</tr>
< /tbody>
</table>
<br>
(repeat for dynamic N tables)

I've adapted and tried to enhance my answer from this other question (Need of Recursive html table tree using only Javascript) to make it work with your example of data:
function nextTr(row) {
while ((row = row.nextSibling) && row.nodeType != 1);
return row;
}
document.getElementById("myTable").addEventListener("click", function(e) {
if (!e) return;
if (e.target.tagName !== "A") return;
e.preventDefault();
var row = e.target.closest("tr");
var cls = row.classList[0];
var lvl = +(cls.slice(-1)) + 1;
cls = cls.slice(0, -1) + lvl;
while ((row = nextTr(row)) && (row.className.includes(cls) || row.className.includes("open")))
row.classList.toggle("open");
});
// Select all the tr childs elements (all levels except 0)
var allChilds = document.querySelectorAll("tr:not([class*=level0])");
document.getElementById("openAll").addEventListener("click", function() {
allChilds.forEach(function(elm){
elm.classList.add("open");
});
});
document.getElementById("closeAll").addEventListener("click", function() {
allChilds.forEach(function(elm){
elm.classList.remove("open");
});
});
tbody tr {
display: none;
}
th, td {
padding: 4px 12px;
}
tr:first-of-type,
tr[class*="level0"],
tr.open {
display: table-row;
}
tr[class*="level0"] th { background: #ccc; }
tr[class*="level1"] td { background: #ddd; }
tr[class*="level2"] td { background: #eee; }
<button id="openAll">+ All</button>
<button id="closeAll">- All</button>
<br>
<label style="font-weight:bold;color:#c9d3e0"> Header1 </label>
<table id="myTable">
<tbody>
<tr class="level0">
<th><a>+/-</a></th><th>Header 1</th>
</tr>
<tr class="level1">
<td></td><td>Match 1</td>
</tr>
<tr class="level1">
<td></td><td>Match 2</td>
</tr>
<tr class="level0">
<th><a>+/-</a></th><th>Header 2</th>
</tr>
<tr class="level1">
<td></td><td>Match 1</td>
</tr>
<tr class="level1">
<td><a>+/-</a></td><td>Match 2</td>
</tr>
<tr class="level2">
<td></td><td>Match 2.1</td>
</tr>
<tr class="level0">
<th><a>+/-</a></th><th>Header 3</th>
</tr>
<tr class="level1">
<td></td><td>Match 1</td>
</tr>
<tr class="level1">
<td></td><td>Match 2</td>
</tr>
</tbody>
</table>
<br>
Using this code, you can have several levels.
Feel free to comment if anything.
I hope it helps in any way.

Related

How to select only an element in the last row of a table using CSS selectors

I have a table with many expandable/collapsible rows that are hidden by labels until clicked (shown below). Input in the right-hand column of the table is meant to be edited and summed, so I have a function to set each cell to be .contentEditable and calculate the sum total of the user-entered numbers. However, because I do not want the sum itself (in the final row of the table) to be edited, I have the following restriction to the event handler in jQuery:
document.querySelectorAll("table tr:not(:last-child) td ~ td");
While the :not(:last-child) part gets rid of the issue of the editable final row, it creates another issue: now, the last rows of every expandable section (the "Jill" and "Jim" rows in the below example) are unable to be edited, too. Is there a way to fix this without making the absolute final row of the table ("Total") editable in the process?
To better visualize the issue, I've attached an example table that demonstrates the problem.
$(document).ready(function() {
$('.hide').hide();
$('[data-toggle="toggle"]').change(function() {
$("label[for='" + this.id + "'] span").toggle();
$(this).parents().next('.hide').toggle();
});
});
function editcell() {
let cellnum = document.querySelectorAll("table tr:not(:last-child) td ~ td");
cellnum.forEach(td => {
td.setAttribute("contentEditable", true);
td.addEventListener("change", getsum());
});
}
function getsum() {
let sum = 0;
let cellnum = document.querySelectorAll("table tr td ~ td");
let count = cellnum.length - 1;
for (i = 0; i < count; i++) {
sum += parseInt(cellnum[i].innerHTML) || 0;
}
cellnum[count].innerHTML = sum;
}
editcell();
table {
width: 100%;
}
table,
tr,
th,
td {
border: 1px solid black;
border-collapse: collapse;
font-family: Arial;
}
[data-toggle="toggle"] {
display: none;
}
label {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="table">
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody class="section">
<tr>
<td colspan="2">
<label class="label" for="class1">
<b>Class 1</b>
<span>(Click this!)</span>
</label>
<input type="checkbox" name="class1" id="class1" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Jack</td>
<td id="jack" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>John</td>
<td id="john" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jill</td>
<td id="jill" oninput="editcell()">
<span>0</span>
</td>
</tr>
</tbody>
<tbody class="section">
<tr>
<td colspan="2">
<label class="label" for="class2">
<b>Class 2</b>
<span>(Click this!)</span>
</label>
<input type="checkbox" name="class2" id="class2" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Joe</td>
<td id="joe" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jane</td>
<td id="jane" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jim</td>
<td id="jim" oninput="editcell()">
<span>0</span>
</td>
</tr>
</tbody>
<tr>
<td style="font-weight:bold; text-align: center">Total</td>
<td id="total"></td>
</tr>
</table>
You can select (to ignore) the total cell as it has a unique id:
let cellnum = document.querySelectorAll("table tr td ~ td:not(#total)");
$(document).ready(function() {
$('.hide').hide();
$('[data-toggle="toggle"]').change(function() {
$("label[for='" + this.id + "'] span").toggle();
$(this).parents().next('.hide').toggle();
});
});
function editcell() {
let cellnum = document.querySelectorAll("table tr td ~ td:not(#total)");
cellnum.forEach(td => {
td.setAttribute("contentEditable", true);
td.addEventListener("change", getsum());
});
}
function getsum() {
let sum = 0;
let cellnum = document.querySelectorAll("table tr td ~ td");
let count = cellnum.length - 1;
for (i = 0; i < count; i++) {
sum += parseInt(cellnum[i].innerHTML) || 0;
}
cellnum[count].innerHTML = sum;
}
editcell();
table {
width: 100%;
}
table,
tr,
th,
td {
border: 1px solid black;
border-collapse: collapse;
font-family: Arial;
}
[data-toggle="toggle"] {
display: none;
}
label {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="table">
<thead>
<tr>
<th>Name</th>
<th>Number</th>
</tr>
</thead>
<tbody class="section">
<tr>
<td colspan="2">
<label class="label" for="class1">
<b>Class 1</b>
<span>(Click this!)</span>
</label>
<input type="checkbox" name="class1" id="class1" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Jack</td>
<td id="jack" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>John</td>
<td id="john" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jill</td>
<td id="jill" oninput="editcell()">
<span>0</span>
</td>
</tr>
</tbody>
<tbody class="section">
<tr>
<td colspan="2">
<label class="label" for="class2">
<b>Class 2</b>
<span>(Click this!)</span>
</label>
<input type="checkbox" name="class2" id="class2" data-toggle="toggle">
</td>
</tr>
</tbody>
<tbody class="hide">
<tr>
<td>Joe</td>
<td id="joe" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jane</td>
<td id="jane" oninput="editcell()">
<span>0</span>
</td>
</tr>
<tr>
<td>Jim</td>
<td id="jim" oninput="editcell()">
<span>0</span>
</td>
</tr>
</tbody>
<tr>
<td style="font-weight:bold; text-align: center">Total</td>
<td id="total"></td>
</tr>
</table>

retrieving just one value from the DOM with 100 datas listed through JS

I am trying to get just one ID when I click the Delete button, but I am receiving the whole list of it. Here is how I tried with jQuery. (solutions in both js and jquery is welcome)
the DOM :-
<tbody>
<tr class="tablerow">
<td class="delete"> </td>
<td class="id"></td>
</tr>
</tbody>
appending the data to the DOM (100 data is being fetched):-
let apiGetter = JSON.parse(localStorage.getItem('data'));
apiGetter.map((item,index) => {
del.append('<li> DELETE </li>');
id.append(`<li> ${item.id}</li>`);
}
My Solution that is listing all the ids that are in the DOM. (i want the id of the button that's clicked on)
$('.delete').click(function() {
let id = $(this).next('.id').text();
console.log(id); // 0 1 2 3 4 5 6 ... 98 99 100
})
Follow this code
HTML
<table>
<thead>
<tr>
<th>ID</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td class="delete" data-id="1">Delete</td>
</tr>
<tr>
<td>2</td>
<td class="delete" data-id="2">Delete</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>3</td>
<td class="delete" data-id="3">Delete</td>
</tr>
</tfoot>
</table>
Jquery
$(document).on('click','.delete',function(){
alert($(this).data('id'));
})
CSS
thead {color:green;}
tbody {color:blue;}
tfoot {color:red;}
table, th, td {
border: 1px solid black;
}
It very easy and it will work.

Datatables. Hide / Show multiple rows within loop

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()

Can't display a third column in my HTML table (drilldown functionality jQuery / JavaScript ) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm working on this HTML Table with drilldown functionality, it works for one column but I cant display a third column, I want to click on subcategory column and display another column but Im stuck in this part .
This is my JS code
function toggleVisibilitySubcategory() {
$('table.drillDown th:nth-child(2)').toggle();
$('table.drillDown tbody td:nth-child(2)').toggle();
}
function toggleVisibilityItem() {
$('table.drillDown th:nth-child(3)').toggle();
$('table.drillDown tbody td:nth-child(3)').toggle();
}
$(function () {
toggleVisibilitySubcategory();
toggleVisibilityItem();
$('table.drillDown').each(function() {
var $table = $(this);
$table.find('.parent').click(function() {
console.log( "click on parent" );
$(this).nextUntil('.parent').toggle();
if ($(this).find("td:hidden").length > 0) {
toggleVisibilitySubcategory();
}
});
var $childRows = $table.find('tbody tr').not('.parent').hide();
$table.find('button.hide').click(function() {
$childRows.hide();
});
$table.find('button.show').click(function() {
$childRows.filter('.child').show();
});
$table.find('tr.child').click(function(){
$(this).nextUntil('.child').show()
});
});
});
This is the HTML table.
<table class="drillDown" border="1" align="center">
<thead>
<th>Category</th>
<th>Subcategory</th>
<th>Item</th>
<th>LW</th>
</thead>
<tbody>
<!--PARENT ROW-->
<tr class="parent">
<td>Category 1</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="child" >
<td> </td>
<td>Subcategory_1 A
<td>a</td>
<td></td>
</tr>
<tr class="child">
<td> </td>
<td>Subcategory_1 B</td>
<td>a</td>
<td></td>
</tr>
</tbody>
</table>
My solution is to create a new toggle function to hide/show last table column and use it likes in the followig snippet:
function toggleVisibilitySubcategory() {
$('table.drillDown th:nth-child(2)').toggle();
$('table.drillDown tbody td:nth-child(2)').toggle();
}
$(function () {
toggleVisibilitySubcategory();
$('table.drillDown').each(function() {
var $table = $(this);
$table.find('.parent').click(function() {
console.log( "*****Click on Parent" );
$(this).nextUntil('.parent').toggle();
if ($(this).find("td:hidden").length == 1) {
toggleVisibilitySubcategory();
}
});
var $childRows = $table.find('tbody tr').not('.parent').hide();
$table.find('button.hide').click(function() {
$childRows.hide();
});
$table.find('button.show').click(function() {
$childRows.filter('.child').show();
});
$table.find('tr.child').click(function(){
$(this).nextUntil('.child').show()
});
});
});
<script src="http://code.jquery.com/jquery-1.11.3.js"></script>
<table class="drillDown" border="1" align="center">
<colgroup>
<thead>
<tr>
<th>Category</th>
<th>Subcategory</th>
</tr>
</thead>
</colgroup>
<tbody>
<!--PARENT ROW-->
<tr class="parent">
<td>Category 1</td>
<td colspan="2">$12,345.45</td>
</tr>
<tr class="child" >
<td> </td>
<td colspan="2">Subcategory_1 A</td>
</tr>
<tr class="child">
<td> </td>
<td colspan="2">Subcategory_1 B</td>
</tr>
<!--PARENT ROW-->
<tr class="parent">
<td>Category 2</td>
<td colspan="2">$135,754.99</td>
</tr>
<tr class="child" >
<td> </td>
<td colspan="2">Subcategory_2 A</td>
</tr>
<tr class="child">
<td> </td>
<td colspan="2">Subcategory_2 B</td>
</tr>
<tr class="child">
<td> </td>
<td colspan="2">Subcategory_2 C</td>
</tr>
<tr class="parent">
<td>Catgegory 3</td>
<td colspan="2">$232,563.46</td>
</tr>
<tr class="child" >
<td> </td>
<td colspan="2">Subcategory_3 A</td>
</tr>
<tr class="child">
<td> </td>
<td colspan="2">Subcategory_3 B</td>
</tr>
<tr class="child">
<td> </td>
<td colspan="2">Subcategory_3 C</td>
</tr>
</tbody>
</table>
You mean like this: https://jsfiddle.net/az0w2c8a/
$('.drillDown tr td:last-child, .drillDown tr th:last-child').hide();
$('.drillDown tr td:first-child, .drillDown tr th:first-child').click(function(){
$('.drillDown tr td:last-child, .drillDown tr th:last-child').show();
})
Alternatively if you need to add additional columns to your table you can use either ':nth-child()'
$('.drillDown tr td:nth-child(2), .drillDown tr th:nth-child(2)').hide();
$('.drillDown tr td:first-child, .drillDown tr th:first-child').click(function(){
$('.drillDown tr td:nth-child(2), .drillDown tr th:nth-child(2)').show();
})
if you need to hide all columns except the first one you can use the '.not()' method.
$('.drillDown tr td, .drillDown tr th').not(':first-child').hide();
$('.drillDown tr td:first-child, .drillDown tr th:first-child').click(function(){
$('.drillDown tr td, .drillDown tr th').not(':first-child').show();
})

how to replace values in innerhtml using javascript

I'm dynamically(on click of radio button) copying the inner html of one row(in a table) to other but after removing some elements.
var a = document.getElementById('copyrow' + e).innerHTML;
a = a.replace('<input type="radio" name="selectradio" id="shareredio"+e "" a="" href="#" onclick="setText("+e");">',"")
.replace('<div class="custom-radio" style="margin-top:50px;">',"")
.replace('<label for="shareredio"+e""></label>',"")
.replace('<input type="checkbox" name="sharecheck" id="sharecheck"+e"">',"")
.replace('<label for="sharecheck"+e""></label>',"")
.replace('Share fare',"");
document.getElementById('copyDiv').innerHTML = a;
I don't know enough about javascript but if there is any better way then please help...
This is probably not the best way to do it but I thought I'd at least give you an answer quickly. This is using jQuery to manipulate the DOM.
var $a = $('#copyrow'+e).clone();
$a.find('input#shareredio'+e).remove();
$a.find('div.custom-radio').remove();
$a.find('label[for="shareredio'+e+'"]').remove();
$a.find('input#sharecheck'+e).remove();
$a.find('label[for="sharecheck'+e+'"]').remove();
var result = $a.html().replace('Share fare', "");
$('#copyDiv').html(result);
something like this?
$(document).ready(function() {
$('.copy').on('change', function(){
if($('.copy:checked').val() == 'yes'){
$('.table2').find('.rowContents1').html($('.table1').find('.rowContents1').html());
$('.table2').find('.rowContents2').html($('.table1').find('.rowContents2').html());
$('.table2').find('.rowContents3').html($('.table1').find('.rowContents3').html());
}
else {
$('.table2').find('.rowContents1').html('');
$('.table2').find('.rowContents2').html('');
$('.table2').find('.rowContents3').html('');
}
});
});
table {
border: 1px solid gray;
}
td, th {
border: 1px solid gray;
padding: 5px;
text-align: center;
}
div{
display: inline-block;
margin-right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Copy Contents Over?<br/>
<input type="radio" class="copy" value="yes" name="copyRadio"/> Yes
<input type="radio" class="copy" value="no" name="copyRadio"/> No
<br/><br/>
<div>
Table1
<table class="table1">
<header>
<tr>
<th>Row Number</th>
<th>Row Contents</th>
</tr>
</header>
<body>
<tr>
<td class="rowNum1">1</td>
<td class="rowContents1">This is the Contents of Row 1</td>
</tr>
<tr>
<td class="rowNum2">2</td>
<td class="rowContents2">This is the Contents of Row 2</td>
</tr>
<tr>
<td class="rowNum3">3</td>
<td class="rowContents3">This is the Contents of Row 3</td>
</tr>
</body>
</table>
</div>
<div>
Table2
<table class="table2">
<header>
<tr>
<th>Row Number</th>
<th>Row Contents</th>
</tr>
</header>
<body>
<tr>
<td class="rowNum1">1</td>
<td class="rowContents1"></td>
</tr>
<tr>
<td class="rowNum2">2</td>
<td class="rowContents2"></td>
</tr>
<tr>
<td class="rowNum3">3</td>
<td class="rowContents3"></td>
</tr>
</body>
</table>
</div>
i used .html() to rewrite the contents inside each td. .find() function just traverses down the element tree and finds the descendants $('.table2').find('.rowContents1') is saying in element of class .table2, find the descendants with class .rowContents1.
hope this helps and is what you're looking for

Categories