I am trying to remove certain children from DOM by iterating through them. I have a table, and I am looping through the children and removing some like this:
for (const row of table.children) {
const testRow = row.children[2];
if (testRow.textContent === "false") {
table.removeChild(row);
}
}
When this code is run, some of the children that meet the condition testRow.textContent === "false"
are removed, but other rows that meet the condition are not removed.
Logging out t.children.lengh
as the loop goes on reveals that the size is decreasing (due to the children being removed). My theory is that removing a child messes up the iterator. I tried rewriting this code as a for loop and got the same result: some children are "skipped over" and not removed.
Ideas on how to remove certain children of a DOM element (based on dynamically pulled data)?
CodePudding user response:
This uses deleteRow
taking the index of the row and using a traditional for
loop iterating backwards from the end of the table rows to the beginning, it also utilizes the rows
property of the table element:
window.addEventListener('DOMContentLoaded', function(){
const tbl = document.querySelector('table');
for(let a = tbl.rows.length - 1; a >= 0; a--){
//you can modify this next condition to match what you want
if(tbl.rows[a].cells[0].textContent === "Even"){
tbl.deleteRow(a);
}
}
});
<table>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
<tr><td>Even</td></tr>
<tr><td>Odd</td></tr>
</table>
CodePudding user response:
This solution I found works for all DOM types (vs just tables in Ryan Wilson's answer). Just decrement the index each time a DOM object is removed so that it will re-run the loop for the new i
th element (because the previous will have been removed):
for (let i = 0; i < t.children.length; i ) {
const row = table.children[i];
const testRow = row.children[2];
if (testRow.textContent === "false") {
table.removeChild(row);
i--;
}
}
CodePudding user response:
If the elements are empty, you could use the css :empty
selector:
setTimeout(()=>{
for (let el of document.querySelectorAll('tr td:empty')){
el.remove()
}
},2000)
td {border: 1px solid black;height:30px;}
<table>
<tr><td>text</td></tr>
<tr><td></td></tr>
<tr><td>text</td></tr>
<tr><td></td></tr>
</table>
CodePudding user response:
table.children
is an HTMLCollection; it gets updated as you change the document. You can convert it to a normal array so that changes to the DOM won't affect the contents that you're iterating through.
In the example below I've used the spread operator (...
) to create the new array, but you could use Array.from instead e.g. Array.from(tbody.children)
.
const tbody = document.querySelector('table tbody');
console.log(tbody.children.constructor.name);
for (const row of [...tbody.children]) {
const testRow = row.children[1];
if (testRow.textContent === "false") {
tbody.removeChild(row);
}
}
<table>
<tbody>
<tr><td>A</td><td>true</td></tr>
<tr><td>B</td><td>false</td></tr>
<tr><td>C</td><td>false</td></tr>
<tr><td>D</td><td>true</td></tr>
<tr><td>E</td><td>false</td></tr>
<tr><td>F</td><td>true</td></tr>
</tbody>
</table>
Because [...tbody.children]
is a normal array you can use functions like filter and forEach if you like, e.g.:
const tbody = document.querySelector('table tbody');
[...tbody.children]
.filter((row) => row.cells[1].textContent === "false")
.forEach((row) => { tbody.removeChild(row); });
<table>
<tbody>
<tr><td>A</td><td>true</td></tr>
<tr><td>B</td><td>false</td></tr>
<tr><td>C</td><td>false</td></tr>
<tr><td>D</td><td>true</td></tr>
<tr><td>E</td><td>false</td></tr>
<tr><td>F</td><td>true</td></tr>
</tbody>
</table>