29

I was directed to the Linkify project on GitHub (https://github.com/cowboy/javascript-linkify) for finding and "linkifying" URLs and domains just floating in text.

It's awesome! It totally works on text!

However, I'm not quite sure how to make it work on a textNode which has the text I want to Linkify.

I understand the textNode only has textContent since.. it's all text. Since this Linkify function returns HTML as text, is there a way to take a textNode and "rewrite" the HTML within it with the Linkify output?

I've been playing with it on JSFiddle here: http://jsfiddle.net/AMhRK/9/

function repl(node) {
  var nodes = node.childNodes;
  for (var i = 0, m = nodes.length; i < m; i++) {
    var n = nodes[i];
    if (n.nodeType == n.TEXT_NODE) {
      // do some swappy text to html here?
      n.textContent = linkify(n.textContent);
    } else {
      repl(n);
    }
  }
}
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
Jippers
  • 2,635
  • 5
  • 37
  • 58

4 Answers4

31

You'll need to replace the textNode with an HTML element, like a span, and then set your linkified-text as that element's innerHTML.

var replacementNode = document.createElement('span');
replacementNode.innerHTML = linkify(n.textContent);
n.parentNode.insertBefore(replacementNode, n);
n.parentNode.removeChild(n);
Will Scott
  • 546
  • 5
  • 6
11

Additionally to previous answer I propose more short way (based on jQuery):

$(n).replaceWith('Some text with <b>html</b> support');

where n - is textNode.

Or the native version

var txt = document.createElement("span");
txt.innerHTML = "Some text with <b>html</b> support";
node.replaceWith(txt);

where node is the textNode

driconmax
  • 956
  • 1
  • 18
  • 32
IStranger
  • 1,868
  • 15
  • 23
  • 1
    `replaceWith()` is available as [vanilla JavaScript](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith) on modern browsers (Chrome and Firefox; Edge is under development). – Franklin Yu Oct 31 '17 at 18:20
  • 8
    But in the vanilla version there is no HTML support. Tags just appear as text. – Kokodoko Dec 14 '18 at 13:02
5

Build on @AlexJeffcott's answer: Perf optimized version utilizing DocumentFragment instead of messing around with <span>, innerHTML and childNodes

const enhanceNodes = (textNodes) => {
    textNodes.forEach((node) => {
        const oldText = node.textContent;
        const newText = fancyTextTranformation(oldText);
        const fragment = document.createRange().createContextualFragment(newText);
        node.replaceWith(fragment);
    })
}
ja0nz
  • 1,891
  • 1
  • 9
  • 5
1

Build on Will Scott's accepted answer, if you do not wish to have to wrap everything in a span, you could do the following:

const enhanceNodes = (textNodes) => {
    const renderNode = document.createElement('span');
    textNodes.forEach((node) => {
        const oldText = node.textContent;
        renderNode.innerHTML = fancyTextTranformation(oldText);
        node.replaceWith(...renderNode.childNodes);
    })
}
AlexJeffcott
  • 116
  • 2
  • 5