Reputation: 98
this is a continuation to this question the idea is still the same and the solution offered there would usually get the job done but after some testing i browsed into a site which bugged this solution see snippet
function censorWord(el, word) {
if (el.children.length > 0) {
Array.from(el.children).forEach(function(child){
censorWord(child, word)
})
} else {
if (el.innerText) {
el.innerText = el.innerText.replace(new RegExp(`\\b${word}\\b`, "g"), "***")
}
}
}
censorWord(document.getElementById("body"),'censor')
<body id="body">
<div>
example of where this solution fails to work censor<br/>
<strong>here it will work censor</strong>
</div>
<div>
example of where this solution works censor
</div>
</body>
the problem is that this solution assumes a HTML tag containing text necessarily has no child elements which isn't always the case i tried solving this by check text contained between triangle brackets but that was just a sloppy solution which would fail a lot due to elements without closing tags like <img>
<br/>
etc this has stumped me for a good while now
Upvotes: 0
Views: 846
Reputation: 370829
The problem with your current code is if the element has both children and text that may need to be replaced, the recursive el.children.length > 0
will be entered into, and the el.innerText
test section won't be.
Rather than else
, instead iterate over the text nodes of the el
parent regardless:
function censorWord(el, word) {
if (el.children.length > 0) {
Array.from(el.children).forEach(function(child) {
censorWord(child, word)
})
}
for (const child of el.childNodes) {
if (child.nodeType === 3) {
child.textContent = child.textContent.replace(new RegExp(`\\b${word}\\b`, "g"), "***");
}
}
}
censorWord(document.getElementById("body"), 'censor')
<body id="body">
<div>
example of where this solution fails to work censor<br/>
<strong>here it will work censor</strong>
</div>
<div>
example of where this solution works censor
</div>
</body>
Another approach using a TreeWalker instead:
const getTextNodes = (parent) => {
const walker = document.createTreeWalker(
parent,
NodeFilter.SHOW_TEXT,
null,
false
);
let node;
const textNodes = [];
while(node = walker.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
function censorWord(el, word) {
const regex = new RegExp(`\\b${word}\\b`, "g");
for (const textNode of getTextNodes(el)) {
textNode.textContent = textNode.textContent.replace(regex, "***");
}
}
censorWord(document.getElementById("body"), 'censor')
<body id="body">
<div>
example of where this solution fails to work censor<br/>
<strong>here it will work censor</strong>
</div>
<div>
example of where this solution works censor
</div>
</body>
Upvotes: 1