4

How can I write a javascript/jquery function that replaces text in the html document without affecting the markup, only the text content?

For instance if I want to replace the word "style" with "no style" here:

<tr>
<td style="width:300px">This TD has style</td>
<td style="width:300px">This TD has <span class="style100">style</span> too</td>
</tr>

I don't want the replacement to affect the markup, just the text content that is visible to the user.

dlamblin
  • 42,580
  • 20
  • 97
  • 135
Sylvain
  • 18,889
  • 22
  • 91
  • 142

2 Answers2

13

You will have to look for the text nodes on your document, I use a recursive function like this:

function replaceText(oldText, newText, node){ 
  node = node || document.body; // base node 

  var childs = node.childNodes, i = 0;

  while(node = childs[i]){ 
    if (node.nodeType == 3){ // text node found, do the replacement
      if (node.textContent) {
        node.textContent = node.textContent.replace(oldText, newText);
      } else { // support to IE
        node.nodeValue = node.nodeValue.replace(oldText, newText);
      }
    } else { // not a text mode, look forward
      replaceText(oldText, newText, node); 
    } 
    i++; 
  } 
}

If you do it in that way, your markup and event handlers will remain intact.

Edit: Changed code to support IE, since the textnodes on IE don't have a textContent property, in IE you should use the nodeValue property and it also doesn't implements the Node interface.

Check an example here.

Christian C. Salvadó
  • 769,263
  • 179
  • 909
  • 832
  • Thanks a lot @CMS, you helped me resolve this problem: http://stackoverflow.com/questions/1512053/how-to-force-breaking-of-non-breakable-strings/ – Sylvain Oct 03 '09 at 07:02
  • Watch out for 2011 builds of MSIE9 - they have node.textContent support, but if you try and assign a new value using `node.textContent = ...` then those builds crash the entire browser with **A problem with this website caused Internet Explorer to close**. Later builds (2012) seem OK. The workaround is to assign with `node.nodeValue = ...` inside the `if (node.textContent)` part of the loop regardless and just forget doing it with textContent. – jaygooby Oct 19 '12 at 13:21
  • Why won't you use `node.data` for all browsers? – gdoron is supporting Monica Nov 26 '12 at 02:36
4

Use the :contains selector to find elements with matching text and then replace their text.

$(":contains(style)").each(function() {
  for (node in this.childNodes) {
    if (node.nodeType == 3) { // text node
      node.textContent = node.textContent.replace("style", "no style");
    }
  }
});

Unfortunately you can't use text() for this as it strips out HTML from all descendant nodes, not just child nodes and the replacement won't work as expected.

cletus
  • 599,013
  • 161
  • 897
  • 938
  • 1
    Don't use a "for...in" to loop through an array-like-object.. a traditional for/while loop is much faster. – James Oct 03 '09 at 14:40