7

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?

4
  • 1
    node.innerHTML = text.replace... Shouldn't have been very difficult to research this yourself Commented Sep 16, 2014 at 1:35
  • @charlietfl: That will break for <li>&gt;/li&lt; bug</li>, for example, unless you make sure to reencode things. document.createElement way is safer (but definitely more cumbersome). Commented Sep 16, 2014 at 1:37
  • 1
    Are you sure the innerHTML is an object at this level? I've tried adding it, and I get nothing. Commented Sep 16, 2014 at 1:45
  • 1
    @charlieftl Yeah, innerHTML isn't a property of Text Objects. So the question remains, is there an easy way convert a text object into an html object. Commented Sep 16, 2014 at 1:53

3 Answers 3

7

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

Sign up to request clarification or add additional context in comments.

2 Comments

Great. How to do its reverse? That is, replace span.red with its text node.
Per SO rules, one question per question. So, make a new question; feel free to link it here if you want. But basically, run cloneNode on the text node, use after to insert it after its parentNode, then remove the old parent node.
0

DEMO

$(function() {
    $('li a').html(function(i,html) {
        return html.replace(/(RED)/gi, '<span class="red">$1</span>');
    });
});

3 Comments

I would love to go that way, but I for a lot of reasons I need to go only on text elements which means I can't narrow it down by 'li a' selectors. And if I just go straight to the 'li' selector the replace wraps the 'red' in the href link which is why I resorted to using the DOM.
If you intend to manipulate text nodes just bear in mind that the result will always be text; therefore any tags would < and > would be escaped, as is the case in your result. To manipulate HTML you have to start off with HTML.
Thanks. That's the best answer so far. I was beginning to suspect that.
0

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 */
    }
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.