I'm trying to figure out a good way to have a table which has sub data where we can sort on the main table without altering the sub data. Here's a MWE.
<table id="my-table">
<tr>
<th>Name</th>
<th class="selectable">Num</th>
</tr>
<tr id="main-1" class="main">
<td>Main 1</td>
<td>1</td>
</tr>
<tr class="main-1 sub">
<td>Sub 1</td>
<td>3</td>
</tr>
<tr id="main-2" class="main">
<td>Main 2</td>
<td>2</td>
</tr>
<tr class="main-2 sub">
<td>Sub 2</td>
<td>4</td>
</tr>
</table>
So you can kind of see I have 4 rows altogether, 2 rows that are "main" rows and 2 that are "sub" rows which are associated to certain "main" rows as can be seen through their classes.
What I'm looking to do is sort the table in such a way that only the main rows get sorted and the sub rows travel with the main rows. So if I sort by the second column I would have Main 1 - Sub 1 - Main 2 - Sub 2 and if I reverse the sort I would have the order Main 2 - Sub 2 - Main 1 - Sub 1.
I tried using https://github.com/padolsey-archive/jquery.fn/tree/master/sortElements but this seems to do the entire table and I can't figure out how to make it move "groups" of rows together.
Any ideas?
(Note: the reason I'm doing the HTML structure as is, is because normally all ".sub" are hidden and when you click on ".main" it grabs the id and shows everything that has a class with that id. I'm open to changing the HTML structure if needed, and I don't mind either way if the sub elements get sorted too, I just prefer that they move with the main row)
This is the code I was using, but since it only does every line in the table, it's not really relevant:
$('.sub').toggle();
$('.main').on('click', function(e) {
e.preventDefault();
$('.' + $(this).attr('id')).toggle();
});
var table = $('#my-table');
$('th.selectable')
.wrapInner('<span title="sort this column"/>')
.each(function(){
var th = $(this),
thIndex = th.index(),
inverse = true;
th.click(function(){
table.find('td').filter(function(){
return $(this).index() === thIndex;
}).sortElements(function(a,b){
a_num = parseInt($.text([a]))
b_num = parseInt($.text([b]))
if (a_num == b_num)
return 0;
return a_num > b_num ?
inverse ? -1 : 1
: inverse ? 1 : -1;
}, function() {
// parentNode is the element we want to move
return this.parentNode;
});
inverse = !inverse;
});
});
Related
This question already has answers here:
HTML table sortable by ROW instead of COLUMN with JavaScript?
(2 answers)
Closed 1 year ago.
I want to sort a tree table not by columns but rows, its header is on the left in the tree part.
My tree table looks like this
the tree table plugin I used.
And the code is something like this
<table>
<tr data-node-id="index">
<th>Index</th>
<td>...</td>
<td>...</td>
</tr>
<tr data-node-id="patient">
<th>Patient Characteristics</th>
</tr>
<tr data-node-id="name" data-node-pid="patient">
<th>Name</th>
<td>...</td>
<td>...</td>
</tr>
<tr data-node-id="DOB" data-node-pid="patient">
<th>DOB</th>
<td>...</td>
<td>...</td>
</tr>
<tr data-node-id="age" data-node-pid="patient">
...
There is no <thead> part, so many sorting methods not work well on this table. I'm new to javascript sort of things, and have no idea if this is easy to do. But I did not find methods for this situation online, so I try to ask for help.
First, access the table element:
var table = document.getElementById("table").children[0]; // You can just add an id to the table
Here is a function for sorting by row:
function sortRow(rowNum) { // rowNum is index of row: for example, name is 2
var answerArray = [];
for (i = 1; i < table.children[rowNum].children.length; i++) {
answerArray.push(table.children[rowNum].children[i].innerHTML);
}
return answerArray;
This function takes in the row number, and returns an array of all values in the row.
I am validating a drill down process in the portal i am testing, for this my script is doing:
Read the value from the first row of a table and click at this value (there is a link for certain cells that perform the drill down to the detail page)
To click at this particular cell I am using it's ID:
<table id="transHistTable" class="table table-hover table-bordered table-striped dataTable no-footer" style="width: 100%" role="grid" aria-describedby="transHistTable_info">
<thead>
<tbody>
<tr role="row" class="odd">
<td id="0-0" class="ng-scope">31 Jul 2018</td>
<td id="0-1" class="ng-scope">RandomText0</td>
<td id="0-2" class="ng-scope">RandomText1</td>
<td id="0-3" class="ng-scope">EmptyValue</td>
<td id="0-4" class="ng-scope">Value I Click And Save it</td>
So for this table I am clicking directly to the row 0 column 4 since my data and my filters will always bring only one row, but then, comes my problem....
When the drill down is performed I never know how many rows I will have since it depends of user operations.
I need to perform a validation to compare the sum of all the values from the table displayed after the drill down with the value captured from table "transHistTable" row 0 column 4
This is the values I get after performing the Drill Down:
<table id="transHistDetailTable" class="table table-hover table-bordered table-striped dataTable no-footer" style="width: 100%" role="grid" aria-describedby="transHistDetailTable_info">
<thead>
</thead>
<tbody><tr role="row" class="odd">
<td id="0-0" class="ng-scope">Site</td>
<td id="0-1" class="ng-scope">Date</td>
<td id="0-2" class="ng-scope">Time</td>
<td id="0-3" class="ng-scope">I</td>
<td id="0-4" class="ng-scope">value 1</td>
<td id="0-5" class="ng-scope">value 2</td>
<td id="0-6" class="ng-scope">value 3</td>
<td id="0-7" class="ng-scope">12</td>
</tr></tbody>
</table>
So what I would like to do is reading all the rows (could be 0,1,2,3,4,5...) saving the value that is stored in Column 7 then after this is done, perform a sum and then comparing with the value I have saved from the first table.
My code is this one:
var rowstransHistDetail = element(by.id('transHistDetailTable')).all(by.tagName("tr"));
rowstransHistDetail.count().then(function(rcount){
//In case only 1 row is displayed
if (rcount < 3)
{
element(by.id('0-7')).getText().then(function(valueQty){
expect(valueQty).toEqual(600)
})
}
else
{
var tempValue
for (i=0; i < rcount; i++)
{
element(by.id(i+'-7')).getText().then(function(valueQty){
tempValue = Number(tempValue) + Number(valueQty)
})
}
expect(tempValue).toEqual(600);
}
});
But when I execute this, gives me a undefined value
Any ideas how to solve this please?
Thank you!!!!
It seems that you are incrementing a value in a loop before execution.
See here: https://stackoverflow.com/a/6867907/6331748
Should bee i++ instead of ++i in a loop.
Drop me a line if I'm wrong.
===========================
Edited:
Here's some code from my side:
var expectedCells = element.all(by.css('#transHistDetailTable tr td:nth-of-type(5)'));
var currentSum = 0;
expectedCells.each((eachCell) => {
eachCell.getText().then((cellText) => {
currentSum += Number(cellText);
});
}).then(() => {
expect(currentSum).toEqual(600);
});
Sorry, but wasn't able to test it. I only want to share a main idea and elaborate it.
expectedCells are all id=n-4 cells. We go through all elements and get text from them, change to the number type and add to current value. Aftermath we do an assertion.
It also looks that if statement is not necessarily.
Let me know how it works.
Two options for your issue:
1) using element.all().reduce()
let total = element
.all(by.css('#transHistDetailTable > tbody > tr > td:nth-child(8)'))
.reduce(function(acc, item){
return item.getText().then(function(txt) {
return acc + txt.trim() * 1;
});
}, 0);
expect(total).toEqual(600);
2) using element.all().getText() and Array.reduce
let total = element
.all(by.css('#transHistDetailTable > tbody > tr > td:nth-child(8)'))
.getText(function(txts){ //txts is a string Array
return txts.reduce(function(acc, cur){
return acc + cur * 1;
}, 0);
});
expect(total).toEqual(600);
Update Code working Here
I have a table populated with teacher's disciplines that has: day of the week and it's time period, of course it also have the disciplines.
Now I need to remove those items.
Table:
<table id="prof-table">
<tbody><tr>
<th>HORÁRIO</th>
<th data-dia="mon">Monday</th>
<th data-dia="tue">Tuesday</th>
<th data-dia="wed">Wednesday</th>
<th data-dia="thu">Thursday</th>
<th data-dia="fri">Friday</th>
</tr>
<tr>
<td>08:30 ~ 10:30</td>
<td><ol><li data-id="6" data-prof="4">Calculo A</li></ol></td>
</tr>
<tr>
<td>10:30 ~ 12:30</td>
td></td><td><ol><li data-id="2" data-prof="4">Lab II</li></ol></td>
</tr>
<tr>
<td>14:30 ~ 16:30</td>
</tr>
<tr>
<td>16:30 ~ 18:30</td>
</tr>
<tr>
<td>18:30 ~ 20:30</td>
<td></td><td></td><td></td><td></td><td><ol><li data-id="5" data-prof="4">Estatistica</li></ol></td>
</tr>
<tr>
<td>20:30 ~ 21:30</td>
</tr>
<tr>
<td>21:30 ~ 23:30</td>
</tr>
</tbody></table>
What I did so far is to get the <td> from the rows but I don't know how to work with it, tried to use .each() from JQuery but I just cant do it.
Javascript:
var myRow = document.getElementById("prof-table").rows[range.index + 1];
var test = $(myRow.getElementsByTagName('td')).not(':first-child');//Skip the first td because its the time period of the discipline.
console.log(teste);
Now if you check the console.log() this is what is shown:
As you can see, there are three lines. Each line/obj has the exactly number of <td>s from the row.
What I need to do is loop through each of these lines. Also I need to reset the index for each loop.
EX: While interacting with the first line of the image, my Index goes from 0 ~ 1. Then when start the second line I need to start my index from 0 again untill 4 (because it has 5 elements td)
Tried something like:
$.each(teste, function(index, obj){
if($(obj).text() == "")
myRow.deleteCells(index);
});
But as the index doesnt "reset" for each of those lines in the picture, I get error about overflowing the row index limite. Because while the first row has only one <td> member, the last has 5, as the index is always growing ++ I get that error. And I have no idea how to work around it.
Because I don't understand you situation well, I create two function
To delete data based on "data-prof" attribute of "li" inside the "td"
To delete all data of the table.
In my oppinion, if you assign "data-prof" value to the "td" instead of "li", it'll boost performance.
Hope it's help.
Function to delete data based on "data-prof" attribute:
function resetTableData(dataProf) {
// Get cell list by data-prof
var $cellList = $("#prof-table").find("li[data-prof = " + dataProf + "]").closest("td");
$cellList.each(function(){
$(this).html(""); //Remove inner HTML of cells.
});
}
Function to delete data of all cells:
function resetTableData() {
// Get row list
var $rowList = $("#prof-table").find("tr").not(":first"); // First row is the header, don't need to select.
// Loop row list and get cell list of each
$rowList.each(function(){
// Cell list
var $cellList = $(this).find("td").not(":first"); // First column is time period, don't delete it.
// Loop cell list and delete content
$cellList.each( function() {
$(this).html(""); //Remove inner HTML of cells.
});
});
}
Well I'm trying to implement the drag and drop sortable rows on table using Mootools. The issue that I'm banging my head all this morning and could figure out it's solution, here's the HTML table :
<table class="table table-bordered">
<tbody>
<tr class="item">
<td>Mark</td>
<td>Otto</td>
<td>#mdo</td>
</tr>
<tr class="item">
<td>Jacob</td>
<td>Thornton</td>
<td>#fat</td>
</tr>
<tr class="item">
<td>Larry</td>
<td>the Bird</td>
<td>#twitter</td>
</tr>
</tbody>
</table>
And here's the Javascipt:
window.addEvent('domready', function() {
/* create sortables */
var d = $$('tr');
var sb = new Sortables(d, {
/* set options */
clone:true,
revert: true,
/* initialization stuff here */
initialize: function() {
},
/* once an item is selected */
onStart: function(el) {
el.setStyle('background','#add8e6');
},
/* when a drag is complete */
onComplete: function(el) {
el.setStyle('background','#ddd');
//build a string of the order
}
});
});
And here's a jsfiddle . Much appreciated.
Here is the problem you are having as I understand it:
You wish to make the rows sortable? And your Fiddle example has all cells individually sortable.
Your issue is here:
var d = $$('tr');
var sb = new Sortables(d);
You have passed in all tr's as sortable lists. This means all td's are sortable.
You need to pass in:
var d = $('tbody');
var sb = new Sortables(d);
Mootools Docs state:
Arguments:
[1] list - (mixed) required, the list or lists that will become sortable.
A list in this case is any element (or array of elements) whose children will become sortable.
See here: Updated Fiddle
A piece of my HTML code looks like this:
<tr data-tt-id="1">
<td>Parent</td>
</tr>
<tr data-tt-id="2" data-tt-parent-id="1">
<td>Child 1</td>
</tr>
<tr data-tt-id="4" data-tt-parent-id="2">
<td>Child 1's child</td>
</tr>
<tr data-tt-id="3" data-tt-parent-id="1">
<td>Child 2</td>
</tr>
<tr data-tt-id="5" data-tt-parent-id="3">
<td>Child 2's child</td>
</tr>
I'm selecting the parent which has data-tt-id="1" by using this:
$('tr[data-tt-parent-id="1"]');
But I want all of the children and children's children too, no matter how deep the tree might be.
As you can see data-tt-id is the child's unique ID and data-tt-parent-id is the ID which the child is appended to.
I was thinking about looping through each one of them, but I have no idea how I would achieve that.
How do I select all descendants for the tr that has data-tt-id set to "1"?
function getDescendants(el, curset) {
curset = curset || $(""); // default to empty set
var id = el.data('tt-id');
var children = $("tr[data-tt-parent-id="+id+"]");
if (children.length) {
curset = curset.add(children);
children.each(function() {
curset = curset.add(getDescendants($(this), curset));
});
}
return curset;
}
There's probably more idiomatic ways to write this without having to reassign curset = ... in several places, but this works.
DEMO