1

I am trying to replace words on google chrome but have run into an issue. I am able to successfully replace specific words but it kills associated html links.

How can I keep links live and still replace text?

This is the code in my chrome extension content scripts:

wordDict = {"the":"piano","and":"Hello", "a":"huh?"};

for (word in wordDict) {
    document.body.innerHTML = document.body.innerHTML.replace(
        new RegExp('\\b' + word + '\\b',"gi"), wordDict[word]
    );
};
njachowski
  • 927
  • 5
  • 14
Lance
  • 123
  • 2
  • 14
  • 1
    how about using innerText instead of innerHTML? – Lucas Rodrigues Jan 12 '16 at 15:08
  • By simply walking through the DOM tree. Rather than do a brain-dead simple regex replace, look at the `.innerText`member of each node in the tree and replace the text in _there_. (a) it wont touch the link targets, (b) it wont cause any event-listeners to be dropped. – enhzflep Jan 12 '16 at 15:10
  • If you can use jQuery.. `$('a').text("Anything");` you can also run a `jQuery.each()` iterator – miguelmpn Jan 12 '16 at 15:13
  • innerText appears to destroy the entire website and just spits out a bunch of unformatted text--not what I was going for. – Lance Jan 12 '16 at 16:33
  • I do have jquery loaded. Give me a moment to understand what your jquery code means so I can format it for my issue. – Lance Jan 12 '16 at 16:34
  • Try my solution bellow.. InnerText should work also, but your replacement might be wrong, try other replacements.. – miguelmpn Jan 12 '16 at 17:07
  • See the detailed answer here: http://stackoverflow.com/a/34607278/2445882 – minj Jan 14 '16 at 14:35

2 Answers2

1

I'm not an expert on regex expressions, so this is a solution to find one hyperlink on the page that contains one word and replace it by another.

for(var i = 0, l=document.links.length; i<l; i++) {
  if(document.links[i].innerText == "Word"){
    document.links[i].innerText = "Other Word";
  }
}

With this you can avoid regex, but you would still have to loop your words object.

In other hand, since you said you have jQuery loaded, this jQuery solution makes does what you intended, it looks in all a tags for the words and replaces them.

jQuery.each( wordDict , function( key, value ) {
  jQuery( "a" ).each(function(){
    if(jQuery(this).text().match(key)) jQuery(this).text(value);
  });
});

The first jQuery each loops the string object and the second each loops thru all a tags on the page, if there is a match it changes the element text by the value on the object.

miguelmpn
  • 1,859
  • 1
  • 19
  • 25
  • Do not word your answer as if you're just pasting a random code snippet and are not sure it works. It doesn't make it look good even if it works and not helpful to others. Explain the problem and show an answer you think works (and explaining why) - if it doesn't, someone will tell you in comments. – Xan Jan 13 '16 at 16:13
  • I tested both, doesn't mean it will work for him.. As you can see I was trying my best to understand and help him, maybe those words were not the best ("Does this works for you?") but I was just trying to get his feedback on my approach – miguelmpn Jan 13 '16 at 16:23
  • I don't see a problem in asking, but that's literally the only text that accompanies your answer. If you could restructure it as "(short explanation) (code) Does it help?" it would be a much better answer. – Xan Jan 13 '16 at 16:26
1

Okay! 2 Weeks later I finally resolved the issue. What was being ignored were the childnodes inside the DOM. The code I am using below effectively addresses the childnodes and maintains the original appearance of the script!

function replaceText(jsonArr) {
$("body *").textFinder(function() {
    for (var key in jsonArr) {
        var matcher = new RegExp('\\b' + key + '\\b', "gi");
        this.data = this.data.replace(matcher, jsonArr[key]);
    }
});
}

// jQuery plugin to find and replace text
jQuery.fn.textFinder = function( fn ) {
this.contents().each( scan );
// callback function to scan through the child nodes recursively
function scan() {
    var node = this.nodeName.toLowerCase();
    if( node === '#text' ) {
        fn.call( this );
    } else if( this.nodeType === 1 && this.childNodes && this.childNodes[0] && node !== 'script' && node !== 'textarea' ) {
        $(this).contents().each( scan );
    }
}
return this;
};
Lance
  • 123
  • 2
  • 14