Winter
Winter

Reputation: 2517

Remove empty node elements from DOM

What is the best way to identify and remove empty elements from the dom WITHOUT jQuery?

If I have code that looks like this:

<div>
    <div>
        <p></p>
    </div>
    <div>
        <p>Some content</p>
    </div>
</div>

What is the best way to get rid of both the empty <p> and <div>?

I tried this: https://www.sitepoint.com/removing-useless-nodes-from-the-dom/ but for some reason it cleaned out all of the spaces in my spans.

Upvotes: 2

Views: 5025

Answers (4)

vsync
vsync

Reputation: 130215

I would use TreeWalker to traverse the DOM and find empty nodes.

In the below example, an empty node is considered as a node which has no text anywhere inside it, regardless of tree depth or whitespaces. A node can also be considered empty only if it has no children.

var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT)
var currentNode = treeWalker.currentNode
var emptyNodes = []

// test if a node has no text, regardless of whitespaces
var isNodeEmpty = node => !node.textContent.trim()

// find all empty nodes
while(currentNode) {
  isNodeEmpty(currentNode) && emptyNodes.push(currentNode)
  currentNode = treeWalker.nextNode()
}

// remove found empty nodes
emptyNodes.forEach(node => node.parentNode.removeChild(node))

// print DOM
console.log(document.body.firstElementChild.outerHTML)
<div>
    <div>
        <p></p>
    </div>
    <div>
        <p>Some content <a></a></p>
    </div>
</div>

Note that while traversing the DOM using TreeWalker, nodes should not be removed because it will mess up the "live" DOM iteration.

Upvotes: 3

guest271314
guest271314

Reputation: 1

You can use :only-child pseudo class to select elements which are the only child of a parent element, remove the node, then check if the parent node has .children, if not remove the parent node

let nodes = document.querySelector("div")
            .querySelectorAll(":only-child");

nodes.forEach(node => {
  if (!node.childNodes.length) {
    let parent = node.parentNode;
    node.parentNode.removeChild(node);
    if (!parent.children.length) {
      parent.parentNode.removeChild(parent)
    }
  }
});

console.log(document.querySelector("div").innerHTML);
<div>
    <div>
        <p></p>
    </div>
    <div>
        <p>Some content</p>
    </div>
</div>

Upvotes: 0

Kramb
Kramb

Reputation: 1092

You could use querySelectorAll to get all of the elements of a type and then determine if that element has innerText.

var wrapper = document.querySelector('.content-wrapper');

var ps = wrapper.querySelectorAll('p');

for (var p = 0; p < ps.length; p++) {
  if (ps[p].innerText === '') {
    wrapper.removeChild(ps[p].parentNode);
  }
}

var divs = wrapper.querySelectorAll('div');

for (var d = 0; d < divs.length; d++) {
  if (divs[d].querySelectorAll('p').length === 0) {
    wrapper.removeChild(divs[d]);
  }
}
.content-wrapper {
  height: 100%;
  width: 100%;
}

.content-wrapper>div {
  width: 100%;
  height: 100px;
  color: #fff;
}
<div class="content-wrapper">
  <div style="background-color: red;">
  </div>
  <div style="background-color: green;">
    <p>
    </p>
  </div>
  <div style="background-color: blue;">
    <p>
      Text!
    </p>
  </div>
</div>

Upvotes: 0

Zenoo
Zenoo

Reputation: 12880

You could try to build a recursive function checking if an element's innerHTML is empty to remove them all :

function recursiveCleaner(el){
  if (el.childNodes.length > 0) {
    for(var i=0;i<el.childNodes.length;i++){
      recursiveCleaner(el.children[i]);
    }
    for(var i=0;i<el.childNodes.length;i++){
      if(el.children[i].innerHTML === "") el.removeChild(el.children[i]);
    }
  }
}

Upvotes: 0

Related Questions