0

I have a problem with the javascript replace function and I don't succeed to resolve it.

This is my code : https://jsfiddle.net/r36k20sa/1/

  var tags = ['zazie', 'johnny'];

  tags.forEach(function(element) {
    content = content.replace(
      new RegExp("(?!<a.*?>.*?)(\\b" + element + "\\b)(?!.*?<\\/a>)", "igm"),
      '<a href="" class="esk-seo-plu-link" style="background:red;color:white">$1</a>'
    );
  });

In the tags array, if I reverse the array "johnny" then "zazie" all tags are well selected otherwise, some tags are missing. (The last in this example). What can be the trick?

What can be explained that ? It seems like the javascript replace function runs asynchronous?

Thanks for your help.

4
  • Can you please post the initial value of content that you're working with? Commented Sep 20, 2017 at 12:25
  • No, replace is not asynchronous. Commented Sep 20, 2017 at 12:25
  • Can you please explain how you expected your regex to work? What are these negative lookaheads good for? The first one never will match given your element. Commented Sep 20, 2017 at 12:30
  • Notice that . does not match linebreaks. Do you think that could explain something? Commented Sep 20, 2017 at 12:31

2 Answers 2

0

Are you seriously using regex to process HTML when you have a DOM parser at your fingertips?

var content = document.getElementById('content');

function findTextNodes(root,ret) {
    // recursively descend into child nodes and return an array of text nodes
    var children = root.childNodes, l = children.length, i;
    ret = ret || [];
    for( i=0; i<l; i++) {
        if( children[i].nodeType == 1) { // ElementNode
            // excluding A tags here, you might also want to exclude BUTTON tags
            if( children[i].nodeName != "A") {
                findTextNodes(children[i],ret);
            }
        }
        if( children[i].nodeType == 3) { // TextNode
            ret.push(children[i]);
        }
    }
    return ret;
}
var textNodes = findTextNodes(content);

// now search those text node contents for matching tags.
var tags = ['zazie','johnny'], tagcount = tags.length, regexes, tag;
for( tag=0; tag<tagcount; tag++) {
    regexes[tag] = new RegExp("\b"+tags[tag]+"\b","i");
}

var node, match, index, tagtext, newnode;
while(node = textNodes.shift()) {
    for( tag=0; tag<tagcount; tag++) {
        if( match = node.nodeValue.match(regexes[tag])) {
            index = match.index;
            textNodes.unshift(node.splitText(index + tags[tag].length));
            tagtext = node.splitText(index);
            newnode = document.createElement('a');
            newnode.href = "";
            newnode.className = "esk-seo-plu-link";
            newnode.style.cssText = "background:red;color:white";
            tagtext.parentNode.replaceChild(newnode,tagtext);
            newnode.appendChild(tagtext);
        }
    }
}

// and done - no more action needed since it was in-place.

See it in action

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks a lot, that's a lovely way to achieve that ! You're great !
Just have a problem with the case the word in inside another like this : jsfiddle.net/r36k20sa/8 . the tag 'monnaie' is underlined for the word 'monnaies' . How do you think I can fix that ?
@FrançoisDusautoir I've updated the code to use a regex with word-boundary constraints. Hopefully this solves your issue.
It seems your solution has an error because, it doesn't work anymore ? I don't succeed to find what it's wrong. jsfiddle.net/r36k20sa/9
Do you think it is possible to do the same with PHP ? I tried with PHP DOM PARSER but I don't succeed in : stackoverflow.com/questions/46446272/…
0

Please replace . with \\.

  var tags = ['zazie', 'johnny'];

  tags.forEach(function(element) {
    content = content.replace(
      new RegExp("(?!<a.*?>\\.*?)(\\b" + element + "\\b)(?!\\.*?<\\/a>)", "igm"),
      '<a href="" class="esk-seo-plu-link" style="background:red;color:white">$1</a>'
    );
  });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.