I want to invert table tbody rows with jQuery.
WHAT I HAVE:
<table width="630" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td>TITLE A</td>
<td>TITLE B</td>
(...) continue in jsfiddle.
Here what I have and what I want: http://jsfiddle.net/ZaUrP/1/
fiddle
pretty much the same as the other guy, only I use .detach() which is guarunteed to keep any crazy events that were attached to the trs intact. I also use $.makeArray to avoid reversing any of the proto stuff on the base jQuery object.
$(function(){
$("tbody").each(function(elem,index){
var arr = $.makeArray($("tr",this).detach());
arr.reverse();
$(this).append(arr);
});
});
Try this:-
Get the array of trs from tbody using .get() and use Array.reverse to reverse the elements and assign it back.
var tbody = $('table tbody');
tbody.html($('tr',tbody).get().reverse());
Fiddle
In case you have events to tr or any containing elements you could just attach it using delegation, so that the reversed elements also get them delegated.
Demo
$('tbody').each(function(){
var list = $(this).children('tr');
$(this).html(list.get().reverse())
});
Demo --> http://jsfiddle.net/ZaUrP/5/
I wrote a jQuery plugin called $.reverseChildren which will reverse all the specified children of a given element. Credit goes to DefyGravity for his insightful and intriguing use of $.makeArray.
I have not only reversed the rows of a table, but also the columns.
(function($) {
$.fn.reverseChildren = function(childSelector) {
this.each(function(el, index) {
var children = $.makeArray($(childSelector, this).detach());
children.reverse();
$(this).append(children);
});
return this;
};
}(jQuery));
$(function() {
var tableCopy = $('#myTable').clone(true).attr('id', 'myTableCopy').appendTo(
$('body').append('<hr>').append($('<h1>').html('Reversed Table')));
tableCopy.find('tr').reverseChildren('th, td'); // Reverse table columns
tableCopy.find('tbody').reverseChildren('tr'); // Reverse table rows
});
* { font-family: "Helvetica Neue", Helvetica, Arial; }
h1 { font-size: 16px; text-align: center; }
table { margin: 0 auto; }
th { background: #CCC; padding: 0.25em; }
td { border: 1px solid #CCC; padding: 5px; }
hr { margin: 12px 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h1>Original Table</h1>
<table id="myTable" width="320" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr> <th>Header A</th> <th>Header B</th> <th>Header C</th> </tr>
</thead>
<tbody>
<tr> <td>Data 1A</td> <td>Data 1B</td> <td>Data 1C</td> </tr>
<tr> <td>Data 2A</td> <td>Data 2B</td> <td>Data 2C</td> </tr>
<tr> <td>Data 3A</td> <td>Data 3B</td> <td>Data 3C</td> </tr>
<tr> <td>Data 4A</td> <td>Data 4B</td> <td>Data 4C</td> </tr>
</tbody>
</table>
I know this is late, but it can help some other users looking for an answer
function reverseTable() {
var table = document.getElementById("table")
var trContent = []
for (var i = 0, row; row = table.rows[i]; i++) {
trContent.push(row.innerHTML)
}
trContent.reverse()
for (var i = 0, row; row = table.rows[i]; i++) {
row.innerHTML = trContent[i]
}
}
table {border-collapse: collapse}
table, td {border: 1px solid black}
<html>
<body onload="reverseTable()">
Original Table
<table>
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 1,2</td>
<td>Cell 1,3</td>
</tr>
<tr>
<td>Cell 2,1</td>
<td>Cell 2,2</td>
<td>Cell 2,3</td>
</tr>
<tr>
<td>Cell 3,1</td>
<td>Cell 3,2</td>
<td>Cell 3,3</td>
</tr>
</tbody>
</table>
<br>
Reversed Table
<table id="table">
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 1,2</td>
<td>Cell 1,3</td>
</tr>
<tr>
<td>Cell 2,1</td>
<td>Cell 2,2</td>
<td>Cell 2,3</td>
</tr>
<tr>
<td>Cell 3,1</td>
<td>Cell 3,2</td>
<td>Cell 3,3</td>
</tr>
</tbody>
</table>
</body>
</html>
Related
Is there a way to archive the same as described in this post using only javascript and no jquery? The goal is to detect clicks on individual rows, while ignoring clicks on the header.
My approach so far:
document.addEventListener('click', function(e) {
var table = document.getElementById("table01");
var rows = table.getElementsByTagName("tr");
for (i = 0; i < rows.length; i++) {
rows[i].addEventListener('click', function(e) {
console.log('clicked')
})
}
})
<table class="table table-striped" id="table01">
<thead class="thead-green">
<tr>
<th scope="col">A</th>
<th scope="col">B</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<td>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<td>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
</tbody>
</table>
I would avoid attaching event listeners to each individual rows, for two reasons:
If the table is too long, the large number of event listeners will
hurt performance
If the table content is dynamic, newly added rows
will require new listeners to be added.
Instead, you can attach an event listener on the entire table and check if the click target is inside the tbody. (this is also known as the event delegation pattern in JavaScript)
var table = document.getElementById("table01");
table.addEventListener('click', e => {
if (e.target.closest('tbody')) {
console.log(e.target.closest('tr')); // emit the row you just clicked;
}
});
<table class="table table-striped" id="table01">
<thead class="thead-green">
<tr>
<th scope="col">A</th>
<th scope="col">B</th>
</tr>
</thead>
<tbody>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
</tbody>
</table>
And even simpler than that, you can attach event listener onto the tbody. This way you can guarantee all clicks come from rows in the table body.
var tableBody = document.getElementById("table01").querySelector('tbody');
tableBody.addEventListener('click', e => {
if (e.target.closest('tr')) {
console.log(e.target.closest('tr')); // emit the row you just clicked;
}
});
<table class="table table-striped" id="table01">
<thead class="thead-green">
<tr>
<th scope="col">A</th>
<th scope="col">B</th>
</tr>
</thead>
<tbody>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
<tr>
<th>Item 1</td>
<td>Item 2</td>
<td>Item 3</td>
</tr>
</tbody>
</table>
Meanwhile, please consider the accessibility of your desired behavior. How will a keyboard user trigger this kind of click? How will a screen reader user know the rows are actually "clickable"?
Just made little modification to Your javascript. See provided code :
document.addEventListener('click', function(e) {
var table = document.getElementById("table01");
var rows = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr");
for (i = 0; i < rows.length; i++) {
rows[i].addEventListener('click', function(e) {
alert('clicked');
})
}
});
You'll see that modification is made in Your var rows = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr");. By this way You'll get only rows from tbody, not entire table (thead and tfoot) using pure javascript and avoid querySelector too.
Collect all the headers in a variable and check if any of those are the descendants of each row
var headers = document.getElementsByTagName("th");
for (i = 0; i < rows.length; i++) {
var hasHeader = false;
for (j = 0; j < headers.length; j++) {
if(rows[i].contains(headers[j])) {
hasHeader = true;
break;
}
}
if(!hasHeaders) {
rows[i].addEventListener('click', function(e) {
console.log('clicked')
})
}
}
document.querySelectorAll("#table01>tbody>tr").forEach(function(tr){tr.addEventListener....};
">" stands for the direct children.
I have one table in that I am repeating data inside column. One column have property and other have value of that property. I am getting problem when data wraps property and value not aligned properly. Below is my example.
My example
<table cellspacing="0" celpadding="0">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div>Property 1</div>
<div>Property 2</div>
<div>Property 3</div>
<div>Property 4</div>
</td>
<td>
<div>Value 1</div>
<div>Value 2</div>
<div>Value 3</div>
<div>Value 4</div>
</td>
</tr>
<tr>
<td>Property 2</td>
<td>Value 2</td>
</tr>
<tr>
<td>Property 1</td>
<td>Property 1</td>
</tr>
</tbody>
</table>
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
color:#fff;
}
table{
width:100px
}
table tr td,table tr th{border:1px solid #fff;padding:5px;}
table tr td div{
border-bottom:1px solid #eee;
padding:5px 0;
}
Right now I have given fixed width to table to produce my problem. In this condition what should I do to reolve this issue. I want to align property and value equally with respective values. Please help.
The easiest way to solve your issue is to add a fixed height to your table tr td div rules, so that it aligns properly. So, you could update that specific rule to something like:
table tr td div{
border-bottom: 1px solid #eee;
padding: 5px 0;
height: 48px;
}
However, <div>s are not really supposed to go into tables. Maybe you need to rethink your table's structure, as this will have accessibility issues for sure.
What's wrong with using a classic table with no random divs contained in your td elements? Consider the below:
<table cellspacing="0">
<tr>
<td>Property 1</td>
<td>Value 1</td>
</tr>
<tr>
<td>Property 2</td>
<td>Value 2</td>
</tr>
<tr>
<td>Property 3</td>
<td>Value 3</td>
</tr>
<tr>
<td>Property 4</td>
<td>Value 4</td>
</tr>
</table>
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
color:#fff;
}
table{
width:200px
}
td {
width:100px;
padding:10px;
border:1px solid white;
}
Here is a fiddle: https://jsfiddle.net/8dyxqbkz/2/
You should use <tr><tr/> tag for table row
and <td><td/> tag for table cell.
Let me show an example:
<table cellspacing="0" celpadding="0">
<tr>
<td>
Property 1
</td>
<td>
Property 2
</td>
<td>
Property 3
</td>
<td>
Property 4
</td>
</tr>
<tr>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
<td>Value 4</td>
</tr>
<tr>
<td>Property 2</td>
<td>Property 1</td>
</tr>
<tr>
<td>Value 2</td>
<td>Property 1</td>
</tr>
</table>
The code example below creates a table with 6 rows. The last two rows are empty.
The JavaScript code finds and displays correctly the number of rows in a table.
I would like to find the row number of the first row with empty cells. In this example it will be row (4) (counting from 0). I tried several solutions and they did not work.
Your help is appreciated.
Thanks,
Menachem
<!DOCTYPE html>
<html>
<head>
<style>
table, td {
border: 1px solid black;
}
</style>
</head>
<body>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert ("Number of rows in the table is " + x);
</script>
</body>
</html>
You can use combination of :has() and :not().
td:not(:empty) get td which is not empty
tr:not(:has(td:not(:empty))) selects all tr which is not contains any non empty td
tr:not(:has(td:not(:empty))):first gets the first tr from them
var index = $("#myTable tr:not(:has(td:not(:empty))):first").index();
console.log(index);
table,td {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert("Number of rows in the table is " + x);
</script>
I would like to find the row number of the first row with empty cells
You can use :has(), adjacent sibling selector + to match td:empty, that has next element sibling that is td:empty, :first, index(). The index of the first tr element which has a child td element without child nodes would be 3
$("#myTable tr:has(td:empty + td:empty):first").index()
<!DOCTYPE html>
<html>
<head>
<style>
table,
td {
border: 1px solid black;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
</head>
<body>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
<br>
<script>
var x = document.getElementById("myTable").rows.length;
alert("Number of rows in the table is " + x);
console.log($("#myTable tr:has(td:empty + td:empty):first").index());
</script>
</body>
</html>
$("#myTable tr").find('td').filter(function() {
return $(this).text() == '' ;
}).addClass('empty');
table,
td {
border: 1px solid black;
}
.empty {
background-color: red
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
Use .filter()
You can use a test inside a .filter() call that checks the number of empty td's against the number of all td's in a tr. Filter out all those that have any non-empty td, call .first() to find the first one in the filtered set, and .index() to get the index
var index = $("#myTable tr").filter(function(){
return $("td",this).length == $("td:empty",this).length;;
}).first().index();
console.log("Empty row index: "+index);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable">
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
Assuming an example table:
<table>
<tr>
<th>Head 1</th>
<th>Head 2</th>
</tr>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
<tr>
<td>Data 3</td>
<td>Data 4</td>
</tr>
<tr>
<td>Data 5</td>
<td>Data 6</td>
</tr>
<tr>
<td>Data 7</td>
<td>Data 8</td>
</tr>
</table>
I'm looking for the best technique to highlight the rows <= n, where n is the hovered over row (excluding the header row). For example, if the mouse is over
<tr>
<td>Data 5</td>
<td>Data 6</td>
</tr>
the following part of the table should be highlighted:
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
<tr>
<td>Data 3</td>
<td>Data 4</td>
</tr>
<tr>
<td>Data 5</td>
<td>Data 6</td>
</tr>
Any ideas how this effect could be achieved?
Basically, you could see the thing the opposite way : any tr after hovered tr should have no background:
test this :
table:hover tr {
background:gray;
}
table:hover tr:hover ~tr {
background:none;
}
DEMO
=============== EDITED from request in comments ================
React only on last element in row.
BEWARE :This option doesn't allow to click in cells but last one of each row.
table {
pointer-events:none;
}
table tr :last-child {
pointer-events:auto;
cursor: pointer;
}
Try this:
(fiddle: http://jsfiddle.net/5SLN3/)
$('tr').hover(
function(){
$(this).addClass('hover').prevAll().addClass('hover');
},
function(){
$(this).removeClass('hover').prevAll().removeClass('hover');
}
)
and the style:
<style>
tr.hover td{background-color:#888}
</style>
Since there is not previous selector, you need to sort of do the opposite. Add a hove to the tbody and chnage the color of the rows after the row that was chosen.
HTML:
<table class="hovTable">
<thead>
<tr>
<th>Head 1</th>
<th>Head 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data 1</td>
<td>Data 2</td>
</tr>
<tr>
<td>Data 3</td>
<td>Data 4</td>
</tr>
<tr>
<td>Data 5</td>
<td>Data 6</td>
</tr>
<tr>
<td>Data 7</td>
<td>Data 8</td>
</tr>
</tbody>
</table>
CSS:
.hovTable, tr, td, th {
border-collapse: collapse;
border: 1px solid black;
}
.hovTable tbody td {
background-color: #FFF;
}
.hovTable tbody:hover td {
background-color: #CCC;
}
.hovTable tbody:hover tr:hover ~ tr td {
background-color: #FFF;
}
Example:
JSFiddle
References:
General Sibling Selector
var nameOfRows = "row"
//useful when there are other tables in your document.
//use like this
//<table>
//...<tr name="row">...</tr><tr name="row">...</tr>...
var backgroundOriginal = "#ffffff"; //background when the row isn't selected
var backgroundHover = "#ff0000"; //background when the row is selected;
function setHigh (id)
{
var rows = document.getElementsByName(nameOfRows);
for (var i = 0; i < rows.length; i++)
{
rows[i].style.background = backgroundOriginal;
}
for (i = 0; i <= id; i++) {
rows[i].style.background = backgroundHover;
}
}
for (var i = 0; i < document.getElementsByName(nameOfRows).length; i++)
{
document.getElementsByName(nameOfRows)[i].onmouseover = setHigh(i);
}
Try this out. I've not tested it yet, but I think it should work!
<table id="foo" border="1">
<tr>
<td rowspan="3" class="date">Monday</td>
<td>Cell A1 edit</td>
</tr>
<tr>
<td>Cell A2 edit</td>
</tr>
<tr>
<td>Cell A3 edit</td>
</tr>
<tr>
<td rowspan="3" class="date">Tuesday</td>
<td>Cell B1 edit</td>
</tr>
<tr>
<td>Cell B2 edit</td>
</tr>
<tr>
<td>Cell B3 edit</td>
</tr>
</table>
On the above table, how can I get the value of rowSpan of the parent TD? For example, if I click on the edit link of cell A3, a Javascript function will be called. In that function, I need the rowSpan value of the parent TD (i.e. Monday).
These and are dynamically created.
If you are using jQuery try this one:
$("#foo").on('click', '.edit', function(){
var tr = $(this).closest('tr'),
rowspan = 0,
text, td;
while(tr.length && !rowspan ) {
td = tr.find('td:first');
rowspan = td.attr('rowspan');
tr = tr.prev();
}
alert(td.text());
})
Live demo: http://jsfiddle.net/bJfKL/1/
Assuming that in your function this is a reference to the a element, you could do this:
var tr = this.parentNode.parentNode;
tr = tr.parentNode.rows[tr.rowIndex - (tr.rowIndex % 3)];
alert(tr.cells[0].innerHTML);
Or if it's not guaranteed to be every 3 rows, you can traverse and test the rowSpan.
var tr = this.parentNode.parentNode;
while (tr && tr.cells[0].rowSpan === 1)
tr = tr.parentNode.rows[tr.rowIndex - 1];
alert(tr.cells[0].innerHTML);
FWIW, you should probably take advantage of multiple <tbody> elements. This makes things much simpler.
<table id="foo" border="1">
<tbody>
<tr>
<td rowspan="3" class="date">Monday</td>
<td>Cell A1 edit</td>
</tr>
<tr>
<td>Cell A2 edit</td>
</tr>
<tr>
<td>Cell A3 edit</td>
</tr>
</tbody>
<tbody>
<tr>
<td rowspan="3" class="date">Tuesday</td>
<td>Cell B1 edit</td>
</tr>
<tr>
<td>Cell B2 edit</td>
</tr>
<tr>
<td>Cell B3 edit</td>
</tr>
</tbody>
</table>
Then its just:
var tr = this.parentNode.parentNode.parentNode.rows[0];
tr.cells[0].innerHTML;