Door Door - 27 days ago 8
Javascript Question

Replace text with link with chrome extension

I am trying to replace text on a webpage with links. When I try this it just replaces the text with the tag and not a link. For example this code will replace "river" with:

<a href="http://www.cnn.com">asdf</a>


This is what I have so far:

function handleText(textNode)
{
var v = textNode.nodeValue;
v = v.replace(/\briver\b/g, '<a href="http://www.cnn.com">asdf</a>');
textNode.nodeValue = v;
}

Answer

If all you wanted to do was change the text to other plain text, then you could change the contents of the text nodes directly. However, you are wanting to add an <a> element. For each <a> element you want to add, you are effectively wanting to add a child element. Text nodes can not have children. Thus, to do this you have to actually replace the text node with a more complicated structure. In doing so, you will want to make as little impact on the DOM as possible, in order to not disturb other scripts which rely on the current structure of the DOM. The simplest way to make little impact is to replace the text node with a <span> which contains the new text nodes (the text will split around the new <a>) and any new <a> elements.

The code below should do what you desire. It replaces the textNode with a <span> containing the new text nodes and the created <a> elements. It only makes the replacement when one or more <a> elements need to be inserted.

function handleTextNode(textNode) {
    if(textNode.nodeName !== '#text') {
        //Don't do anything except on text nodes.
        return;
    }
    let origText = textNode.textContent;
    let newHtml=origText.replace(/\briver\b/g,'<a href="http://www.cnn.com">asdf</a>');
    //Only change the DOM if we actually made a replacement in the text.
    //Compare the strings, as it should be faster than a second RegExp operation and
    //  lets us use the RegExp in only one place for maintainability.
    if( newHtml !== origText) {
        let newSpan = document.createElement('span');
        newSpan.innerHTML = newHtml;
        textNode.parentNode.replaceChild(newSpan,textNode);
    }
}

//Testing: Walk the DOM of the <body> handling all non-empty text nodes
function processDocument() {
    //Create the TreeWalker
    let treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT,{
        acceptNode: function(node) { 
            if(node.textContent.length === 0) {
                return NodeFilter.FILTER_SKIP; //Skip empty text nodes
            } //else
            return NodeFilter.FILTER_ACCEPT;
        }
    }, false );
    //Iterate over all text nodes, calling handleTextNode for each non-blank text node.
    while(treeWalker.nextNode()) {
        handleTextNode(treeWalker.currentNode);
    }
}
document.getElementById('clickTo').addEventListener('click',processDocument,false);
<input type="button" id="clickTo" value="Click to process"/>
<div id="testDiv">This text should change to a link -->river<--.</div>

The TreeWalker code was taken from my answer here.

Comments