There is a bug
You need to run the script several times to remove all empty elements.
Two points
You say remove empty tags that contain ""
or a single space " "
. Does that include " "
or " "
two or more spaces. What about other white space characters?
Your element removal is order dependent because you use getElementsByTagName
which returns a live list.
Consider the html <p><span></span></p>
You first check all the p
tags which fail the empty test, then you test the span
tags which passes and you get <p></p>
which is, by your definition, empty and should have been removed.
On the other hand the html <span><p></p></span>
will first remove the p
then remove the span
.
The removal process is order dependent. Not what your question indicates.
Changes
For the first point you could use element.textContent
to check for empty elements. It will ignore the HTML and convert the
to a space for you. You could even use element.textContent.trim()
and thus get all blank elements (like the pseudo-class :blank
(Which has very limited support FF only))
This also covers the second point.
Example Mark and remove
To reduce the DOM calls you can mark and remove deleting the marked elements only.
const isNotMarked = el => {
while (el && el.parentNode && !el.parentNode.marked) {
el = el.parentNode;
if (el.marked) { return false }
}
return true;
}
[...document.querySelectorAll("span, p, strong")]
.filter(el => el.textContent.trim() === "" && isNotMarked(el) ? el.marked = true : false)
.forEach(el => el.parentNode.removeChild(el));
Example simple brute force
Mark and remove saves you attempting to delete already deleted elements but you may not care, as the shorter form, is a two liner, and thus could be argued to be the better solution.
document.querySelectorAll("span, p, strong")
.forEach(el => el.textContent.trim() === "" && el.parentNode.removeChild(el))
The following snippet shows the HTML after using your function and then the two example functions
/*=================================================================================
OP ver modified for example
=================================================================================*/
var ps = cleaned.getElementsByTagName('p'),
spans = cleaned.getElementsByTagName('span'),
strongs = cleaned.getElementsByTagName('strong');
for (let el of ps) { cleaner(el); }
for (let el of spans) { cleaner(el); }
for (let el of strongs) { cleaner(el); }
function cleaner(el) {
if (el.innerHTML == ' ' || el.innerHTML == '') {
el.parentNode.removeChild(el);
}
}
content.textContent = cleaned.innerHTML;
/*=================================================================================
Mark and remove
=================================================================================*/
const isNotMarked = el => {
while (el && el.parentNode && !el.parentNode.marked) {
el = el.parentNode;
if (el.marked) { return false }
}
return true;
}
[...cleanerClean.querySelectorAll("span, p, strong")]
.filter(el => el.textContent.trim() === "" && isNotMarked(el) ? el.marked = true : false)
.forEach(el => el.parentNode.removeChild(el));
contentA.textContent = cleanerClean.innerHTML;
/*=================================================================================
Brute force remove
=================================================================================*/
simplerClean.querySelectorAll("span, p, strong")
.forEach(el => el.textContent.trim() === "" && el.parentNode.removeChild(el))
contentB.textContent = simplerClean.innerHTML;
#content {
display: block;
}
<div id="cleaned" style="display:none;">
<p>text</p>
<p> </p>
<p>text</p>
<p><span>text</span></p>
<p><span></span></p>
<p>text</p>
<p><strong>text</strong></p>
<p></p>
<p> </p>
<p>text</p>
<p><span><strong> </strong></span></p>
<p><span><strong>text</strong></span></p>
<p> </p>
<p><span>text</span></p>
<p></p>
<p><span></span></p>
<p><span> </span></p>
<p><span><strong></strong></span></p>
<p>text</p>
</div>
<fieldset>
<legend>Original OPs script & Resulting HTML</legend>
<code id = "content"></code>
</fieldset>
<div id="cleanerClean" style="display:none;">
<p>text</p>
<p> </p>
<p>text</p>
<p><span>text</span></p>
<p><span></span></p>
<p>text</p>
<p><strong>text</strong></p>
<p></p>
<p> </p>
<p>text</p>
<p><span><strong> </strong></span></p>
<p><span><strong>text</strong></span></p>
<p> </p>
<p><span>text</span></p>
<p></p>
<p><span></span></p>
<p><span> </span></p>
<p><span><strong></strong></span></p>
<p>text</p>
</div>
<fieldset>
<legend>Mark and remove</legend>
<code id = "contentA"></code>
</fieldset>
<div id="simplerClean" style="display:none;">
<p>text</p>
<p> </p>
<p>text</p>
<p><span>text</span></p>
<p><span></span></p>
<p>text</p>
<p><strong>text</strong></p>
<p></p>
<p> </p>
<p>text</p>
<p><span><strong> </strong></span></p>
<p><span><strong>text</strong></span></p>
<p> </p>
<p><span>text</span></p>
<p></p>
<p><span></span></p>
<p><span> </span></p>
<p><span><strong></strong></span></p>
<p>text</p>
</div>
<fieldset>
<legend>Brute force remove</legend>
<code id = "contentB"></code>
</fieldset>
<p></p>
be a child element of#test
following execution of the code? Can you include the expected resulting HTML at the question? \$\endgroup\$