0

I am trying to add a new element to the parent of a text node, however all my attempts are not working. I want to extract some text from a text node and convert it into a <a> tag, then add that node and the remaining text to the parent of the text node.

Below is my latest attempt at this issue:

var regex = new RegExp(/(^|\s|:|-)((?:0x)[0-9a-fA-F]{40})(?:\s|$)/gi, "gi");
var replace = '$1<a href="/$2">$2</a>';

function convertHexToLink()
{
    var arrWhitelistedTags = ["code", "span", "p", "td", "li", "em", "i", "b", "strong", "small"];

    //Get the whitelisted nodes
    for(var i=0; i<arrWhitelistedTags.length; i++) {
        var objNodes = document.getElementsByTagName(arrWhitelistedTags[i]);
        //Loop through the whitelisted content
        for(var x=0; x<objNodes.length; x++) {
            convertHex(objNodes[x], regex, replace);
        }
    }
}

function convertHex(objNode, replaceRegex, replacePattern)
{
    // Some nodes have non-textNode children
    // we need to ensure regex is applied only to text otherwise we will mess the html up
    for(var i=0; i < objNode.childNodes.length; i++){
        if(objNode.childNodes[i].nodeType == 3){ // nodeType 3 = a text node

            var child = objNode.childNodes[i];
            var strContent = child.textContent;

            var element = document.createElement('div');
            element.innerHTML = strContent.replace(replaceRegex, replacePattern);
            
            console.log(element.outerHTML);
            console.log(element.childNodes.length);

            for(var x=0; x < element.childNodes.length; x++){
                console.log("Element Child Node :");
                console.log(element.childNodes[x].innerHTML);
                child.parentNode.insertBefore(element.childNodes[x], child);
            }
            child.parentNode.removeChild(child);
        }
    }
}

convertHexToLink();
<span>text about things 0xa74476443119A942dE498590Fe1f2454d7D4aC0d and some more text over here <a href="#">A link somewhere</a> more text here 0xa74476443119A942dE498590Fe1f2454d7D4aC0d</span>

My problem is that whenever I try to add an element to the parent of the text node it doesn't happen. I understand that you can't add an element to a text node, but I'm not doing that I'm adding a new element to the parent node of the textNode.

What am I doing wrong?

Samuel Hawksby-Robinson
  • 2,652
  • 4
  • 24
  • 25

1 Answers1

1

The root cause seems to be the fact that you loop through live HTMLCollections (document.getElementsByTagName()) and collection of nodes (objNode.childNodes), and modify the parent nodes at the same time.

Therefore your loops become unreliable, as new elements get in the collection or get removed, while you are walking through the collection.

A workaround would be to replace the current node by a single replacement node, but you have to take care not to introduce a tag that will get detected by your arrWhitelistedTags!

Same issue when you replace your hex value by a link. In order not to shift the collection you are walking through, you should replace the text node by a single replacement node.

var regex = new RegExp(/(^|\s|:|-)((?:0x)[0-9a-fA-F]{40})(?:\s|$)/gi, "gi");
var replace = '$1<a href="/$2">$2</a>';

function convertHexToLink() {
  var arrWhitelistedTags = ["code", "span", "p", "td", "li", "em", "i", "b", "strong", "small"];

  //Get the whitelisted nodes
  for (var i = 0; i < arrWhitelistedTags.length; i++) {
    // objNodes is a LIVE HTMLCollection.
    var objNodes = document.getElementsByTagName(arrWhitelistedTags[i]);
    //Loop through the whitelisted content
    for (var x = 0; x < objNodes.length; x++) {
      convertHex(objNodes[x], regex, replace);
    }
  }
}

function convertHex(objNode, replaceRegex, replacePattern) {
  // Some nodes have non-textNode children
  // we need to ensure regex is applied only to text otherwise we will mess the html up
  // objNode.childNodes is a LIVE collection of nodes.
  for (var i = 0; i < objNode.childNodes.length; i++) {
    if (objNode.childNodes[i].nodeType == 3) { // nodeType 3 = a text node
      // Use an HTMLElement that is not in arrWhitelistedTags, in order not to shift objNodes.
      var replacement = document.createElement('sup');

      var child = objNode.childNodes[i];
      var strContent = child.textContent;
      
      replacement.innerHTML = strContent.replace(replaceRegex, replacePattern);
      
      // Replace child (single text node) by replacement (single node), so that objNode.childNodes is not shifted.
      objNode.insertBefore(replacement, child);
      child.parentNode.removeChild(child);
    }
  }
}

convertHexToLink();
<span>text about things 0xa74476443119A942dE498590Fe1f2454d7D4aC0d and some more text over here <a href="#">A link somewhere</a> more text here 0xa74476443119A942dE498590Fe1f2454d7D4aC0d</span>
ghybs
  • 47,565
  • 6
  • 74
  • 99