Collapsible Table Rows with jQuery - javascript

i am looking for a way to make table rows collasible or expandable in multiple levels.
The default view should be collapsed (maybe its possible to show level 0 and 1?)
This is what i have right now, gathered from an answer on another question on here.
<table>
<tr class="header">
<th>A</th>
<th>B</th>
</tr>
<tr class="level_0" id="10">
<td>10</td>
<td> Level 0</td>
</tr>
<tr class="level_1 parent_10" id="11">
<td>10.1</td>
<td>Level 1</td>
</tr>
<tr class="level_1 parent_10" id="12">
<td >10.2</td>
<td>Level 1</td>
</tr>
<tr class="level_2 parent_12" id="13">
<td>10.2.1</td>
<td>Level 2</td>
</tr>
<tr class="level_0" id="20">
<td>20</td>
<td> Level 0</td>
</tr>
<tr class="level_1 parent_20" id="21">
<td>20.1</td>
<td>Level 1</td>
</tr>
</table>
JavaScript:
if (typeof jQuery === 'undefined') throw "jQuery Required";
jQuery(function ($) {
var treeTable = {
parentClassPrefix : '',
collapsedClass : 'collapsed',
init : function(parentClassPrefix) {
this.parentClassPrefix = parentClassPrefix;
$('table').on('click', 'tr', function () {
treeTable.toggleRowChildren($(this));
});
},
toggleRowChildren : function(parentRow) {
var childClass = this.parentClassPrefix+parentRow.attr('id');
var childrenRows = $('tr', parentRow.parent()).filter('.'+childClass);
childrenRows.toggle();
childrenRows.each(function(){
if (!$(this).hasClass(treeTable.collapsedClass)) {
treeTable.toggleRowChildren($(this));
}
});
parentRow.toggleClass(this.collapsedClass);
}
};
treeTable.init('parent_');
});

