如何正则表达式和替换innerText,然后恢复HTML元素

问题描述 投票:0回答:1

我想要正则表达式并仅替换html的文本内容(innerText),并最终保留所有HTML元素(或将它们恢复原样)。

正则表达式不得检查 HTML 元素,而只能检查 HTML 内的文本内容(innerText、textContent 等)

虚构的例子,“对话荧光笔”

字符串:

<html>
<body>
    <h1>Hello, World!</h1>
    <p id="aaaa";>"Omae wa moshindeiru."</p>
    <p id="aaaa";>"Naani!"</p>
</body>
</html>

Javascript:

element = document.querySelector('body');
element.innerText = element.innerText
.replace(/[\"“”]([^\"”“\n]+)[\"”“]/g, '\"€€$1××\"');
element.innerHTML = element.innerHTML
.replace(/€€/g, '<span style="color: red">')
.replace(/××/g, '<\/span>')

预期输出:

<html>
<body>
    <h1>Hello, World!</h1>
    <p id="aaaa";>"<span style="color: red;">Omae wa mo shindeiru</span>"</p>
    <p id="aaaa";>"<span style="color: red;">Naani!</span>"</p>
</body>
</html>

实际产量:

<html>
<body>
Hello, World!<br><br>"<span style="color: red;">Omae wa moshindeiru.</span>"<br><br>"<span style="color: red;">Naani!</span>"
</body>
</html>

是的,我知道我可以调整正则表达式,但不行。 我只想对文本内容进行操作,然后恢复丢失的 HTML 元素。

javascript html dom
1个回答
0
投票

引用我自己的上述评论......

仅纯文本和基于正则表达式/替换的解决方案并不是完成此类任务的正确工具。您需要的是树遍历(在 DOM 上)和基于正则表达式的测试的组合,以便捕获所有匹配的文本节点。从那里,您需要节点方法来创建和插入匹配文本内容及其每个新的和封闭的

<span/>
元素的正确组合。

提供的示例代码恰好实现了两个功能,...

  • collectEveryTextNode
    ,即“treewalker”,从提供的元素或文本节点或节点列表开始,递归地收集每个文本节点,

  • replaceWithMatchingMarkerFragment

    是一个
    this
    上下文感知函数,它从提供的文本节点创建文本节点或元素节点,其中后者至少包含一次匹配的短语/子字符串。该函数确实用文档片段替换传递的文本节点,该文档片段已附加至少一个包含匹配文本的标记节点。

const regXQuotedPhrase = /(?<quote>["“”])(?<phrase>[^"“”]+)\k<quote>/; const matchingTextNodes = collectEveryTextNode(document.body) .filter(({ nodeValue }) => regXQuotedPhrase.test(nodeValue)); console.log({ matchingTextContents: matchingTextNodes .map(({ nodeValue }) => nodeValue) }); matchingTextNodes .forEach(replaceWithMatchingMarkerFragment, { /** * - `forEach`'s 2nd `thisArg` parameter gets * used as config, where one can provide the * matching criteria as regular expression and * a custom element node too which wraps itself * as marker around each matching text-fragment. */ regX: regXQuotedPhrase, node: document.createElement('mark'), });
mark {
  color: #006;
  background-color: #ff0;
}
.as-console-wrapper {
  left: auto!important;
  width: 60%;
  min-height: 100%;
}
<h1>Hello, World!</h1>

<p id="aaaa">   Foo ... "Omae wa moshindeiru." ... bar.   </p>
<p id="aaaa">bar ... "Naani!" ... baz ... "Naani!" ... biz.</p>


<script>
/**
 *  - The **treewalker** which recursively collects
 *    every text-node, starting from either a provided
 *    (elemen/text) node or a node-list/collection.
 */
function collectEveryTextNode(nodeOrCollection) {
  const { ELEMENT_NODE, TEXT_NODE } = Node;

  nodeOrCollection = nodeOrCollection || {};

  return (nodeOrCollection.nodeType === TEXT_NODE)

    ? [nodeOrCollection]    
    : Array
        .from(
          nodeOrCollection.childNodes ?? nodeOrCollection
        )
        .reduce((result, childNode) => {
          const { nodeType } = childNode;

          if (nodeType === TEXT_NODE) {

            result
              .push(childNode);

          } else if (nodeType === ELEMENT_NODE) {

            result = result
              .concat(
                // self recursive call.
                collectEveryTextNode(childNode)
              );
          }
          return result;
        }, []);
}

/**
 *  - The `this` context-aware function which creates
 *    either text- or element-nodes from a provided
 *    text-node, where the latter contains the matching
 *    phrase/substring at least once.
 *  - The passed text-node then gets replaced by a
 *    document-fragment which has got appended at least
 *    one marker node that encloses a matching text.
 */
function replaceWithMatchingMarkerFragment(textNode) {

  const { regX, node: markerNode } = this;
  const { parentNode, nodeValue } = textNode;
 
  const fragment = document.createDocumentFragment();
  const nodeList = [];

  let text = nodeValue;
  let regXResult;

  while (regXResult = regX.exec(text)) {

    const { index, input } = regXResult;

    const quotedPhrase = regXResult[0];
    const prePhrase = input.slice(0, index);

    if (prePhrase) {

      nodeList.push(
        document.createTextNode(prePhrase),
      );
    }
    const elmNode = markerNode.cloneNode(true);

    elmNode.appendChild(
      document.createTextNode(quotedPhrase),
    );
    nodeList.push(elmNode);

    text = input.slice(index + quotedPhrase.length);
  }
  if (text) {
    // equals a `postPhrase`.

    nodeList.push(
      document.createTextNode(text),
    );
  }
  nodeList.forEach(node => fragment.appendChild(node));

  textNode.replaceWith(fragment);
}
</script>

© www.soinside.com 2019 - 2024. All rights reserved.