Deleting Empty Rows/Nodes - javascript

I am trying to delete the empty rows in a table. I traversed to those empty rows. But I don't know how to delete that particular node. I tried to traverse to the parent node and delete, but somehow it seems to show error.
empr[e].removeChild(empr[e].rows[et]) I used this inside the for loop
function emptyrows() {
var count = 0;
var empr = document.getElementsByClassName("tide");
var emlen = document.getElementsByClassName("tide").length;
alert(emlen);
for (var e = 0; e < emlen; e++) {
var emtab = empr[e].rows.length;
for (var et = 0; et < emtab; et++) {
if (empr[e].rows[et].innerHTML == "") {
} else {
console.log("Nothing Empty");
}
}
}
}
<table>
<tbody>
<tr>
<td>1</td>
<td>Barry</td>
<td>
<table class="tide">
<tr>50</tr>
<tr>10</tr>
<tr>200</tr>
</table>
</td>
</tr>
<tr>
<td>2</td>
<td>Allen</td>
<td>
<table class="tide">
<tr>50</tr>
<tr></tr>
<tr></tr>
</table>
</td>
</tr>
<tr>
<td>3</td>
<td>Mary</td>
<td>
<table class="tide">
<tr>50</tr>
<tr>20</tr>
<tr></tr>
</table>
</td>
</tr>
</tbody>
</table>

Try the below code, however you need to correct your HTML to be semantic (include inside ). But the code below should give you the general idea on how to proceed:
function emptyrows() {
var tables = document.getElementsByClassName("tide");
for (var i = 0; i < tables.length; i++) {
for (var j = 0; j < tables[i].childNodes.length; j++) {
if (tables[i].childNodes[j].innerHTML === '') {
tables[i].removeChild(tables[i].childNodes[j]);
}
}
}
}
emptyrows();

Related

Re-order table columns in HTML dynamically with Javascript

