Here's a modern native JavaScript solution; which is actually quite similar to the jQuery one from 2010. I adapted it from that answer for a project that I am working on, and thought I would share it here.
document.querySelectorAll("*:empty").forEach((x)=>{x.remove()});
document.querySelectorAll returns a NodeList; which is essentially an array of all DOM nodes which match the CSS selector given to it as an argument.
*:empty is a selector which selects all elements (* means "any element") that is empty (which is what :empty means).
This will select any empty element within the entire document, if you only wanted to remove any empty elements from within a certain part of the page (i.e. only those within some div element); you can add an id to that element and then use the selector #id *:empty, which means any empty element within the element with an id of id.
This is almost certainly what you want. Technically some important tags (e.g. <meta> tags, <br> tags, <img> tags, etc) are "empty"; so without specifying a scope, you will end up deleting some tags you probably care about.
forEach loops through every element in the resulting NodeList, and runs the anonymous function (x)=>{x.remove()} on it. x is the current element in the list, and calling .remove() on it removes that element from the DOM.
Hopefully this helps someone. It's amazing to see how far JavaScript has come in just 8 years; from almost always needing a library to write something complex like this in a concise manner to being able to do so natively.
Edit
So, the method detailed above will work fine in most circumstances, but it has two issues:
- Elements like
<div> </div> are not treated as :empty (not the space in-between). CSS Level 4 selectors fix this with the introduction of the :blank selector (which is like empty except it ignores whitespace), but currently only Firefox supports it (in vendor-prefixed form).
- Self-closing tags are caught by
:empty - and this will remain the case with :blank, too.
I have written a slightly larger function which deals with these two use cases:
document.querySelectorAll("*").forEach((x)=>{
let tagName = "</" + x.tagName + ">";
if (x.outerHTML.slice(tagName.length).toUpperCase() == tagName
&& /[^\s]/.test(x.innerHTML)) {
x.remove();
}
});
We iterate through every element on the page. We grab that element's tag name (for example, if the element is a div this would be DIV, and use it to construct a closing tag - e.g. </DIV>.
That tag is 6 characters long. We check if the upper-cased last 6 characters of the elements HTML matches that. If it does we continue. If it doesn't, the element does't have a closing tag, and therefore must be self-closing. This is preferable over a list, because it means you don't have to update anything should a new self-closing tag get added to the spec.
Then, we check if the contents of the element contain any whitespace. /[^\s]/ is a RegEx. [] is a set in RegEx, and will match any character that appears inside it. If ^ is the first element, the set becomes negated - it will match any element that is NOT in the set. \s means whitespace - tabs, spaces, line breaks. So what [^\s] says is "any character that is not white space".
Matching against that, if the tag is not self-closing, and its contents contain a non-whitespace character, then we remove it.
Of course, this is a bit bigger and less elegant than the previous one-liner. But it should work for essentially every case.