如何在不破坏现有 HTML 的情况下更改 DOM 中的所有文本?

How to change all texts in DOM without breaking existing HTML?

我想用 javascript 替换页面上的特定文本。为简单起见,假设我想用字母 X 替换所有字母 A。重要的是它不会中断内联 HTML.

是否有一种简单的方法来遍历所有 DOM 元素并仅更改实际文本?

<span>hello world <a href="/">abcd</a>..</span>

应该变成

<span>hello world <a href="/">xbcd</a>..</span>

而不是

<spxn>hello world <x href="/">xbcd</x>..</spxn>

迭代 all text nodes,如果它们包含 a:

,则更改它们的 nodeValue

function getAllTextNodes() {
    var walker = document.createTreeWalker(
        document.body, 
        NodeFilter.SHOW_TEXT, 
        null, 
        false
    );

    var node;
    var textNodes = [];

    while(node = walker.nextNode()) {
        textNodes.push(node);
    }
    return textNodes;
}

getAllTextNodes().forEach((node) => {
  const { nodeValue } = node;
  const newValue = nodeValue.replace(/a/g, 'x');
  if (newValue !== nodeValue) {
    node.nodeValue = newValue;
  }
});
<a href="/">abcd</a>

您还可以创建parents的白名单或黑名单,其文本节点可更改,如果您愿意:

function getAllTextNodes() {
    var walker = document.createTreeWalker(
        document.body, 
        NodeFilter.SHOW_TEXT, 
        null, 
        false
    );

    var node;
    var textNodes = [];

    while(node = walker.nextNode()) {
        textNodes.push(node);
    }
    return textNodes;
}

const tagNamesToKeepUnchanged = ['SCRIPT'];

getAllTextNodes().forEach((node) => {
  if (tagNamesToKeepUnchanged.includes(node.parentNode.tagName)) {
    return;
  }
  const { nodeValue } = node;
  const newValue = nodeValue.replace(/a/g, 'x');
  if (newValue !== nodeValue) {
    node.nodeValue = newValue;
  }
});

const obj = JSON.parse(
  document.querySelector('script[type="application/json"]').textContent
);
console.log(obj.key);
<a href="/">abcd</a>
<p>foo bar</p>
<script type="application/json">{"key":"value"}</script>

这将保留标签名称、事件侦听器以及除了某些文本节点的内容之外的几乎所有内容。

我经常用这个:

/**
 * Executes operation over all text nodes in a document
 * @param {HTMLElement} element
 * @param {function(Text):void} callback
 */
function processTextNodes(element, callback) {
    // For text node, execute callback
    if (element.nodeType == Node.TEXT_NODE)
        callback(element);
    // Otherwise, loop over child nodes
    else if (element.childNodes.length > 0) {
        for (const childNode of element.childNodes) {
            if (childNode.nodeType == Node.TEXT_NODE)
                callback(childNode);
            // Recursion to child nodes
            else {
                processTextNodes(childNode, callback);
            }
        }
    }
}

例如试试这个:

processTextNodes(document.body, (el)=>{el.data = el.data.toUpperCase()})

我在几个用户脚本中使用了它来替换新闻文章中的单词,使它们更有趣。