I've a table in HTML looks like this:
Subjects
n1
n2
n3
subject1
10
0
0
subject2
0
5
20
<table>
<thead>
<tr>
<th class="subject">Subjects</th>
<th>n1</th>
<th>n2</th>
<th>n3</th>
</tr>
</thead>
<tbody>
<tr>
<th class="subject">subject1</th>
<td>10</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th class="subject">subject2</th>
<td>0</td>
<td>5</td>
<td>20</td>
</tr>
</tbody>
</table>
Is there any thought or approach with javascript I could re-order columns in a specific order let order = ['n2','n1','n3']:
Subjects
n2
n1
n3
subject1
0
10
0
subject2
5
0
20
I've solved by turning the table into 2-dimensional array and sort it and turn it back into table HTML:
function tableToArray(tbl, opt_cellValueGetter) {
opt_cellValueGetter = opt_cellValueGetter || function(td) {
return td.textContent || td.innerText;
};
var twoD = [];
for (var rowCount = tbl.rows.length, rowIndex = 0; rowIndex < rowCount; rowIndex++) {
twoD.push([]);
}
for (var rowIndex = 0, tr; rowIndex < rowCount; rowIndex++) {
var tr = tbl.rows[rowIndex];
for (var colIndex = 0, colCount = tr.cells.length, offset = 0; colIndex < colCount; colIndex++) {
var td = tr.cells[colIndex],
text = opt_cellValueGetter(td, colIndex, rowIndex, tbl);
while (twoD[rowIndex].hasOwnProperty(colIndex + offset)) {
offset++;
}
for (var i = 0, colSpan = parseInt(td.colSpan, 10) || 1; i < colSpan; i++) {
for (var j = 0, rowSpan = parseInt(td.rowSpan, 10) || 1; j < rowSpan; j++) {
twoD[rowIndex + j][colIndex + offset + i] = text;
}
}
}
}
return twoD;
}
let order = ['n2', 'n1', 'n3', "Subjects"];
const sort2dArrayColumsByFirstRow = (array) => {
if (!Array.isArray(array)) return [];
const sortedFirstRow = array[0]
.map((item, i) => ({
v: item,
i: i
}))
.sort((a, b) => {
return order.indexOf(a.v) - order.indexOf(b.v);
});
return array.map((row) => row.map((_, i) => row[sortedFirstRow[i].i]));
};
function arrayToTable(columnNames, dataArray) {
var myTable = document.createElement('table');
var y = document.createElement('tr');
myTable.appendChild(y);
for (var i = 0; i < columnNames.length; i++) {
var th = document.createElement('th'),
columns = document.createTextNode(columnNames[i]);
th.appendChild(columns);
y.appendChild(th);
}
for (var i = 0; i < dataArray.length; i++) {
var row = dataArray[i];
var y2 = document.createElement('tr');
for (var j = 0; j < row.length; j++) {
myTable.appendChild(y2);
var th2 = document.createElement('td');
var date2 = document.createTextNode(row[j]);
th2.appendChild(date2);
y2.appendChild(th2);
}
}
document.querySelector('#tableEl').innerHTML = myTable.innerHTML;
}
let arr = tableToArray(document.querySelector('#tableEl'))
console.log('before:', arr)
let arrOrdered = sort2dArrayColumsByFirstRow(arr);
console.log('after:', arrOrdered);
arrayToTable(arrOrdered[0], arrOrdered.slice(1))
<table id="tableEl">
<thead>
<tr>
<th class="subject">Subjects</th>
<th>n1</th>
<th>n2</th>
<th>n3</th>
</tr>
</thead>
<tbody>
<tr>
<th class="subject">subject1</th>
<td>10</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th class="subject">subject2</th>
<td>0</td>
<td>5</td>
<td>20</td>
</tr>
</tbody>
</table>
This is a good DOM question.
Tables are modified by the TABLE API.
https://html.spec.whatwg.org/multipage/tables.html
The TABLE element has THEAD, TFOOT, and TBODY elements. Use of these elements provides structure for your javascript. (Good job so far).
<table id="s-table">
<thead>
<tr>
<th class="subject">Subjects</th>
<th>n1</th>
<th>n2</th>
<th>n3</th>
</tr>
</thead>
<tbody>
<tr>
<th class="subject">subject1</th>
<td>10</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th class="subject">subject2</th>
<td>0</td>
<td>5</td>
<td>20</td>
</tr>
</tbody>
</table>
Next, you'll need some javascript.
You'll also find insertBefore, and possibly before, and after Element methods handy.
https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore
Get the TBODY element.
For each row, reorder(cell[i], cell[j]).
Let's start with
function resortTBody(tBody) {
const rows = tBody.rows;
for(let i = 0; i < tBody.rows.length; i++) {
reorderRow(rows[i]);
}
}
function reorderRow(row) {
let cells = row.cells;
row.insertBefore(cells[2], cells[1]);
}
This code has a hard-coded swap of cells. To reorder the cells to match a specific order, you'll need to modify reorderRow:
reorderRow(row, newOrder);
The TH's can be similarly reordered.
Design Notes: It's a good idea to minimize scope of identifiers. That is, put them in scope only as broad as it can be maximally justified.
If reorderRow is only needed for resortTbody, it can be restricted to private access.
let resortTBody = function(tBody) {
function resortTBodyInner(tBody) {
const rows = tBody.rows;
for(let i = 0; i < tBody.rows.length; i++) {
reorderRow(rows[i]);
}
}
function reorderRow(row) {
let cells = row.cells;
row.insertBefore(cells[2], cells[1]);
}
resortTBodyInner(tBody);
resortTBody = resortTBodyInner;
};
It might be desirable to maintain the column headers but resort their contents. That would require a subtle change to the approach.
It might be desirable to reset the table to its original state. All of that can be done.
The following one-liner will reorganize the columns in the desired order:
document.querySelectorAll("#tableEl tr").forEach(tr=>[...tr.children].forEach((_,i,a)=>tr.append(a[[0,2,1,3][i]])));
<table id="tableEl">
<thead>
<tr>
<th class="subject">Subjects</th>
<th>n1</th>
<th>n2</th>
<th>n3</th>
</tr>
</thead>
<tbody>
<tr>
<th class="subject">subject1</th>
<td>10</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<th class="subject">subject2</th>
<td>0</td>
<td>5</td>
<td>20</td>
</tr>
</tbody>
</table>

table collapse (rows hide) not working Javascript

