Reputation: 139
This is sort of a continuation of an earlier question.
I have some html.
<h3>things that are red:</h3>
<ul>
<li><a href="html://www.redapples.com">Red Apples</a></li>
<li><a href="html://www.redmeat.com">Red Meat</a></li>
<li><a href="html://www.redcar.com">All Red Cars</a></li>
</ul>
I want to use javascript to wrap all of the text elements with a element
The result I am looking for.
<h3>things that are <span class="red">red</span>:</h3>
<ul>
<li><a href="html://www.redapples.com"><span class="red">Red</span> Apples</a></li>
<li><a href="html://www.redmeat.com"><span class="red">Red</span> Meat</a></li>
<li><a href="html://www.redcar.com">All <span class="red">Red</span> Cars</a></li>
</ul>
After a lot of thought I realized that I had to distinguish between text nodeTypes and Element NodeTypes while navigating the DOM. I used some of the feedback from my earlier question, and wrote this little script.
function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
walkTheDOM(document.body, function (node) {
// Is it a Text node?
if (node.nodeType === 3) {
var text = node.data.trim();
// Does it have non white-space text content?
if (text.length > 0) {
node.data = text.replace(/(RED)/gi, '<span class="red">$1</span>');
}
}
});
This does pretty much what I want it to do, except the output is text rather than html. So my question is this, is there an easy way to fix this line
node.data = text.replace(/(RED)/gi, '<span class="red">$1</span>');
So that the output is html?
Upvotes: 7
Views: 11312
Reputation: 29
The answer from Amadan is perfect to manipulate (convert from textnode to element for css purposes) for instance some VAT-text-information in the cart-totals section on the cart page in the storefront-child-theme in woocommerce in wordpress. There is no other usual or better way of changing this little piece of textnode that lies within a div containing different textnodes and elementnodes. So i wanted to say thanks for this solution.
Of course, this only works if you use only one language.
var cart_totals = document.querySelector(".cart_totals .shop_table");
if( !!cart_totals ) {
for( var x = 0; x < cart_totals.childNodes.length; x++ ) {
if( cart_totals.childNodes[x].nodeType === 3 &&
cart_totals.childNodes[x].nodeValue.includes( 'inkl. 19 % MwSt.' ) ) {
var textNode = cart_totals.childNodes[x];
wrapTextNode( textNode ); /* see function from Amadan above */
}
}
}
Upvotes: 0
Reputation: 198334
What you need to do to make this generic, as I have hinted in the comment, is to create a new element, put the text node into it, then replace the text node with the new element.
function wrapTextNode(textNode) {
var spanNode = document.createElement('span');
spanNode.setAttribute('class', 'red');
var newTextNode = document.createTextNode(textNode.textContent);
spanNode.appendChild(newTextNode);
textNode.parentNode.replaceChild(spanNode, textNode);
}
[].forEach.call(document.querySelectorAll('a'), function(el) {
var textNode = el.childNodes[0];
wrapTextNode(textNode);
});
EDIT: fiddle
Upvotes: 7