【问题标题】:How to change all texts in DOM without breaking existing HTML?如何在不破坏现有 HTML 的情况下更改 DOM 中的所有文本?
【发布时间】:2020-01-09 05:59:30
【问题描述】:

我想用 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>

【问题讨论】:

标签: javascript html tampermonkey userscripts


【解决方案1】:

遍历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;
  }
});
&lt;a href="/"&gt;abcd&lt;/a&gt;

如果需要,您还可以创建文本节点可更改的父母的白名单或黑名单:

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>

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

【讨论】:

  • 这会改变
  • 哦,确实如此,不过幸运的是,这可能并不重要,因为脚本已经被执行了。如果需要,您可以将其文本可更改的父元素(或黑名单)制作成白名单
  • 没关系,因为脚本在添加时只运行一次,而且检查是否出于某种原因有人想要这样做真的很简单。
  • 这个答案很酷,我不知道createTreeWalker! +1
  • 这真的很重要。它操纵脚本块中的函数,这让我以后遇到异常。
【解决方案2】:

我通常用这个:

/**
 * 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()})

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

【讨论】:

    猜你喜欢
    • 2010-12-21
    • 2020-12-29
    • 1970-01-01
    • 1970-01-01
    • 2019-02-28
    • 2022-11-01
    • 2013-12-14
    • 1970-01-01
    • 2014-07-29
    相关资源
    最近更新 更多