function tablecollapse()
{
var table = document.getElementById(tblbatting);
var rowCount = table.rows.length;
for(var i=4; i< rowCount; i++)
{
var row = table.rows[i];
row.display="none";
}
}
I have this code running onload() but the table's connect aren't hiding.
What is wrong with this code? or any other suggestions?
What Wayne said. He was a lot faster than me.
function tablecollapse(id) {
var table = document.getElementById(id);
var rows = table.rows;
for (var i = 4; i < rows.length; i++) {
rows[i].style.display = "none";
}
}
<link href="//cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" />
<table id="foo">
<thead>
<td>Column</td>
<td>Column</td>
</thead>
<tr>
<td>one</td>
<td>one</td>
</tr>
<tr>
<td>two</td>
<td>two</td>
</tr>
<tr>
<td>three</td>
<td>three</td>
</tr>
<tr>
<td>four</td>
<td>four</td>
</tr>
<tr>
<td>five</td>
<td>five</td>
</tr>
</table>
<button onclick="tablecollapse('foo')">Collapse</button>
There are two errors in the code. First, you need to put quotes around the table element id in var table = document.getElementById(tblbatting);. So, this code becomes var table = document.getElementById("tblbatting");.
Second, to set the display style, you need to access the style property of the table row element. So row.display="none"; becomes row.style.display="none";.
var table = document.getElementById("tblbatting");
var rowCount = table.rows.length;
for(var i=4; i< rowCount; i++)
{
var row = table.rows[i];
row.style.display="none";
}
I am not sure if you have done it deliberately or not, but you should be aware that your code will not hide the first 4 rows of the table because you have used var i=4 to initialise your loop counter.

Multi-Filterable Table with JS

I've managed to modify a filterable table JS script to my liking, but I'd like to make it more advanced by making it so I can filter from the remaining filtered items, but I'm not quite sure how to go about it.
Here's a jsfiddle with a similar setup to what I have. The code has gotten significantly more messy since I started messing with it, you might be able to see that I was trying to use the form to ensure we didn't start overwriting our display:none's but then I realised I was a bit over my head.
To clarify, what I'd like to be able to do is to filter say, the name, and then filter that remaining list even further, say, by type.
Is there an efficient way of doing this that I'm completely missing?
Here is the original filter code which was much cleaner before I messed with it:
function filter(term, _id, cellNr){
var suche = term.value.toLowerCase();
var table = document.getElementById(_id);
var ele;
for (var r = 2; r < table.rows.length; r++){
ele = table.rows[r].cells[cellNr].innerHTML.replace(/<[^>]+>/g,"");
if (ele.toLowerCase().indexOf(suche)>=0 )
table.rows[r].style.display = '';
else table.rows[r].style.display = 'none';
}
}
I myself would do it like this:
function filter(_id) {
var table = document.getElementById(_id);
//get all filters.
var getFilters = [table.querySelectorAll("input")[0].value.toLowerCase(),
table.querySelectorAll("input")[1].value.toLowerCase(),
table.querySelectorAll("input")[2].value.toLowerCase()]
for (var r = 2; r < table.rows.length; r++)
{
//strip tags
var el1 = table.rows[r].cells[0].innerHTML.replace(/<[^>]+>/g, "");
var el2 = table.rows[r].cells[1].innerHTML.replace(/<[^>]+>/g, "");
var el3 = table.rows[r].cells[2].innerHTML.replace(/<[^>]+>/g, "");
var search1 = el1.toLowerCase().indexOf(getFilters[0]);
var search2 = el2.toLowerCase().indexOf(getFilters[1]);
var search3 = el3.toLowerCase().indexOf(getFilters[2]);
//test all searches, if found or el = empty display
if ( (search1 >= 0 || el1 == "" ) && (search2 >= 0 || el2 == "" ) && (search3 >= 0 || el3 == "" )) {
table.rows[r].style.display = '';
} else {
table.rows[r].style.display = 'none';
}
}
}
<table id="table">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
<tr>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Grass</td>
</tr>
<tr>
<td>4</td>
<td>Charmander</td>
<td>Fire</td>
</tr>
<tr>
<td>7</td>
<td>Squirtle</td>
<td>Water</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Poison</td>
</tr>
<tr>
<td>6</td>
<td>Charizard</td>
<td>Flying</td>
</tr>
</table>
First, get all input from the input boxes. Store it in an array called getFilters.
Then:
search the different cells and see if there is a match.
When there is a match (Not -1) or when the input of that column was empty return true. All three conditions need to return true for a row to show. If not hide the row.
So id : 1 with the two other inputs empty would yield only bulbasaur with two types.
The real trick here are the conditions:
(search1 >= 0 || el1 == "" ) && (search2 >= 0 || el2 == "" )
They are divided into blocks with the parentheses. The will evaluate seperately into true or false. There are three of those blocks in this example.
This was answered quite a while ago and it deservers a more modern approach:
function filter(_id) {
const table = document.getElementById(_id);
//get all filters
const getFilters = [...table.querySelectorAll("input")].map(element => element.value.toLowerCase());
document.querySelectorAll("tr:nth-child(n+3) > td:first-child").forEach((firstTD) => {
//iterate to the next to td's using nextSibling
const textArray = [firstTD.textContent, firstTD.nextElementSibling.textContent, firstTD.nextElementSibling.nextElementSibling.textContent];
const found = textArray.every((element, index) => {
return element.toLowerCase().indexOf(getFilters[index]) >= 0 || getFilters[index] == "";
});
if (found) {
firstTD.parentElement.style.display = '';
} else {
firstTD.parentElement.style.display = 'none';
}
});
}
<table id="table">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
<tr>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Grass</td>
</tr>
<tr>
<td>4</td>
<td>Charmander</td>
<td>Fire</td>
</tr>
<tr>
<td>7</td>
<td>Squirtle</td>
<td>Water</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Poison</td>
</tr>
<tr>
<td>6</td>
<td>Charizard</td>
<td>Flying</td>
</tr>
</table>