Something like this - this could be done better if I was going to take little more time looking at it. But this is just a quick example of how it could be done.
$(document).ready(function()
{
$(".level_0").each(function()
{
console.log("IN HERE");
$(this).nextUntil('tr.level_0').toggle();
});
});
$(document).on("click", '.level_0', function()
{
$(this).nextUntil('tr.level_0').toggle();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><table>
<tr class="header">
<th>A</th>
<th>B</th>
</tr>
<tr class="level_0" id="10">
<td>10</td>
<td> Level 0</td>
</tr>
<tr class="level_1 parent_10" id="11">
<td>10.1</td>
<td>Level 1</td>
</tr>
<tr class="level_1 parent_10" id="12">
<td >10.2</td>
<td>Level 1</td>
</tr>
<tr class="level_2 parent_12" id="13">
<td>10.2.1</td>
<td>Level 2</td>
</tr>
<tr class="level_0" id="20">
<td>20</td>
<td> Level 0</td>
</tr>
<tr class="level_1 parent_20" id="21">
<td>20.1</td>
<td>Level 1</td>
</tr>
</table>

Related

How to remove the contents of a <td> in a table dynamically using JS

I want when the user clicks the button with the class name "removeElement" the next "td" with class "forRemove" should have its contents removed.
How can I do it?
<table class="table">
<tbody class="list" id="list">
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
</tbody>
</table>
The solution to your problem is here.
$('.removeElement').click(function(e){
$(this).parent().next().remove();
});
Also, I create the fiddle for you.
checkout this
<table class="table">
<tbody class="list" id="list">
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
</tbody>
</table>
<script>
const buttons = document.getElementsByClassName('removeElement');
for(let i =0; i < buttons.length; ++i) {
buttons[i].onclick = function(event) {
const parent = event.currentTarget.parentNode.parentNode;
parent.children[parent.children.length - 1].innerHTML = "";
}
}
</script>
Is this more or less what you're looking for? To be honest I wouldn't access table elements like this, this is doing a bit much for such simple functionality, for exmaple, on the buttons I would add an indexer at the end of the class name or id, I would use IDs for in this case not only because is faster but also because you'd want quicker access to these elements without the hassle of having to go through so many nodes. i.e.:
<table class="table">
<tbody class="list" id="list">
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button id="removeElement-0" class="removeElement">removeNextTd</button>
</td>
<td class="forRemove-0">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button id="removeElement-1" class="removeElement">removeNextTd</button>
</td>
<td class="forRemove-1">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button id="removeElement-2" class="removeElement">removeNextTd</button>
</td>
<td class="forRemove-2">BODY 4</td>
</tr>
</tbody>
</table>
<script>
const buttons = document.getElementsByClassName('removeElement');
for(let i =0; i < buttons.length; ++i) {
buttons[i].onclick = function(event) {
document.getElementsByClassName("forRemove-"+event.currentTarget.id[event.currentTarget.id.length-1])[0].innerHTML = "";
}
}
</script>
But Whatever works for you mate.
This clears the td. It is still there so the table wouldn't brake, but it is emty.
$('.removeElement').on('click', function() {
$(this).closest('tr').find('.forRemove').empty();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table">
<tbody class="list" id="list">
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
</tbody>
</table>
This will remove the contents of the next TD, with the class forRemove
$(".removeElement").on("click", function() {
$(this).closest("tr").find(".forRemove").empty();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table class="table">
<tbody class="list" id="list">
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
<tr>
<td>BODY 1</td>
<td>BODY 2</td>
<td>
<button class="removeElement">removeNextTd</button>
</td>
<td class="forRemove">BODY 4</td>
</tr>
</tbody>
</table>
In short, when you click the button it will find the nearest tr element by traversing up the DOM tree, and then find the element within that with the class forRemove, and then empty() it.
not exactly what you asked but you can do it like this with jquery
$this .parent()
.parent()
.remove();
since the delete button is within the row you want to delete this should work

Grouping selectors by ID

I have this code
<table>
<tr id="groupItem-12863">
<td>10</td>
<td class="selCost">34</td>
</tr>
<tr id="groupItem-12863">
<td>20</td>
<td class="selCost">5</td>
</tr>
<tr>
<td>14</td>
<td class="selCost">23</td>
</tr>
<tr id="groupItem-73632">
<td>28</td>
<td class="selCost">8</td>
</tr>
<tr id="groupItem-73632">
<td>54</td>
<td class="selCost">55</td>
</tr>
<tr id="groupItem-73632">
<td>13</td>
<td class="selCost">99</td>
</tr>
<tr>
<td>18</td>
<td class="selCost">55</td>
</tr>
<tr>
<td>99</td>
<td class="selCost">66</td>
</tr>
</table>
I want a jquery selector where I can loop over the group of id and fetch the value of td cell. Note: the tr id are generated dynamically and can have none or N rows for each group of id. For example (groupItem-12863 has 2 and groupItem-73632 has 3 rows)
Even if the following solution should do what you are expecting, I strongly suggest you to change the id to another attribute, a custom one like group-id or use CSS classes. By the way:
$(function() {
var groupedRows = {};
$.each($('tr[id^=groupItem]'), function() {
if (groupedRows[$(this).attr('id')]) {
groupedRows[$(this).attr('id')].push($(this));
}
else {
groupedRows[$(this).attr('id')] = [$(this)];
}
});
console.log(groupedRows);
});
HTML must not have same ids, it will be better to use class for your use-case.
After this you can get the elements with a given class and loop over them like
$(function(){
$('.groupItem-73632').each(function() {
console.log('grp');
$(this).find('td').each(function(){
console.log($(this).text());
})
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr class="groupItem-12863">
<td>10</td>
<td class="selCost">34</td>
</tr>
<tr class="groupItem-12863">
<td>20</td>
<td class="selCost">5</td>
</tr>
<tr>
<td>14</td>
<td class="selCost">23</td>
</tr>
<tr class="groupItem-73632">
<td>28</td>
<td class="selCost">8</td>
</tr>
<tr class="groupItem-73632">
<td>54</td>
<td class="selCost">55</td>
</tr>
<tr class="groupItem-73632">
<td>13</td>
<td class="selCost">99</td>
</tr>
<tr>
<td>18</td>
<td class="selCost">55</td>
</tr>
<tr>
<td>99</td>
<td class="selCost">66</td>
</tr>
</table>
I had to do bit of hack, I know the html was not ideal, we can't have same id's. But sometimes refactoring the older code is out of scope. Here is the solution, not ideal but works :
$('tr[id^=groupItem-]').each(function(){
var s = 0 ;
var text = $(this).attr('id');
var arr = text.split('-');
var elem = 'tr[id^=groupItem-' + arr[1] + ']';
$(elem).find("td.selCost").each(function(){
s += parseFloat($(this).text());
});
console.log($(this).attr('id')+ '=='+s);
});
You can use the wildcard selector like tr[id^=groupItem]. Check Attribute Selector for more example.
$(function() {
$('tr[id^=groupItem]').css({
'background-color': 'red'
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr id="groupItem-12863">
<td>10</td>
<td class="selCost">34</td>
</tr>
<tr id="groupItem-12863">
<td>20</td>
<td class="selCost">5</td>
</tr>
<tr>
<td>14</td>
<td class="selCost">23</td>
</tr>
<tr id="groupItem-73632">
<td>28</td>
<td class="selCost">8</td>
</tr>
<tr id="groupItem-73632">
<td>54</td>
<td class="selCost">55</td>
</tr>
<tr id="groupItem-73632">
<td>13</td>
<td class="selCost">99</td>
</tr>
<tr>
<td>18</td>
<td class="selCost">55</td>
</tr>
<tr>
<td>99</td>
<td class="selCost">66</td>
</tr>
</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>

Using JQuery to select all <tr> elements with a specific class

The basic structure is a table with a few rows. I would like the top half of the table showing and the bottom half collapsed until a user clicks on the READ MORE cell. I have that functionality working but I can't get the JQuery right that selects all of the ".collapseThis" rows and hides them on page load.
Here is the table
<div id="tables">
<table class="expandableTable">
<tr>
<td></td>
<td></td>
</tr>
<tr class="accordion">
<td colspan="2">READ MORE</td>
</tr>
<tr class="collapseThis">
<td></td>
<td></td>
</tr>
</table>
<table class="expandableTable">
<tr>
<td></td>
<td></td>
</tr>
<tr class="accordion">
<td colspan="2">READ MORE</td>
</tr>
<tr class="collapseThis">
<td></td>
<td></td>
</tr>
</table>
</div>
Here is the JQuery.
$(document).ready(function () {
function getCollapsable($row) {
var children = [];
while ($row.next().hasClass('collapseThis')) {
children.push($row.next());
$row = $row.next();
}
return children;
}
function hideTableRows($row) {
children = getCollapsable($row);
$.each(children, function () {
$(this).toggle();
});
}
$('#tables').each($('expandableTable'), function () {
hideTableRows($(this).children().hasClass('.accordion'));
});
You can just use css to set the display value, there is no need to use jQuery to set the initial state.
If you want to use jQuery, use a simple selector like $('#tables .expandableTable .collapseThis').hide().
$(document).ready(function() {
//if you don't want to use css
//$('#tables .expandableTable .collapseThis').hide();
});
#tables .expandableTable .collapseThis {
display: none;
}
<div id="tables">
<table class="expandableTable">
<tr>
<td>1.1</td>
<td>1.2</td>
</tr>
<tr class="accordion">
<td colspan="2">READ MORE</td>
</tr>
<tr class="collapseThis">
<td>2.1</td>
<td>2.2</td>
</tr>
</table>
<table class="expandableTable">
<tr>
<td>1.1</td>
<td>1.2</td>
</tr>
<tr class="accordion">
<td colspan="2">READ MORE</td>
</tr>
<tr class="collapseThis">
<td>2.1</td>
<td>2.2</td>
</tr>
</table>
<table class="expandableTable">
<tr>
<td>1.1</td>
<td>1.2</td>
</tr>
<tr class="accordion">
<td colspan="2">READ MORE</td>
</tr>
<tr class="collapseThis">
<td>2.1</td>
<td>2.2</td>
</tr>
</table>
</div>

Turn HTML table into array with javascript

Hi I have the following table:
<tbody>
<tr class="odd first-child">
<td class="first-child account_name">Label 1</td>
<td class="last-child balance">Amount 1</td>
</tr>
<tr class="even">
<td class="first-child account_name">Label 2</td>
<td class="last-child balance">Amount 2</td>
</tr>
<tr class="odd">
<td class="first-child account_name">Label 3</td>
<td class="last-child balance">Amount 3</td>
</tr>
<tr class="even">
<td class="first-child account_name">Label 4</td>
<td class="last-child balance">Amount 4</td>
</tr>
<tr class="odd">
<td class="first-child account_name">Label 5</td>
<td class="last-child balance">Amount 5</td>
</tr>
</tbody>
I need to turn it into an array using javascript, so that "Label 1" is the key for value "Amount 1," "Label 2" is the key for value "Amount 2," etc.
How might I go about this?
Thanks!
All you need to do is simply loop over the nodes.
var c = document.querySelectorAll('#tgt td');
var d = {};
for (var i = 0; i < c.length; i += 2) {
d[c[i].innerHTML] = c[i + 1].innerHTML;
}
console.log(d);
<table id="tgt">
<tbody>
<tr class="odd first-child">
<td class="first-child account_name">Label 1</td>
<td class="last-child balance">Amount 1</td>
</tr>
<tr class="even">
<td class="first-child account_name">Label 2</td>
<td class="last-child balance">Amount 2</td>
</tr>
<tr class="odd">
<td class="first-child account_name">Label 3</td>
<td class="last-child balance">Amount 3</td>
</tr>
<tr class="even">
<td class="first-child account_name">Label 4</td>
<td class="last-child balance">Amount 4</td>
</tr>
<tr class="odd">
<td class="first-child account_name">Label 5</td>
<td class="last-child balance">Amount 5</td>
</tr>
</tbody>
</table>
You could try simulate key/value pairs using following javascript:
var trElements = document.getElementsByTagName('tr');
var keyValue = [];
for(var i = 0; i < trElements.length; i++)
{
var tdElements = trElements[i].getElementsByTagName('td');
keyValue.push({"key":tdElements[0].innerHTML,"value":tdElements[1].innerHTML});
}
With following html:
<body>
<table>
<tr>
<td>Key1</td>
<td>Value1</td>
</tr>
<tr>
<td>Key2</td>
<td>Value2</td>
</tr>
</table>
</body>
That way you can get the value of your key or value by either choosing keyValue[i].key or keyValue[i].value. I think it's the simplest way. I don't think js supports key-value pairs by default.

Categories