Collapsing and expanding multiple nested rows in jQuery - javascript

I need a little help with collapsing and expanding nested rows. Currently my code below expands and collapses as desired at the first level but the subsequent levels also show.
$(document).ready(function(e) {
$('.collapseTitle').click(function() {
$(this).parent()
.parent()
.next('tbody')
.toggleClass('collapsed');
});
});
.collapsed {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<td class="collapseTitle">Title</td>
</tr>
</thead>
<tbody class="collapsed">
<tr>
<td>Level 1</td>
</tr>
<tr>
<td>Level 2</td>
</tr>
</tbody>
</table>
I am trying to achieve that Level 1 expands when "collapseTitle" is clicked and that only when "collapseAccount" is clicked, does Level 2 expand. Now I know that my code should look something like the below, but I am struggling...
<table>
<thead>
<tr>
<td class="collapseTitle">Title</td>
</tr>
</thead>
<tbody>
<tr class="collapsed account">
<td class="collapseAccount">Level 1</td>
</tr>
<tr class="collapsed level">
<td>Level 2</td>
</tr>
</tbody>
</table>
<script>
$(document).ready(function(e) {
$('.collapseTitle').click(function() {
$(this).parent().parent().next('tbody tr').toggleClass('account');
});
});
$(document).ready(function(e) {
$('.collapseAccount').click(function() {
$(this).next('tr').toggleClass('level');
});
});
</script>
Any help would be greatly appreciated.

The following code should do it. I hope it helps with what you want to achieve:
collapses/expands columns when clicking on the header/title of the column and
collapses/expands all rows following the account row when clicking on it (until the next account row)
The only thing you need to do is add the class account to the higher level rows. You can do this pretty easily when you're displaying these with a loop.
.collapsed {
/* using visibility, since display causes layout issues */
/* due to empty rows rows/columns collapsing */
visibility: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<td>Title1</td>
<td>Title2</td>
</tr>
</thead>
<tbody>
<tr class="account">
<td class="collapsed">Account Summary - Account A</td>
<td class="collapsed">Account Summary - Account A</td>
</tr>
<tr class="collapsed">
<td class="collapsed">Account Details Part I - Account A</td>
<td class="collapsed">Account Details Part I - Account A</td>
</tr>
<tr class="collapsed">
<td class="collapsed">Account Details Part II - Account A</td>
<td class="collapsed">Account Details Part II - Account A</td>
</tr>
<tr class="account">
<td class="collapsed">Account Summary - Account B</td>
<td class="collapsed">Account Summary - Account B</td>
</tr>
<tr class="collapsed">
<td class="collapsed">Account Details Part I - Account B</td>
<td class="collapsed">Account Details Part I - Account B</td>
</tr>
</tbody>
</table>
<script>
$(document).ready(() => {
/* the following handlers expand/collapse the columns */
$('thead > tr > td').each((i, el) => {
$(el).click(() => {
const colIndex = $(el).index() + 1;
const nRows = $('tbody > tr').length;
for (let j = 0; j < nRows; j += 1) {
let cellSelector = `tbody > tr:nth-child(${j+1})`
cellSelector += `> td:nth-child(${colIndex})`;
$(cellSelector).toggleClass('collapsed');
}
})
})
/* the following handlers expand/collapse the account-details rows */
$('tbody > tr.account').each((i, el) => {
$(el).click(() => {
$(el).nextUntil('.account').each((j, ele) => {
$(ele).toggleClass('collapsed');
})
})
})
});
</script>

Your jQuery selectors are just a bit off for the toggleClass. You can use the class names of the row to toggle. Also you should create a class to be toggled that displays the row/hides it. For example:
edit:
I now created the titles and rows dynamically to give you a better idea of how this can be done using data- attributes.
You will have a title td and a row td that match on a data- attribute, so when you click a title and corresponding tr will be shown. So for example if you click title 1 (with a data-index=1) then the tr with the attribute data-rowindex=1 will be shown.
$(document).ready(function(e) {
createTable();
$('.collapseTitle').click(function() {
// grab the data-index value from the clicked title
let index = $(this).attr("data-index");
// find a tr that has the attribute data-rowindex and it matches index
$("tr[data-rowindex='" + index + "']").toggleClass("collapsed");
});
});
function createTable(){
// create the titles and the level rows
for(let i = 0; i < 3; i++){
// create the title row
let $trTitle = $('<tr>');
let $tdTitle = $('<td>', {class: "collapseTitle", text: "Title " + i});
$tdTitle.attr("data-index", i);
let $finalTitleRow = $trTitle.append( $tdTitle );
// create the level row
let $trLevel = $('<tr>', {class: "collapsed account"} );
$trLevel.attr("data-rowindex", i);
let $tdLevel = $('<td>', {text: "Level " + i});
let $finalLevelRow = $trLevel.append( $tdLevel );
// add the title and level row pairs to the head and body
$("#myTableHead").append($finalTitleRow[0]);
$("#myTableBody").append($finalLevelRow[0]);
}
}
.collapsed {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead id="myTableHead">
</thead>
<tbody id="myTableBody">
</tbody>
</table>

I managed to find the answer I was looking for... Thank you to "stackoverfloweth",
Answer To My Question
The snippet of code which has helped me thus far is below,
$('.parent-row').click(function(){
var $element = $(this).next();
while(!$element.hasClass('parent-row')){
$element.toggle();
if($element.next().length >0){
$element = $element.next();
}
else{
return;
}
} });
$('.child-row.has-children').click(function(){
var $element = $(this).next();
while($element.hasClass('child-child-row')){
$element.toggle();
if($element.next().length >0){
$element = $element.next();
}
else{
return;
}
} });

Related

How to replace <td> values in a table with jQuery (in every Rows)? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 months ago.
Improve this question
I have a table with multiple columns, one column named: ‘Type’. The values in Type column could be: 1 or 2.
I want to replace the value “1” to “Information” and the value “2” to “Problem” in every row with jQuery, how can I do that?
Here in this demo you'll find a function transformTableData() that takes the table existing in the document and will:
find where is located the field having as header the string "Type";
loop through all its rows and change the value of the corresponding field as the result coming out of the map defined on top. So according to the default map I defined, if the field value is '1' it will be transformed to 'Information' and if the value is '2' it will be transformed to 'Problem';
If there's no corresponding value in the map, the value will be untouched;
The function runs when you click the button on the bottom of the page. Of course the same function could be called on document ready.
function transformTableData(){
const map = {
'1' : 'Information',
'2' : 'Problem',
}
const typeHeaderCell = $('table thead tr th:contains(Type)');
const typeHeaderIndex = $(typeHeaderCell).index();
$('table tbody tr').each((i, row)=>{
const rowCell = $(row).find(`td:nth-child(${typeHeaderIndex+1})`);
const value = rowCell.text();
rowCell.text( map?.[value] );
});
}
table, tr, th, td{
border: solid 1px black;
padding: 1rem;
}
button{
margin-top: 1rem;
padding: 1rem;
font-size: 1.25rem;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
<th>...</th>
<th>Type</th>
<th>...</th>
<th>ColumnN</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td>1</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>2</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>INVALID</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button onclick="transformTableData();">Transform Table Data</button>
There are many ways to achieve something like this. Here is one example. It first looks for the index by comparing the text of each cell in the table header. then it gets all cells in the table body with the index in each table row and replaces the content if it is "1" or "2". There are for sure even shorter or faster methods.
// Find index of column with "Type"
let index = -1;
let th = $('#myTable thead tr th');
for (let i=0; i<th.length; i++) {
if ($(th[i]).text() == 'Type') {
index = i;
break;
}
}
// If index is greater then -1 we found the column
if (index > -1) {
// Get all the table cells in each row at the specific index (need to add +1 to the index)
let td = $('#myTable tbody tr td:nth-child(' + (index+1) + ')');
for (let i=0; i<td.length; i++) {
// Compare content and replace it
if ($(td[i]).text() == '1') {
$(td[i]).text('Information');
}
else if ($(td[i]).text() == '2') {
$(td[i]).text('Problem');
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="myTable">
<thead>
<tr>
<th>ID</th>
<th>Type</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
<td>John</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>Maria</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
<td>Walter</td>
</tr>
<tr>
<td>3</td>
<td>1</td>
<td>Julia</td>
</tr>
</tbody>
</table>

jquery - show / hide elements rows by elemenst in data-id array

I have table with rows like that:
<tr class="listRow" data-id="[11,0]">...</tr>
<tr class="listRow" data-id="[1,2,3]">...</tr>
How i can using JQuery filter specific rows with element in array? For example by button click show all rows with 1 in array and hide rest.
Edit - my sample code so far:
i don't know how to filtering elements in data-id array.
$(document).on('click','#filterList',function()
{
var element = $(this).data("id");
// how to filter elements in rows
}
);
If i understand correctly:
$('#check').click(function() {
$('.listRow').each(function() {
if($.inArray(1, $(this).data().id)>-1) {
$(this).show();
}
else {
$(this).hide()
}
});
});
$('#check').click(function() {
$('.listRow').each(function() {
if($.inArray(1, $(this).data().id)>-1) {
$(this).show();
}
else {
$(this).hide()
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="tg">
<thead>
<tr class="listRow" data-id="[1,0]">
<th class="tg-0pky">Here is 1</th>
<th class="tg-0pky">xxxx</th>
<th class="tg-0pky"></th>
<th class="tg-0pky"></th>
</tr>
</thead>
<tbody>
<tr class="listRow" data-id="[11,0]">
<td class="tg-0pky">Not 1</td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
</tr>
<tr class="listRow" data-id="[15,0]" >
<td class="tg-0pky">Not 1</td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
</tr>
<tr class="listRow" data-id="[1,0,3]">
<td class="tg-0pky" >Here is 1</td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
<td class="tg-0pky"></td>
</tr>
</tbody>
</table>
<button id="check">
Click
</button>
when you press the button, loop through all the elements that have a data-id
parse the data-id as json, which will give you an array
if the array includes the id you're looking for, set the class to hide or show (where they have the display css assigned accordingly)
here's what that might look like without jquery and using style and opacity. Usually it's done using class but this is for demonstration purposes, changing to use classes should be straight forward.
function findElsById(id){
var matches = []
document.querySelectorAll('[data-id]').forEach(function(el){
try{
var arr = JSON.parse(el.dataset.id)
if (arr.includes(id)) matches.push(el)
} catch (e){
// prolly not valid json
}
})
return matches
}
function show(id){
var els = findElsById(id);
console.log('show', id, '\nshowing: ', els)
if (els) {
els.forEach(function(el){
el.style = 'opacity:1'
})
}
}
function hide(id){
var els = findElsById(id);
console.log('hide', id, '\nhiding: ', els)
if (els) {
els.forEach(function(el){
el.style = 'opacity:0.1'
})
}
}
<table>
<tr class="listRow" data-id="[1,0]"><td>1, 0</td></tr>
<tr class="listRow" data-id="[1,2]"><td>1, 2</td></tr>
</table>
<button onclick="hide(0)">-0</button>
<button onclick="hide(1)">-1</button>
<button onclick="hide(2)">-2</button>
<button onclick="show(0)">+0</button>
<button onclick="show(1)">+1</button>
<button onclick="show(2)">+2</button>

How to loop through a table and get the td elements to follow a condition

I just want make so it the tr hides when the td does not follow the requirements, tried with jQuery and JavaScript, don't know what's wrong.
$(document).ready(function(){
$("td").each(function() {
var id = $(this).attr("price_search");
if (id > value4 && id < value5) {
$(this).hide;
}
else {
$(this).hide;
}
});
});
You can do this.
Hope this will help you.
$(document).ready(function() {
var value4 = 2;
var value5 = 4;
$("td").each(function() {
var id = $(this).attr("price_search");
if (id > value4 && id < value5) {
$(this).hide();
} else {
$(this).show();
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td price_search="3">10</td>
<td price_search="2">20</td>
<td price_search="3">30</td>
</tr>
</table>
I am going to go out on a limb here and make broad assumptions on content not in the question.
Your .hide; is invalid syntax
You are missing value for two variables value4 and value4 which frankly are not well named variables at all. I will make an assumption that those are better named and that they come from somewhere during the page rendering.
I make an assumption that you have something you want to filter/hide by those upper/lower price points.
I make the assumption the attribute might contain values that need to be parsed (not a number as they are)
var lowerPricePoint = .45;
var upperPricePoint = 5.25;
$(function() {
$("td").filter('[price_search]').each(function() {
// parse out a price from perhaps formatted values
let price = Number.parseFloat($(this).attr("price_search").replace(/\$|,/g, ''));
// toggle visibility of the row
$(this).closest('tr').toggle(price > lowerPricePoint && price < upperPricePoint);
});
});
td {
border: solid black 1px;
padding: 0.4em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<table>
<tr>
<td>Wear it</td>
<td price_search="123.13">Shoes</td>
</tr>
<tr>
<td>Drive it</td>
<td price_search="$23,123.13">Car</td>
</tr>
<tr>
<td>Drink it</td>
<td price_search="3.13">Beet Juice</td>
</tr>
<tr>
<td>Eat it</td>
<td price_search="12.13">Can of expensive corn</td>
</tr>
<tr>
<td>Cheap</td>
<td price_search="35">Radish</td>
</tr>
<tr>
<td>Use it</td>
<td price_search="1.45">Paper towel</td>
</tr>
<tr>
<td>Plain</td>
<td price_search="$1.87">Butter</td>
</tr>
<tr>
<td>Herb</td>
<td price_search="$2.45">Butter</td>
</tr>
<tr>
<td>Cheap</td>
<td price_search="15">Gum</td>
</tr>
</table>

Showing and Hiding Table Rows Based Off Alphabet Buttons

I have a table with a lot of rows in it, and I want to give users the ability to click an 'A' button and all the results that start with 'A' are displayed. They could do the same for every letter. This is what I've come up with so far:
HTML
<input type="button" id="aSort" value="A" onclick="alphaSort(this.value);">
<table>
<thead>
<tr>
<td>Title</td>
<td>Description</td>
<td>Active</td>
</tr>
</thead>
<tbody>
<tr>
<td name="title">Apple</td>
<td>It's a fruit</td>
<td>Active</td>
</tr>
<tr>
<td name="title">Pear</td>
<td>It's also fruit</td>
<td>No</td>
</tr>
</tbody>
</table>
JS
function alphaSort(val) {
//pseudocode
var $rows = $('td[name=title]');
$rows.forEach(function(e) {
if(e.innerText == val + '%') {
e.closest('tr').show();
} else {
e.closest('tr').hide();
}
}
}
So with what I have here, the idea is if the user clicked the button only the Apple row would show. Ideally the function would be case insensitive. Could someone help me with how to properly iterate through all the table rows efficiently and compare the value stored in the title row?
you can use startsWith function : https://www.w3schools.com/jsref/jsref_startswith.asp
like this :
$("#aSort").click(function(){
var $rows = $('td[name=title]');
var val = $(this).val()
$rows.each(function() {
if($(this).text().startsWith(val)) {
$(this).closest('tr').show();
} else {
$(this).closest('tr').hide();
}
})
})
https://jsfiddle.net/xpvt214o/899140/

How to keep adding data to next table column

This Fiddle Example shows a comparison table that dynamically shows information in a column when a button is clicked. Once the table is filled up, I want to start the whole process again. But as the example shows, the buttons are stuck at adding information to th:nth-child(2) and td:nth-child(2) during the second time instead of moving on to the next column like during the first time.
I'm guessing this part needs some change if( allCells === fullCells ) { to keep information being added to next columns.
HTML
<div class="area">
<button>Gah</button>
</div>
<div class="area">
<button>Kaj</button>
</div>
<div class="area">
<button>Fdw</button>
</div>
<div class="area">
<button>ffdf</button>
</div>
<table>
<thead>
<tr>
<th>Placeholder</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Age</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Name</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Race</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Nationality</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Education</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>Language</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
Code:
$(function() {
$('.area').each(function(){
var area = $(this),
filltable ='',
button = $(this).find('button'),
buttontext = button.text();
button.on("click",function(){
var allCells = $('table').find('th,td').length;
var fullCells = $('table').find('th,td').filter(function() {
return $(this).text() != '';
}).length;
if( allCells === fullCells ) { // If table is full
$('table').find('th,td').not(':first-child').removeClass('addinfo');
filltable();
}
else { // If table isn't full
filltable = function(){
var i = $('th.addinfo').length !== 0 ? $('th.addinfo:last').index() : 0;
console.log( i );
i + 2 > $('th').length ||
$('th,td').filter(':nth-child(' + (i + 2) + ')')
.addClass( 'addinfo' ).html(buttontext);
}
filltable();
}
}); // end button on click function
});
});
Please see the attached link for demo. I have created a function name cleartable() which clears the table if its full and have used your old filltable() function to repopulate. There is repetition of code which you will have to clean up.
th:nth-child(2) identifies second child of th.
td:nth-child(2) identifies second column.
Similarly if you wanted to do something with let say second row, you can use tr:nth-child(2).
I hope this helps you understand a little about parent-child relationship in jquery.
JSFIDDLE DEMO
function clearTable() {
$('table th:nth-child(2)').html('');
$('table th:nth-child(3)').html('');
$('table th:nth-child(4)').html('');
$('table td:nth-child(2)').html('');
$('table td:nth-child(3)').html('');
$('table td:nth-child(4)').html('');
}
http://jsfiddle.net/jqVxu/1/
I think you'd better to count th only. count all td and th make me confused.
$(function () {
function filltable(buttontext) {
var i = $('th.addinfo').length !== 0 ? $('th.addinfo:last').index() : 0;
i + 2 > $('th').length || $('th,td').filter(':nth-child(' + (i + 2) + ')')
.addClass('addinfo').html(buttontext);
}
$('.area').each(function () {
var area = $(this),
button = $(this).find('button'),
buttontext = button.text();
button.on("click", function () {
var allCells = $('table').find('th').length-1;
var fullCells = $('table th.addinfo').length;
console.log(allCells, fullCells);
if (allCells === fullCells) { // If table is full
$('table .addinfo').removeClass('addinfo');
filltable(buttontext);
} else { // If table isn't full
filltable(buttontext);
}
});
});
});

Categories

Resources