2

I've made two different attempts to replace all occurrences of a single word from a webpage using JQuery in a Chrome extension. Both attempts kind of worked, but neither worked as a universal method for replacing all occurrences of a word from a webpage.

How can I write a script that replaces all occurrences of a certain word in a webpage?

Read on for details of the 2 different attempts which both kind of worked but failed for different reasons.

Attempt 1: Replace the text of nodes that have no children. This fails in cases where child nodes are used for formatting. For example, it fails when parsing the following:

<p>StringToReplace <strong>doesn't</strong> get replaced!</p>

The exact code I used for this attempt is:

$("*").each(function () {
    if ($(this).children().length == 0) { 
        $(this).text(replaceStrings($(this).text()));
    } 
}

(replaceStrings is a separate function with a bunch of arbitrary calls to replace)

Attempt 2: Replace the HTML of nodes that probably only contain text (e.g. p). This fails because some of the webpages my script needs to work on have text appearing right inside tags like body. If I try replacing the HTML of body, it has an undesired side-effect of breaking functionality on some pages. IMO it would be a nightmare to try to handle every edge case where site functionality is impaired by replacing HTML of body or divs high up the DOM tree.

The code I used for this second attempt:

$("*").each(function () { 
    if (
        $(this)[0].nodeName.toLowerCase() == "font"
        || $(this)[0].nodeName.toLowerCase() == "span"
        || $(this)[0].nodeName.toLowerCase() == "p"
        //|| $(this)[0].nodeName.toLowerCase() == "body"
        // || $(this)[0].nodeName.toLowerCase() == "div" 
        ){
        $(this).html(replaceStrings($(this).html()));
    }
}

How can I write a script that replaces all occurrences of a certain word in a webpage?

Thanks!

Andy E
  • 338,112
  • 86
  • 474
  • 445
Trindaz
  • 17,029
  • 21
  • 82
  • 111
  • 2
    As an aside, the selector for your second attempt could easily be written as `$('font, span, p')` negating the need to check `nodeName`. – roryf Mar 01 '11 at 11:35

2 Answers2

3

I don't like the plugin in the first answer because it only works 1 level deep. Here's a version that goes through the entire structure under the selected element.

Usage: $(".content").replaceText("visitor","John Doe");

// Replace occurences of 'search' with 'replace' anywhere in the element. 
// New HTML tags will be rendered, except if 'text_only' is true. 
$.fn.replaceText = function(search, replace, text_only) {  
    return this.each(function(){  
        var v1, v2, rem = [];
        $(this).find("*").andSelf().contents().each(function(){
            if(this.nodeType === 3) {
                v1 = this.nodeValue;
                v2 = v1.replace(search, replace);
                if(v1!=v2) {
                    if(!text_only && /<.*>/.test(v2)) {  
                        $(this).before( v2 );  
                        rem.push(this);  
                    }
                    else this.nodeValue = v2;  
                }
            }
        });
        if(rem.length) $(rem).remove();
    });
};
Sygmoral
  • 7,021
  • 2
  • 23
  • 31
0

I haven't thoroughly tested this, but it should point you in the right direction:

$('*', 'body')
    .andSelf()
    .contents()
    .filter(function() {
        return this.nodeType === 3;
    })
    .each(function() {
        this.nodeValue = replaceStrings(this.nodeValue);
    });

(based on this answer along with help from this plugin)

Community
  • 1
  • 1
roryf
  • 29,592
  • 16
  • 81
  • 103
  • Thanks roryf but I've already implemented hubbl's solution and it's working perfectly so I probably won't test this one out. – Trindaz Mar 01 '11 at 11:57