8

I'm trying to write a JavaScript program without the use of jQuery to replace all visible target text on a webpage without messing up the page's functionality.

Specifically I'm trying to make a Chrome extension that does it passively on sites such as Facebook.

I've experienced limited success with the following:

checkLoad();
function checkLoad(){
    if (document.readyState === "complete") {
        document.body.innerHTML = document.body.innerHTML.replace("target string", "replacement string");
    } else {
        setTimeout('checkLoad();', 500)
    }
}

This code misses things like people's names, titles and such.

I've looked around and can't seem to find a working solution. Any ideas?

Sampson
  • 265,109
  • 74
  • 539
  • 565
Andrew
  • 155
  • 1
  • 2
  • 9
  • 1
    Can you give an example of what it misses? If it's stuff loaded via AJAX for example then, yeah, you might miss it unless you keep checking constantly. – Matt Burland Nov 25 '12 at 00:59
  • 4
    I don't recommend using `innerHTML` for a number of reasons; for one thing you will risk modifying the contents of HTML tags and attributes. – Jim Blackler Nov 25 '12 at 01:00
  • The Javascript function .replace takes a regex as the first argument. Try replacing the first argument with the equivalent regex version of it (i.e. escaping all regex special characters, etc.) – MadSkunk Nov 25 '12 at 01:00
  • Re-writing `innerHTML` is an awful approach. Read here: http://stackoverflow.com/q/7392930/425275 – Šime Vidas Nov 25 '12 at 01:01
  • It misses things like if I enter a person's name, the name will be unchanged beside all their posts, where as their name in comments or post content changes fine. – Andrew Nov 25 '12 at 01:11
  • I see now innerHTML is a poor way to set about doing this, webpages seemed to lose functionality. What would be a better way? – Andrew Nov 25 '12 at 01:13
  • Check out this article http://blog.alexanderdickson.com/javascript-replacing-text – Jim Blackler Nov 25 '12 at 01:16

3 Answers3

11

Simple regular expression to fix it:

document.body.innerHTML = document.body.innerHTML.replace(/target string/g, "replacement string");
Bruno Sousa
  • 480
  • 3
  • 12
  • That did solve my problem with innerHTML replace! Thank you. Though apparently this is no longer the right approach to my problem, good to know for the future. – Andrew Nov 25 '12 at 21:12
  • 2
    One issue with this approach is that it unbinds javascript event handlers from the DOM. – Dylan Valade Feb 23 '14 at 20:01
  • A warning - this seems to reload the entire document, causing any events fired from a chrome extension to refire. I haven't found a fix for it, as I just discovered the problem. FYI for the one other person in the universe this affects... – Joe B. Mar 02 '17 at 01:35
1

I'd recommend against replacing the innerHTML for a few reasons from this blog (https://j11y.io/javascript/replacing-text-in-the-dom-solved/):

  1. You can't guarantee that the text replaced is text. Maybe it's an attribute in the html or other relevant code.
  2. It resets the document and, in my case, fired my chrome extension again. This led to my extension reloading again and again.

I solved this by downloading the js file from the blog and called it like so:

  var regex = new RegExp(targetString, "g");
  findAndReplaceDOMText(document.body, {
    find: regex,
    replace: function(portion) {
      var e = document.createElement('span');
      e.appendChild(document.createTextNode(replacementString));
      return e;
    }
  });

My use case above wraps the text being replaced in a span tag, so it's slightly different than yours. This javascript function has lots of flexibility.

Joe B.
  • 1,153
  • 1
  • 10
  • 17
1

The link https://j11y.io/javascript/replacing-text-in-the-dom-solved/ is cool but I'm not 100% agree with a solution. IMHO walking on DOM could be overkill.

Here is my tricky solution working on regexp only so can element.innerHTML be used. So that replaces text everywhere except tags and nbsp-like things:

function highlightInHtml(html, textToHighlight) {
  const t = textToHighlight'

  const tagsRe = new RegExp('(<.+?>|&\\w+;)');

  const htmlParts = html.split(tagsRe).filter(Boolean);

  const r = htmlParts.map( item =>
    tagsRe.test(item)
      ? item
      : item.replace(new RegExp(`(${t})`, 'ig'), `<span class="highlight">$1</span>`)
  ).join('');

  return r;
}

If there is something weak in this solution please let me know!

alex_1948511
  • 6,073
  • 2
  • 20
  • 21