将TextNode中的文本替换为基于正则表达式的新DOM节点

Replacing text in TextNode with new DOM node based on regular expression

本文关键字:正则表达式 DOM 节点 TextNode 文本替换      更新时间:2023-09-26

我有一个函数可以预处理HTML DOM中的文本节点。

其目的本质上是执行一些插值或字符串模板。

该函数基本上检查与正则表达式/'${([^}]*)}/g/{{([^}]*)}/g匹配的出现。

示例:${foo + 1}{{foo + 2}}

这一切都奏效了。

但我的目标是用新的节点(例如span(替换这些出现,这些节点由Knockout绑定表达式组成,其中包含正则表达式中匹配项的内部值。位置正确。在出现空白的地方保留空白。

像这样:

<span ko-text="foo" />用于${foo}(注意:自定义绑定语法(

我就是无法用TextNode.splitText来理解它。

我该如何做到这一点?

这是我目前掌握的代码:

preprocessNode(node: Element) {
    if ("nodeValue" in node && node.nodeValue !== null) {
        var value = node.nodeValue;
        var match = value.matchAll(/'${([^}]*)}/g);
        if (!match) {
            match = value.matchAll(/{{([^}]*)}/g);
        }
        if (match !== null && match.length > 0) {
            var parentNode = node.parentNode;
            for (let entry of match) {
                var offset = node.nodeValue.indexOf(entry[0]);
                var oldNode = node;
                node = node.splitText(offset);
                var newNode = document.createElement("span");
                newNode.setAttribute("ko-text", entry[1]);
                node.parentNode.appendChild(newNode);
            }
            return [parentNode, parentNode];
        }
    }
    return null;
}

函数matchAll是一个自定义函数。

免责声明:我不懂JavaScript!这只是一个建议。

 #  /^'$'{([^{}]+)'}|'{'{([^{}]+)'}'}|((?:(?!^'$'{[^{}]+'}|'{'{[^{}]+'}'})['S's])+)/
   ^ '$'{
   ( [^{}]+ )                    # (1)
   '}
|  
   '{'{ 
   ( [^{}]+ )                    # (2)
   '}'}
|  
   (                             # (3 start)
        (?:
             (?!
                  ^ '$'{ [^{}]+ '}
               |  '{'{ [^{}]+ '}'}
             )
             ['S's] 
        )+
   )                             # (3 end)

伪代码:

if ( regex_find ( /^'$'{[^{}]+'}|'{'{[^{}]+'}'}/, value ) )
{
    var found = false;
    while ( regex_search ( /^'$'{([^{}]+)'}|'{'{([^{}]+)'}'}|((?:(?!^'$'{[^{}]+'}|'{'{[^{}]+'}'})['S's])+)/g, match, value ) )
    {
        if ( match[1] != null )
        {
            // Found '${..}' form
            found = true;
            var newNode = document.createElement("span");
            newNode.setAttribute("ko-text", match[1]);
            // Append newNode 
        }
        else
        if ( match[2] != null )
        {
            // Found '{{..}}' form
            found = true;
            var newNode = document.createElement("span");
            newNode.setAttribute("ko-text", match[2]);
            // Append newNode 
        }
        else
        if ( match[3] != null )
        {
            // Found '...' normal text
            found = true;
            var newNode = document.createTextNode( match[3] );
            // Append newNode 
        }
    }
    if ( found )
    {
        // Clear or delete the original text node
        // ... 
    }
}

经过多次考虑,我就是这样解决的:

if (match !== null && match.length > 0) {
  var parentNode = node.parentNode;
  var nodes = [];
  var textString = value;
  var i = 0;
  for (let entry of match) {
    var startOffset = node.nodeValue.indexOf(entry[0]);
    var endOffset = startOffset + entry[0].length;
    var length = startOffset - i;
    if (length > 0) {
      var str = textString.substr(i, length);
      var textNode = document.createTextNode(str);
      parentNode.insertBefore(textNode, node);
    }
    var newNode2 = document.createElement("span");
    newNode2.setAttribute("ko-text", entry[1]);
    parentNode.insertBefore(newNode2, node);
    nodes.push(newNode2);
    i = endOffset;
  }
  var length = textString.length - i;
  if (length > 0) {
    var str = textString.substr(i, length);
    var textNode = document.createTextNode(str);
    parentNode.insertBefore(textNode, node);
  }
  parentNode.removeChild(node);
  return nodes;
}

它肯定可以改进。

var textNodesWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
var node;
while (node = textNodesWalker.nextNode()) {
    var newNodes = [];
    var execResult = MY_REG_EXP.exec(node.nodeValue);
    var start = 0;
    while (execResult) {
        newNodes.push(document.createTextNode(node.nodeValue.substring(start, execResult.index)));
        var generatedElement = ...; // create element (document.createElement) based on execResult
        newNodes.push(generatedElement);
        start = execResult.index + execResult[0].length;
        execResult = MY_REG_EXP.exec(node.nodeValue);
    }
    if (newNodes.length == 0) {
        continue;
    }
    newNodes.push(document.createTextNode(node.nodeValue.substring(start, node.nodeValue.length)));
    for (var i=0; i<newNodes.length; i++) {
        node.parentNode.insertBefore(newNodes[i], node)
    }
    node.parentNode.removeChild(node);
}

让我们假设我们有模式/asdf/g,而不需要加粗。我的代码将转换以下html:

<p>Begin asdf End</p>

<p>Begin <b>asdf</b> End</p>