<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;
Related
We have a table:
<table>
<tbody>
<tr>
<td>Column 1</td>
<td colspan="3">Column 2</td>
<td>Column 3</td>
<td colspan="99999">Column 4</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td id="target">C</td>
<td>D</td>
<td>E</td>
<td>F</td>
</tr>
</tbody>
</table>
Using JavaScript or jQuery, how would we able to get the column element (or its index) of the first row that is spanning the cell with id "target"? I don't really want to use any box positioning method (is: getBoundingClientRect()) technique.
In this example, the associated cell element that is spanning "target" is the cell with text "Column 2".
Here is a solution for the case, that the second row also has colspans and there is no third row:
Iterate over the cells of the second row with a for loop and count their colspans until you find your target cell (if there is no colspan defined it is automatically '1'). Then iterate over the cells of the first row and count their colspans until the count is equal or bigger then the count of the second row. In that case you have found the desired head cell.
Working example:
const head_cells = document.querySelectorAll('#head-row td');
const target_cells = document.querySelectorAll('#target-row td');
let head_position = 0;
let target_position = 0;
for (i = 0; i < target_cells.length; i++) {
target_position += target_cells[i].colSpan;
if (target_cells[i].id === 'target') {
for (k = 0; k < head_cells.length; k++) {
head_position += head_cells[k].colSpan;
if (head_position >= target_position) {
console.log(head_cells[k].textContent);
break;
}
}
break;
}
}
<table>
<tbody>
<tr id="head-row">
<td>Column 1</td>
<td colspan="4">Column 2</td>
<td>Column 3</td>
<td colspan="99999">Column 4</td>
</tr>
<tr id="target-row">
<td>A</td>
<td colspan="2">B</td>
<td id="target">C</td>
<td>D</td>
<td>E</td>
<td>F</td>
</tr>
</tbody>
</table>
<script>
function findHeader(cell) {
let count = cell.cellIndex + 1; // 3
for(let header of headers.cells) {
const colspan = +header.getAttribute('colspan') || 1;
count -= colspan;
if (count<1) return alert(header.textContent);
}
}
</script>
<table border=1>
<tbody>
<tr id="headers">
<td>Column 1</td>
<td colspan="3">Column 2</td>
<td>Column 3</td>
<td colspan="99999">Column 4</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td onclick="findHeader(this)">Click</td>
<td>D</td>
<td>E</td>
<td>F</td>
</tr>
</tbody>
</table>
The following code gets all the tr tags in #mytable:
table = document.getElementById("myTable");
trs = table.getElementsByTagName("tr");
But if we want to get only tr tags whose display is not none, what should I do?
Thanks.
Not the best solution, but you can do this...
let tableRows = $("#my-table tr");
tableRows.map((i, obj)=>{
if($(obj).attr('style') != 'display: none;'){
// whatever you want to do here...
console.log(obj);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<table id="my-table">
<tbody>
<tr>
<td>data 1</td>
<td>data 2</td>
</tr>
<tr style="display: none;">
<td>data 3</td>
<td>data 4</td>
</tr>
<tr>
<td>data 5</td>
<td>data 6</td>
</tr>
<tr style="display: none;">
<td>data 7</td>
<td>data 8</td>
</tr>
<tr>
<td>data 9</td>
<td>data 10</td>
</tr>
</tbody>
</table>
You can use the :visible selector which is a jquery extension (https://api.jquery.com/visible-selector/) that allows seelction of elements based on display visibility.
In the following snippet - there are 3 tr's but the middle one is hidden with display:none. The console log targets the visible tr's and logs the number (2);
$(document).ready(function(){
const totalRows = $('#myTable tr');
const visibleRows = totalRows.filter(':visible');
console.log('total rows: '+ totalRows.length); // gives 3
console.log('visible rows: '+ visibleRows.length); // gives 2 - since one tr is hidden
})
.second-row {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="myTable">
<tbody>
<tr class="first-row">
<td>row 1</td>
<td>row 1</td>
<td>row 1</td>
</tr>
<tr class="second-row">
<td>row 2</td>
<td>row 2</td>
<td>row 2</td>
</tr>
<tr class="third-row">
<td>row 3</td>
<td>row 3</td>
<td>row 3</td>
</tr>
</tbody>
</table>
Simple solution with VanillaJS
var elems = document.querySelectorAll('tr');
var noneElems = [];
elems.forEach(function (element) {
if (window.getComputedStyle(element).display == 'none') {
noneElems.push(element);
}
});
console.log( { noneElems } );
<table id="mytable">
<tr>
<td>Block</td>
<td>1</td>
</tr>
<tr style="display:none">
<td>None</td>
<td>1</td>
</tr>
<tr>
<td>Block</td>
<td>2</td>
</tr>
<tr style="display:none">
<td>None</td>
<td>2</td>
</tr>
</table>
I use this code to hide all rows in a table that have a td with a class named "hide". This is working fine.
$('.table').find('tr:has(td.hide)').hide();
Now I am trying to hide all all rows in table if the row has n number of td with the class equal to hide. I was not even able to loop on the tr list of the table with thos code
$('.table > tr').each(function() {
console.log("new tr", $(this).text());
});
my html looks as following
<table class='table'>
<tr class='hidable'><td class='hide'> Some text</td> <td class='hide'> Some text</td></tr>
<tr class='hidable'><td class='hide'> Some text</td> <td class='nothide'> Some text</td></tr>
</table>
in this example i want to hide the row if the two tds have the class hide.
When you create a table without tbody, that tag is automatically generated.
Child combinator:
Elements matched by the second selector must be the immediate children of the elements matched by the first selector.
Include tbody as part of the selector. Try $('.table tbody > tr')
$('.table tbody > tr').each(function() {
console.log("new tr", $(this).text());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class='table'>
<tr class='hidable'>
<td> Some text </td>
<td class='hide'> Some text</td>
<td class='hide'> Some text</td>
</tr>
<tr>
<td class='nothide'> Some text</td>
</tr>
</table>
OR: Remove > from the selector
$('.table tr').each(function() {
console.log("new tr", $(this).text());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class='table'>
<tr class='hidable'>
<td> Some text </td>
<td class='hide'> Some text</td>
<td class='hide'> Some text</td>
</tr>
<tr>
<td class='nothide'> Some text</td>
</tr>
</table>
With jQuery I'd suggest using toggleClass():
// here we select the <tr> elements, and chain the toggleClass() method
// to that jQuery collection:
$('tr').toggleClass(function() {
// within the anonymous function 'this' refers to the current <tr>
// element of the collection (the native DOM node not a jQuery $(this))
// we use Element.querySelectorAll() to retrieve all the <td> elements
// with a class of 'hide' and then test the length to see if there
// are more than one. If this is true, we return the 'hideRow' class
// to the method, otherwise we return an empty string. Obviously this
// approach uses a CSS selector ('hideRow') to hide the relevant <tr>
// elements:
return this.querySelectorAll('.hide').length > 1 ? 'hideRow' : '';
});
.hide {
background-color: limegreen;
opacity: 0.5;
}
.hideRow {
/* here we use opacity: 0.5 so that you can visually
see which elements are selected/affected; in production
you should obviously switch to 'display: none' to hide the
elements: */
opacity: 0.5;
}
<table>
<tbody>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td class="hide">cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td class="hide">cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td class="hide">cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td class="hide">cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td class="hide">cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
</tbody>
</table>
JS Fiddle demo.
In native JavaScript — using a contemporary browser — the following would achieve the same thing:
// here we use the spread syntax to conver the iterable NodeList returned by
// document.querySelectorAll() into an Array, we then iterate over that Array
// of Nodes using Array.prototype.forEach():
[...document.querySelectorAll('tr')].forEach(
// we use an anonymous Arrow function - as we don't need to use 'this' - in
// order perform a function on each of the <tr> elements of the Array of
// <tr> elements; the 'tr' passed into the function is a reference to the
// current <tr>:
(tr) => {
// here we use the Element.classList API, with its toggle() method to
// supply a class-name ('hideRow'), and we use the assessment to determin
// whether or not the class-name should be applied. If the assessment
// evaluates to true then the class-name is applied, if false it is not:
tr.classList.toggle('hideRow', tr.querySelectorAll('.hide').length > 1);
});
.hide {
background-color: limegreen;
opacity: 0.5;
}
.hideRow {
opacity: 0.5;
}
<table>
<tbody>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td class="hide">cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td class="hide">cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td class="hide">cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td class="hide">cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td class="hide">cell 1</td>
<td class="hide">cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
<td>cell 4</td>
</tr>
</tbody>
</table>
JS Fiddle demo.
As an important addenda to my original answer, the reason that your selector:
$('.table > tr')
doesn't work is because of the child combinator, the >, which would cause jQuery to retrieve the <tr> elements which are children of the <table class=".table"> element. As browsers predictably rescue 'broken' HTML — though a <tbody> is not mandatory according to the spec — they will all automagically insert a <tbody> element to wrap any <tr> elements which are contained within a <table> that aren't already so wrapped.
This has been discussed elsewhere on the site: https://stackoverflow.com/a/5568877/82548
References:
JavaScript:
Array.prototype.forEach().
Arrow functions ((arguments) => { ...statements... }.
document.querySelectorAll().
Element.classList API.
Element.querySelectorAll().
NodeList.prototype.forEach().
Spread (...) syntax.
jQuery:
toggleClass().
You want to hide row if 2 td's have hide class , if this is your requirement then here is tested example
$(".table tr").each(function(){
if($(this).find("td.hide").length == 2) {
$(this).hide();
}
});
Here I loop through each tr and then in each tr I check all td with class "hide" with find("td.hide").length == 2 and if length is equal to two then hide the row.
Is there a simple way of swapping elements of an html table using javascript?
for instance having a table like this:
<table>
<tr>
<td class = "draggable">
<div class = "droppable">Item 1</div>
</td>
</tr>
<tr>
<td class = "draggable">
<div class = "droppable">Item 2</div>
</td>
</tr>
I want to make it available to swap cells.
Thanks!
I've written a little function to swap elements. Pass as arguments the parent (container of swapping elements), and two numbers (index) of the children elements that you want to be swapped.
var rowsParent = document.getElementById('sortRows');
var cellsParent = document.getElementById('sortCells');
swapElements(rowsParent,0,1);
swapElements(cellsParent,2,0);
function swapElements(parent,elemA,elemB){
//a little of validation
if(!parent||parent.constructor.toString().search('HTML')===-1) return;
var children = parent.children;
if(typeof elemA!=='number' || typeof elemB!=='number' || elemA===elemB || !children[elemA] || !children[elemB]) return;
elemB = elemA<elemB ? elemB--:elemB;
var childNumb = children.length - 1;
var a = parent.removeChild(children[elemA]);
var b = parent.removeChild(children[elemB]);
append(elemB,a);
append(elemA,b);
function append(a,b){
childNumb===a ? parent.appendChild(b) : parent.insertBefore(b,children[a]);
}
}
table, td {
border: solid 1px black;
padding: 3px;
margin: 15px;
}
<table>
<tbody id="sortRows">
<tr>
<td>a 1</td>
<td>a 2</td>
<td>a 3</td>
<td>a 4</td>
<td>a 5</td>
</tr>
<tr id="sortCells">
<td>b 1</td>
<td>b 2</td>
<td>b 3</td>
<td>b 4</td>
<td>b 5</td>
</tr>
<tr>
<td>c 1</td>
<td>c 2</td>
<td>c 3</td>
<td>c 4</td>
<td>c 5</td>
</tr>
<tr>
<td>d 1</td>
<td>d 2</td>
<td>d 3</td>
<td>d 4</td>
<td>d 5</td>
</tr>
<tr>
<td>e 1</td>
<td>e 2</td>
<td>e 3</td>
<td>e 4</td>
<td>e 5</td>
</tr>
</tbody>
</table>
You could do it in the same way you swap contents for any variable:
var child1_HTML = $("table tr:nth-child(1)").html();
$("table tr:nth-child(1)").html($("table tr:nth-child(2)").html());
$("table tr:nth-child(2)").html(child1_HTML);
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>