Matrym
Matrym

Reputation: 17053

How to search/replace text with an "a href" wrapper in JavaScript?

I've converted html to a string, I'm able to use replace in that string to wrap the text with a link, and I can put that html back into the ID it came from.

My problem is that my replace method is going inside existing links on the page. This could create nested links, which is a problem. Does anyone out there know how to prevent the replace method from matching text that is within a link already?

I have right now:

keyword = "matching phrase";
keywordLink = "<a href='http://myurl.com'/>" + keyword + "</a>";
sasser = sasser.replace(keyword, keywordLink);
sasDom.innerHTML = sasser;

I'm looking for, in pseudo code:

... (keyword [if the next " < " sign is not followed by "/a>", regardless of how far away it is], keywordLink);

Upvotes: 0

Views: 1304

Answers (3)

bobince
bobince

Reputation: 536349

You can't do this kind of thing with regex at all. Work on the document objects which are already nicely parsed into a structure for you.

Here's a keyword linker adapted from this question.

// Find text in descendents of an element, in reverse document order
// pattern must be a regexp with global flag
//
function findTextExceptInLinks(element, pattern, callback) {
    for (var childi= element.childNodes.length; childi-->0;) {
        var child= element.childNodes[childi];
        if (child.nodeType===1) {
            if (child.tagName.toLowerCase()!=='a')
                findTextExceptInLinks(child, pattern, callback);
        } else if (child.nodeType===3) {
            var matches= [];
            var match;
            while (match= pattern.exec(child.data))
                matches.push(match);
            for (var i= matches.length; i-->0;)
                callback.call(window, child, matches[i]);
        }
    }
}

findTextExceptInLinks(document.body, /\bmatching phrase\b/g, function(node, match) {
    node.splitText(match.index+match[0].length);
    var a= document.createElement('a');
    a.href= 'http://www.example.com/myurl';
    a.appendChild(node.splitText(match.index));
    node.parentNode.insertBefore(a, node.nextSibling);
});

eta re comments: Here's a version of the same thing using plain text matching rather than regex:

function findPlainTextExceptInLinks(element, substring, callback) {
    for (var childi= element.childNodes.length; childi-->0;) {
        var child= element.childNodes[childi];
        if (child.nodeType===1) {
            if (child.tagName.toLowerCase()!=='a')
                findPlainTextExceptInLinks(child, substring, callback);
        } else if (child.nodeType===3) {
            var index= child.data.length;
            while (true) {
                index= child.data.lastIndexOf(substring, index);
                if (index===-1)
                    break;
                callback.call(window, child, index)
            }
        }
    }
}

var substring= 'matching phrase';
findPlainTextExceptInLinks(document.body, substring, function(node, index) {
    node.splitText(index+substring.length);
    var a= document.createElement('a');
    a.href= 'http://www.example.com/myurl';
    a.appendChild(node.splitText(index));
    node.parentNode.insertBefore(a, node.nextSibling);
});

Upvotes: 2

yu_sha
yu_sha

Reputation: 4410

I would do it in three steps:

1) replace <a [^>]+>matching phrase</a> with $1some_other_phrase</a>

2) replace matching phrase with <a...>keyword</a>

3) replace some_other_phrase with matching phrase

Upvotes: 0

Soviut
Soviut

Reputation: 91525

If you don't mind using JQuery, you can employ its wrap() function to wrap text or html elements in the specified tags.

Upvotes: 1

Related Questions