Iterate over table cells, re-using rowspan values

I have a simple HTML table, which uses rowspans in some random columns. An example might look like
A | B |
---|---| C
D | |
---| E |---
F | | G
I'd like to iterate over the rows such that I see rows as A,B,C, D,E,C, then F,E,G.
I think I can probably cobble together something very convoluted using cell.index() to check for "missed" columns in later rows, but I'd like something a little more elegant...
without jquery:
function tableToMatrix(table) {
var M = [];
for (var i = 0; i < table.rows.length; i++) {
var tr = table.rows[i];
M[i] = [];
for (var j = 0, k = 0; j < M[0].length || k < tr.cells.length;) {
var c = (M[i-1]||[])[j];
// first check if there's a continuing cell above with rowSpan
if (c && c.parentNode.rowIndex + c.rowSpan > i) {
M[i].push(...Array.from({length: c.colSpan}, () => c))
j += c.colSpan;
} else if (tr.cells[k]) {
var td = tr.cells[k++];
M[i].push(...Array.from({length: td.colSpan}, () => td));
j += td.colSpan;
}
}
}
return M;
}
var M = tableToMatrix(document.querySelector('table'));
console.table(M.map(r => r.map(c => c.innerText)));
var pre = document.createElement('pre');
pre.innerText = M.map(row => row.map(c => c.innerText).join('\t')).join('\n');
document.body.append(pre);
td {
border: 1px solid rgba(0,0,0,.3);
}
<table>
<tr>
<td colspan=2>A</td>
<td rowspan=2>B</td>
</tr>
<tr>
<td>C</td>
<td rowspan=3>D</td>
</tr>
<tr>
<td rowspan=2>E</td>
<td rowspan=4>F</td>
</tr>
<tr></tr>
<tr>
<td rowspan=2 colspan=2>G</td>
</tr>
<tr></tr>
<tr>
<td rowspan=3 colspan=3>H</td>
</tr>
<tr></tr>
<tr></tr>
<tr>
<td colspan=3>I</td>
</tr>
</table>
Try this:
<table id="tbl">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td colspan="2" rowspan="2">A</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
Script:
var finalResult = '';
var totalTds = $('#tbl TR')[0].length;
var trArray = [];
var trArrayValue = [];
var trIndex = 1;
$('#tbl TR').each(function(){
var currentTr = $(this);
var tdIndex = 1;
trArray[trIndex] = [];
trArrayValue[trIndex] = [];
var tdActuallyTraversed = 0;
var colspanCount = 1;
$('#tbl TR').first().children().each(function(){
if(trIndex > 1 && trArray[trIndex - 1][tdIndex] > 1)
{
trArray[trIndex][tdIndex] = trArray[trIndex - 1][tdIndex] - 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex - 1][tdIndex];
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
else
{
if(colspanCount <= 1)
{
colspanCount = currentTr.children().eq(tdActuallyTraversed).attr('colspan') != undefined ? currentTr.children().eq(tdActuallyTraversed).attr('colspan') : 1;
}
if(colspanCount > 1 && tdIndex > 1)
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex][tdIndex - 1];
colspanCount--;
}
else
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).html();
tdActuallyTraversed++;
}
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
tdIndex++;
});
trIndex++;
});
alert(finalResult);
Fiddle
i am not sure about the performance, but it works well.
what I understood with your question is: You want to split the merged cell with same value and then iterate the table simply by row.
I've created a JSFiddle that will split the merged cells with the same value. Then you'll have a table that can be iterated simply by rows to get the desired output that you specified.
See it running here http://jsfiddle.net/9PZQj/3/
Here's the complete code:
<table id="tbl" border = "1">
<tr>
<td>A</td>
<td>B</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td>D</td>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
<br>
<div id="test"> </div>
Here's the jquery that is used to manipulate the table's data.
var tempTable = $('#tbl').clone(true);
var tableBody = $(tempTable).children();
$(tableBody).children().each(function(index , item){
var currentRow = item;
$(currentRow).children().each(function(index1, item1){
if($(item1).attr("rowspan"))
{
// copy the cell
var item2 = $(item1).clone(true);
// Remove rowspan
$(item1).removeAttr("rowspan");
$(item2).removeAttr("rowspan");
// last item's index in next row
var indexOfLastElement = $(currentRow).next().last().index();
if(indexOfLastElement <= index1)
{
$(currentRow).next().append(item2)
}
else
{
// intermediate cell insertion at right position
$(item2).insertBefore($(currentRow).next().children().eq(index1))
}
}
});
console.log(currentRow)
});
$('#test').append(tempTable);
You can use this Gist. It supports all the requirements by W3C, even "rowspan=0" (which seems to be only supported by Firefox).

