Scott Mckay
Scott Mckay

Reputation: 21

Issue with replacing color of word occurrence

I am trying to replace the font color of "bad" throughout an HTML page to red but there are two main issues, firstly it repeats the entire sentence before replacement and I do not want that and also the bad in "badly" was also replaced but I just want only the font in the word "bad" replaced. How do I go about this? Here is my code

 window.onload = function() {
   var text = document.getElementById("content");
   var str = text.innerHTML,
     reg = /red/ig;

   var toStr = String(reg);
   var color = (toStr.replace('\/g', '|')).substring(1);

   var colors = color.replace("|");

   if (colors.indexOf("red") > -1) {
     str = str.replace(/bad/g, '<span style="color:red;">bad</span>');
   }


   document.getElementById("updated").innerHTML = str;
 }
<div id="content">are they good or bad, tell me how badly it is if bad.</div>
<div id="updated"></div>

Upvotes: 1

Views: 81

Answers (2)

le_m
le_m

Reputation: 20228

To replace text in HTML documents, it is a good idea to traverse the DOM tree and replace the content of text nodes where needed.

To match complete words only ("bad" but not "badly"), use regex with word boundary anchors \b.

The following implementation performs depth-first DOM tree traversal and replaces text nodes containing the word "bad" with text nodes for both the text before and after that word as well as a <span style="color: red">bad</span> in place of "bad":

var walk = (node, func) => {
  var children = node.childNodes;
  for (var i = children.length - 1; i >= 0; --i) {
    walk(children[i], func);
  }
  func(node);
};

var highlight = (text, color) => {
  var span = document.createElement("span");
  span.style.color = color;
  span.appendChild(document.createTextNode(text));
  return span;
};

var root = document.getElementById("content");

walk(root, (node) => {
  if (node.nodeType != Node.TEXT_NODE) return;
  var texts = node.data.split(/\bbad\b/),
      length = texts.length;
  if (length > 1) {
    var fragment = texts.reduce((fragment, text, i) => {
      fragment.appendChild(document.createTextNode(text));
      if (i < length - 1) fragment.appendChild(highlight("bad", "red"));
      return fragment;
    }, document.createDocumentFragment());
    node.parentNode.replaceChild(fragment, node);            
  }
});
<div id="content">are they good or bad, tell me how badly it is <bad>bad bad bad</bad> if bad.</div>

See also: JavaScript: Add elements in textNode

Upvotes: 0

warkentien2
warkentien2

Reputation: 979

use \b (a word boundary token) as said by raphael75
str = str.replace(/\b(bad)\b/gi, '<span style="color:red;">$1</span>');
also, I:

  • added the i modifier, for it to be case insensitive (capture Bad, BAD, bad)
  • encapsulated (bad) in the regEx to call again with $1, this keeps the case (if capitalized Bad, stays Bad)
    var str = "bad incredibad badly Bad, testing bad, bad. bad";
    str = str.replace(/\b(bad)\b/gi, '<span style="color:red;">$1</span>');
    document.getElementsByTagName('p')[0].innerHTML = str;
    <p></p>

regex101 is a go-to place for JavaScript RegEx

Upvotes: 1

Related Questions