1

I'm working on a simple browser plug in to replace 1 word with another but with the addition of an anchor for a small tooltip/pop up. I have this actually working already but my problem is, if the word to be replaced is within an anchor already, then my </a> closes the already open <a>.

So I need some help with how to only replace a word, as long as its not within an open anchor tag. A follow up to this is to also make sure the target word isn't in an <input> or other inappropriate tags.

My code is:

var regex = new RegExp("\\b" + nativeWord + "\\b", "igm");
var body = $('body');

body.each(function() {
    $(this).html(
        $(this).html().replace(regex, function() {
            counter++;
            if(counter > wordSwapLimit) {
                return nativeWord;
            } else {
                return "<a class=\"tooltip\" href=\"#\">" + studyWord + "<span class=\"classic\">" + nativeWord + "</span></a>";            
            }
        })
    );
});

I'm suspecting that I might need to write a more complex RegExp but this is my first outing with it so any suggestions would be greatly appreciated!

Update So I have a test page I work with to see how my code works. This is part of the original HTML from the page:

<p id="changeArea"> I <a href="http://www.chicken.com">love chicken but I hate</a> beef. </p>

But with the code shown above and swapping 'chicken' for 'kip', this gets changed to:

<p id="changeArea"> I <a href="http://www.&lt;a class=" tooltip"="">kip<span class="classic">chicken</span></a>.com"&gt;love <a class="tooltip" href="#">kip<span class="classic">chicken</span></a> but I hate beef. </p>

If I have no anchors around what I am swapping, then it works perfectly and I get a nice rollover tooltip.

Thanks again for your help!

Update 2 As requested, here are 'unprocessed' examples that my plugin might come across when swapping 'chicken' for 'kip':

<p id="changeArea"> I <a href="http://www.chicken.com">love chicken but I hate</a> beef. </p>
<p id="changeArea2"> Chickenpie? pie-chicken. </p>

What I'm hoping for is the following:

<p id="changeArea"> I <a href="http://www.chicken.com">love chicken but I hate</a> beef. </p>
<p id="changeArea2"> Chickenpie? pie-<a class="tooltip" href="#">kip<span class="classic">chicken</span></a>. </p>

As you can see in the first line, the html code is left alone, as is text for the word, because it is within a URL and so would break it if my tooltip anchor was put in. The second line ignores 'chickenpie' because I only want whole word swapping, as per my existing regexp. 'pie-chicken' does get swapped though. This line is processed correctly with existing code.

I'm literally just looking to add additional rules to my existing code that say 'dont replace code and dont replace if within an anchor open tag.

booleanBoy
  • 13
  • 5
  • I don't quite get it - even if you're in an open anchor tag, you open another one, so the end tag should be fine...? – sje397 Sep 03 '13 at 12:24
  • Currently it closes anchors for urls/links as well as swaps out the word if it is found within the 'href='. I've edited the question with an example of the problem it causes. – booleanBoy Sep 04 '13 at 14:33
  • Could give a simple example of what you want? A before (two examples) and an after (same two, but as you want them) – Martijn Sep 04 '13 at 14:57

1 Answers1

0

Since you're in JavaScript, you already have access to the DOM. Why would you try using Regexes???

Just iterate through text nodes, ignoring existing links:

(function(elem) {
    var recurse = arguments.callee, nodes = elem.childNodes, l = nodes.length, i, t,
        getLink = function() {
            var a = document.createElement('a'), s = document.createElement('span');
            a.href = "#";
            a.className = "tooltip";
            a.appendChild(document.createTextNode(studyWord));
            s.className = "classic";
            s.appendChild(document.createTextNode(nativeWord));
            a.appendChild(s);
            return a;
        };
    for( i=0; i<l; i++) {
        switch(nodes[i].nodeType) {
        case 1:
            // element - recurse, but not if already a link
            if( nodes[i].nodeName != "A") recurse(nodes[i]);
            break;
        case 3:
            // text node, look for keyword
            t = nodes[i].nodeValue.search(new RegExp("\\b"+nativeWord+"\\b","i"));
            if( t > -1) {
                // found it! now to hack around...
                t = nodes[i].splitText(t);
                t.splitText(nativeWord.length);
                t.parentNode.replaceChild(t,getLink());
            }
            break;
        }
    }
})(document.body);

Please note: This approach uses Vanilla JS. It is up to you to ensure it is run after the page is loaded. This may be done either by:

  • Adding the defer attribute to the script, if it is external
  • Putting the script right before the </body> (or at least after all the content you wish to affect)
  • Wrapping it in a window.onload handler, or even $(function() {...}) since you're already using jQuery.
Community
  • 1
  • 1
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592