How to count number of disabled columns in a table from javascript?

My table format is
<table class"tabletop">
<tr>
<td>
<table>
<tr>
<td id="mycol1"></td>
<td id="mycol2"></td>
<td id="mycol3"></td>
<td id="mycol4"></td>
</tr>
</table>
</td>
</tr>
<tr>
</tr>
</table>
I have to count columns(having id's) that are not disabled(or their display is not none) and that are disabled.
Suppose columns disabled are 4 and not disabled are 2.
So it must return disabled: 4 and not disabled: 2
td's are disabled by their id.
eg
mycol1.style.display="none";
Working Solution try this
<script type = "text/javascript" language = "javascript">
function getHiddenColumnCount() {
var tbl = document.getElementById("myTbl");
var HiddenColumnCount = 0;
for(var OuterCounter = 0 ; OuterCounter < tbl.rows.length ; OuterCounter++)
{
for(var InnerCounter = 0 ; InnerCounter < tbl.rows[OuterCounter].cells.length;InnerCounter++)
{
if (tbl.rows[OuterCounter].cells[InnerCounter].style.display == "none")
HiddenColumnCount++;
}
}
alert("There are " + HiddenColumnCount + " Hidden Columns in Table");
}
</script>
You can use
$('table td:visible').length
Try this: fiidle
<table border="1" id="myTbl">
<tr>
<td class="mycol1">
1
</td>
<td class="mycol2">
2
</td>
<td class="mycol3">
3
</td>
<td class="mycol4">
4
</td>
</tr>
</table>
<script>
function hideColumn(columnClass) {
var els = document.getElementsByClassName(columnClass);
for (var i = 0; i < els.length; i++) {
els[i].style.display = "none";
}
}
hideColumn('mycol1');
hideColumn('mycol2');
function getHiddenColumnsCount() {
var rows = document.getElementById('myTbl').rows;
var count = 0;
for (var i = 0; i < rows.length; i++) {
for (var j = 0; j < rows[i].cells.length; j++) {
if (rows[i].cells[j].style.display == "none")
count++;
}
}
alert(count);
}
getHiddenColumnsCount();
</script>
First of all you should use class for defining column instead of id as id should not be duplicate & to define a column we will have to give similar id to all cells of a column.

Categories

Resources