如何从 DOM 查询文本节点、查找降价模式、用 HTML 标记替换匹配项并用新内容替换原始文本节点?

How to query text-nodes from DOM, find markdown-patterns, replace matches with HTML-markup and replace the original text-node with the new content?

工具提示的类似 Markdown 的功能

问题:

使用 Vanilla JavaScript 我想:

改变这个:

<div>
   <p>
        Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}? 
   </p>
   <p>
        It is [fine]{a word that expresses gratitude}.
   </p>
</div> 

为此:

<div>
    <p>
        Hello <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">world</mark>, how is it <mark data-toggle="tooltip" data-placement="top" title="verb means walking">world</mark>?
    </p>
    <p>
        It is fine <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">thanks</mark>.
    </p>
</div> 

所以看起来像这样:

在某种程度上类似于“降价”编辑功能。

解决方案:

  1. 用不同的方式标记要替换的字符串:
<p>It is fine *[thanks]{a word that expresses gratitude}*!</p>
  1. 启动Bootstrap和tooltip functionality
  2. 抓取所有段落
var p = document.getElementsByTagName('p')
  1. 应用正则表达式
tooltip = original.match(/(\{)(.*?)(\})/gi)[0].slice(1, -1);
hint = original.match(/(\[)(.*?)(\])/gi)[0].slice(1, -1);
  1. 更改他们的内文
replaced = original.replace(/(\*)(.*?)(\*)/gi, 
        `<mark data-toggle="tooltip" data-placement="top" title="${tooltip}">${hint}</mark>`);
elem.innerHTML = replaced;
  1. 全部在一个函数中:
[].forEach.call(p, elem => {
    let original = elem.innerHTML;
    let replaced, tooltip, hint
    tooltip = original.match(/(\{)(.*?)(\})/gi)[0].slice(1, -1);
    hint = original.match(/(\[)(.*?)(\])/gi)[0].slice(1, -1);
    replaced = original.replace(/(\*)(.*?)(\*)/gi, 
        `<mark data-toggle="tooltip" data-placement="top" title="${tooltip}">${hint}</mark>`);
      elem.innerHTML = replaced;
});

但我失败了

当有更多的段落或者当我只想用 2 对括号而不是额外的星号以简单的方式完成它时,我很痛苦。如果 innerTEXT 有更多应该有工具提示的短语/单词,也会失败。 有任何想法吗? 你有什么建议吗? 现有的做法? 图书馆? 脚本?

人们很容易偶然发现如何用其他未知 HTML 内容替换文本节点的正确方法。

通用解决方案考虑了更复杂的 HTML 内容。

因此,从源节点开始,逐步需要在目标文本节点之前插入它的每个子节点(文本节点或元素节点)。插入所有节点后,最终删除目标文本节点。

关于 regex and the markup template, one can create the markup-string within a single replace call from a single regex and a single template string both making use of Capturing Groups

// text node detection helper
function isNonEmptyTextNode(node) {
  return (
       (node.nodeType === 3)
    && (node.nodeValue.trim() !== '')
    && (node.parentNode.tagName.toLowerCase() !== 'script')
  );
}

// text node reducer functionality
function collectNonEmptyTextNode(list, node) {
  if (isNonEmptyTextNode(node)) {
    list.push(node);
  }
  return list;
}
function collectTextNodeList(list, elmNode) {
  return Array.from(
    elmNode.childNodes
  ).reduce(
    collectNonEmptyTextNode,
    list
  );
}

// final dom render function
function replaceTargetNodeWithSourceNodeContent(targetNode, sourceNode) {
  const parentNode = targetNode.parentNode;

  Array.from(sourceNode.childNodes).forEach(function (node) {
    parentNode.insertBefore(node, targetNode);
  });
  parentNode.removeChild(targetNode);    
}

// template and dom fragment render function
function findMarkdownCreateMarkupAndReplaceTextNode(node) {
  const regX = (/\[([^\]]+)\]\{([^\}]+)\}/g);
  const text = node.nodeValue;

  if (regX.test(text)) {
    const template = '<mark data-toggle="tooltip" data-placement="top" title=""></mark>'

    const renderNode = document.createElement('div');
    renderNode.innerHTML = text.replace(regX, template);

    replaceTargetNodeWithSourceNodeContent(node, renderNode);
  }
}


const elementNodeList = Array.from(document.body.getElementsByTagName('*'));

const textNodeList = elementNodeList.reduce(collectTextNodeList, []);

textNodeList.forEach(findMarkdownCreateMarkupAndReplaceTextNode);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<div>
  <p>
    <span>Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}?</span>
    <span>Hello [world]{big round planet we live on}, how is it [going]{verb that means walking}?</span>
  </p>
  <p>
    <span>It is [fine]{a word that expresses gratitude}.</span>
    It is [fine]{a word that expresses gratitude}.
    <span>It is [fine]{a word that expresses gratitude}.</span>
  </p>
</div>
<!--

  // does get rerendered into:

  <div>
    <p>
      <span>
        Hello
        <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">
          world
        </mark>
        , how is it
        <mark data-toggle="tooltip" data-placement="top" title="verb that means walking">
          going
        </mark>
        ?
      </span>
      <span>
        Hello
        <mark data-toggle="tooltip" data-placement="top" title="big round planet we live on">
          world
        </mark>
        , how is it
        <mark data-toggle="tooltip" data-placement="top" title="verb that means walking">
          going
        </mark>
        ?
      </span>
    </p>
    <p>
      <span>
        It is
        <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
          fine
        </mark>
        .
      </span>
      It is
      <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
        fine
      </mark>
      .
      <span>
        It is
        <mark data-toggle="tooltip" data-placement="top" title="a word that expresses gratitude">
          fine
        </mark>
        .
      </span>
    </p>
  </div>

//-->