jQuery 使用 JS 查找和替换文档中的特定文本字符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18643766/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 22:06:48  来源:igfitidea点击:

Find and replace specific text characters across a document with JS

javascriptjquery

提问by doc holiday

I'm wondering if there is a lightweight way I could use JavaScript or jQuery to sniff out a specific text character across a document; say and find all instances of this character. And then!Write an ability to replace all instances of this with say a $.

我想知道是否有一种轻量级的方法可以使用 JavaScript 或 jQuery 来嗅探文档中的特定文本字符;说并找到该角色的所有实例。进而!编写一个用$替换所有实例的能力。

I found this snippet for starters:

我为初学者找到了这个片段:

var str = 'test: '';

str = str.replace(/'/g, "'");

Essentially; I am wanting a solution for a one page document. Grab all instances of X and make it XY. Only text characters.

本质上; 我想要一个单页文档的解决方案。获取 X 的所有实例并将其设为 XY。只有文字字符。

回答by Max Malyk

How about this, replacing @with $:

这个怎么样,替换@$

$("body").children().each(function () {
    $(this).html( $(this).html().replace(/@/g,"$") );
});

http://jsfiddle.net/maximua/jp96C/1/

http://jsfiddle.net/maximua/jp96C/1/

回答by David says reinstate Monica

My own suggestion is as follows:

我自己的建议如下:

function nativeSelector() {
    var elements = document.querySelectorAll("body, body *");
    var results = [];
    var child;
    for(var i = 0; i < elements.length; i++) {
        child = elements[i].childNodes[0];
        if(elements[i].hasChildNodes() && child.nodeType == 3) {
            results.push(child);
        }
    }
    return results;
}

var textnodes = nativeSelector(),
    _nv;
for (var i = 0, len = textnodes.length; i<len; i++){
    _nv = textnodes[i].nodeValue;
    textnodes[i].nodeValue = _nv.replace(/£/g,'');
}

JS Fiddle demo.

JS小提琴演示

The nativeSelector()function comes from an answer(posted by Anurag) to this question: getElementsByTagName() equivalent for textNodes.

nativeSelector()函数来自这个问题的答案(由Anurag发布):getElementsByTagName() 等效于 textNodes

回答by user4642212

ECMAScript 2015+ approach

ECMAScript 2015+ 方法

Pitfalls when solving this task

解决此任务时的陷阱

This seems like an easy task, but you have to take care of several things:

这似乎是一项简单的任务,但您必须注意以下几点:

  • Simply replacing the entire HTML kills all DOM functionality, like event listeners
  • Replacing the HTML may also replace <script>or <style>contents, or HTML tags or attributes, which is not always desired
  • Changing the HTML may result in an xssattack
  • You may want to replace attributes like titleand alt(in a controlled manner) as well
  • 简单地替换整个 HTML 会杀死所有 DOM 功能,例如事件侦听器
  • 更换HTML也可以代替<script><style>内容,或者HTML标签或属性,这是不期望的总
  • 更改 HTML 可能导致xss攻击
  • 您可能还想替换titlealt(以受控方式)之类的属性

Guarding against xssattacks generally can't be solved by using the approaches below. E.g. if a fetchcall reads a URL from somewhere on the page, then sends a request to that URL, the functions below won't stop that, since this scenario is inherently unsafe.

防御xss攻击一般不能用下面的方法解决。例如,如果fetch调用从页面上的某处读取 URL,然后向该 URL 发送请求,下面的函数将不会停止,因为这种情况本质上是不安全的。

Replacing the text contents of all elements

替换所有元素的文本内容

This basically selects all elements that contain normal text, goes through their child nodes?—?among those are also text nodes?—, seeks those text nodes out and replaces their contents.

这基本上选择了所有包含普通文本的元素,通过它们的子节点?——?其中也有文本节点?——,寻找这些文本节点并替换它们的内容。

You can optionally specify a different root target, e.g. replaceOnDocument(//g, "$", { target: someElement });; by default, the <body>is chosen.

您可以选择指定不同的根target,例如replaceOnDocument(//g, "$", { target: someElement });;默认情况下,<body>选择了。

const replaceOnDocument = (pattern, string, {target = document.body} = {}) => {
  // Handle `string`?—?see the last section
  [
    target,
    ...target.querySelectorAll("*:not(script):not(noscript):not(style)")
  ].forEach(({childNodes: [...nodes]}) => nodes
    .filter(({nodeType}) => nodeType === document.TEXT_NODE)
    .forEach((textNode) => textNode.textContent = textNode.textContent.replace(pattern, string)));
};

replaceOnDocument(//g, "$");

Replacing text nodes, element attributes and properties

替换文本节点、元素属性和属性

Now, this is a little more complex: you need to check three cases: whether a node is a textnode, whether it's an element and its attributeshould be replaced, or whether it's an element and its propertyshould be replaced. A replacerobject provides methods for text nodes and for elements.

现在,这有点复杂:您需要检查三种情况:节点是否为文本节点,是否为元素及其属性应被替换,或者是否为元素及其属性应被替换。甲replacer对象提供用于文本节点和元件的方法。

Before replacing attributes and properties, the replacer needs to check whether the element has a matching attribute; otherwise new attributes get created, undesirably. It also needs to check whether the targeted property is a string, since only strings can be replaced, or whether the matching property to the targeted attribute is not a function, since this may lead to an xssattack.

在替换属性和属性之前,替换者需要检查元素是否有匹配的属性;否则会创建新的属性,这是不受欢迎的。它还需要检查目标属性是否是字符串,因为只能替换字符串,或者目标属性的匹配属性是否不是函数,因为这可能导致xss攻击。

In the example below, you can see how to use the extended features: in the optional third argument, you may add an attrsproperty and a propsproperty, which is an iterable (e.g. an array) each, for the attributes to be replaced and the properties to be replaced, respectively.

在下面的示例中,您可以看到如何使用扩展功能:在可选的第三个参数中,您可以添加一个attrs属性和一个props属性,每个属性都是一个可迭代的(例如数组),用于要替换的属性和属性分别更换。

You'll also notice that this snippet uses flatMap. If that's not supported, use a polyfill or replace it by the reduceconcat, or mapreduceconcatconstruct, as seen in the linked documentation.

您还会注意到此代码段使用flatMap. 如果不支持,请使用 polyfill 或将其替换为reduceconcatmapreduceconcat构造,如链接文档中所示。

const replaceOnDocument = (() => {
    const replacer = {
      [document.TEXT_NODE](node, pattern, string){
        node.textContent = node.textContent.replace(pattern, string);
      },
      [document.ELEMENT_NODE](node, pattern, string, {attrs, props} = {}){
        attrs.forEach((attr) => {
          if(typeof node[attr] !== "function" && node.hasAttribute(attr)){
            node.setAttribute(attr, node.getAttribute(attr).replace(pattern, string));
          }
        });
        props.forEach((prop) => {
          if(typeof node[prop] === "string" && node.hasAttribute(prop)){
            node[prop] = node[prop].replace(pattern, string);
          }
        });
      }
    };

    return (pattern, string, {target = document.body, attrs: [...attrs] = [], props: [...props] = []} = {}) => {
      // Handle `string`?—?see the last section
      [
        target,
        ...[
          target,
          ...target.querySelectorAll("*:not(script):not(noscript):not(style)")
        ].flatMap(({childNodes: [...nodes]}) => nodes)
      ].filter(({nodeType}) => replacer.hasOwnProperty(nodeType))
        .forEach((node) => replacer[node.nodeType](node, pattern, string, {
          attrs,
          props
        }));
    };
})();

replaceOnDocument(//g, "$", {
  attrs: [
    "title",
    "alt",
    "onerror" // This will be ignored
  ],
  props: [
    "value" // Changing an `<input>`'s `value` attribute won't change its current value, so the property needs to be accessed here
  ]
});

Replacing with HTML entities

替换为 HTML 实体

If you need to make it work with HTML entities like &shy;, the above approaches will just literally produce the string &shy;, since that's an HTMLentity and will only work when assigning .innerHTMLor using related methods.

如果您需要使其与 HTML 实体一起使用,例如&shy;,上述方法将直接生成 string &shy;,因为这是一个HTML实体,并且仅在分配.innerHTML或使用相关方法时才有效。

So let's solve it by passing the input string to something that accepts an HTML string: a new, temporary HTMLDocument. This is created by the DOMParser's parseFromStringmethod; in the end we read its documentElement's textContent:

因此,让我们通过将输入字符串传递给接受 HTML 字符串的内容来解决它:一个新的临时HTMLDocument. 这是由DOMParserparseFromString方法创建的;最后我们读了它documentElementtextContent

string = new DOMParser().parseFromString(string, "text/html").documentElement.textContent;

If you want to use this, choose one of the approaches above, depending on whether or not you want to replace HTML attributes and DOM properties in addition to text; then simply replace the comment // Handle `string`?—?see the last sectionby the above line.

如果你想使用这个,选择上面的一种方法,这取决于你是否要替换文本之外的 HTML 属性和 DOM 属性;然后简单地用// Handle `string`?—?see the last section上面的行替换注释。

Now you can use replaceOnDocument(/Güterzug/g, "G&uuml;ter&shy;zug");.

现在您可以使用replaceOnDocument(/Güterzug/g, "G&uuml;ter&shy;zug");.

NB: If you don't use the string handling code, you may also remove the {}around the arrow function body.

注意:如果您不使用字符串处理代码,您也可以删除{}围绕箭头函数体。

Note that this parses HTML entities but still disallows inserting actual HTML tags, since we're reading only the textContent. This is also safe against most cases of xss: since we're using parseFromStringand the page's documentisn't affected, no <script>gets downloaded and no onerrorhandler gets executed.

请注意,这会解析 HTML 实体,但仍然不允许插入实际的 HTML 标签,因为我们只读取textContent. 这对于xss 的大多数情况也是安全的:由于我们正在使用parseFromString并且页面document不受影响,因此不会<script>下载并且不会onerror执行处理程序。

You should also consider using \xADinstead of &shy;directly in your JavaScript string, if it turns out to be simpler.

如果结果更简单,您还应该考虑在 JavaScript 字符串\xAD&shy;直接使用而不是使用。

回答by Jsilvermist

Similar to @max-malik's answer, but without using jQuery, you can also do this using document.createTreeWalker:

与@max-malik 的回答类似,但不使用 jQuery,您也可以使用document.createTreeWalker执行此操作:

button.addEventListener('click', e => {
  const treeWalker = document.createTreeWalker(document.body);
  while (treeWalker.nextNode()) {
    const node = treeWalker.currentNode;
    node.textContent = node.textContent.replace(/@/g, '$');
  }
})
<div>This is an @ that we are @ replacing.</div>
<div>This is another @ that we are replacing.</div>
<div>
  <span>This is an @ in a span in @ div.</span>
</div>
<br>
<input id="button" type="button" value="Replace @ with $" />

回答by sohail.hussain.dyn

In javascript without using jquery:

在不使用 jquery 的 javascript 中:

document.body.innerText = document.body.innerText.replace('actualword', 'replacementword');

回答by Suresh Mahawar

Use split and join method

使用拆分和连接方法

$("#idBut").click(function() {
    $("body").children().each(function() {
        $(this).html($(this).html().split('@').join("$"));
    });
});

here is solution

这是解决方案

回答by Kevin B

The best would be to do this server-side or wrap the currency symbols in an element you can select before returning it to the browser, however if neither is an option, you can select all text nodes within the body and do the replace on them. Below i'm doing this using a plugin i wrote 2 years ago that was meant for highlighting text. What i'm doing is finding all occurrences of and wrapping it in a span with the class currency-symbol, then i'm replacing the text of those spans.

最好的办法是在服务器端执行此操作或将货币符号包装在您可以选择的元素中,然后再将其返回到浏览器,但是,如果两者都不是选项,您可以选择正文中的所有文本节点并对其进行替换. 下面我使用我 2 年前写的一个插件来做这个,该插件用于突出显示文本。我正在做的是找到所有出现的事件并将其包装在一个带有类货币符号的跨度中,然后我将替换这些跨度的文本。

Demo

演示

(function($){

    $.fn.highlightText = function () {
        // handler first parameter
        // is the first parameter a regexp?
        var re,
            hClass,
            reStr,
            argType = $.type(arguments[0]),
            defaultTagName = $.fn.highlightText.defaultTagName;

        if ( argType === "regexp" ) {
            // first argument is a regular expression
            re = arguments[0];
        }       
        // is the first parameter an array?
        else if ( argType === "array" ) {
            // first argument is an array, generate
            // regular expression string for later use
            reStr = arguments[0].join("|");
        }       
        // is the first parameter a string?
        else if ( argType === "string" ) {
            // store string in regular expression string
            // for later use
            reStr = arguments[0];
        }       
        // else, return out and do nothing because this
        // argument is required.
        else {
            return;
        }

        // the second parameter is optional, however,
        // it must be a string or boolean value. If it is 
        // a string, it will be used as the highlight class.
        // If it is a boolean value and equal to true, it 
        // will be used as the third parameter and the highlight
        // class will default to "highlight". If it is undefined,
        // the highlight class will default to "highlight" and 
        // the third parameter will default to false, allowing
        // the plugin to match partial matches.
        // ** The exception is if the first parameter is a regular
        // expression, the third parameter will be ignored.
        argType = $.type(arguments[1]);
        if ( argType === "string" ) {
            hClass = arguments[1];
        }
        else if ( argType === "boolean" ) {
            hClass = "highlight";
            if ( reStr ) {
                reStr = "\b" + reStr + "\b";
            }
        }
        else {
            hClass = "highlight";
        }

        if ( arguments[2] && reStr ) {
            reStr = reStr = "\b" + reStr + "\b";
        } 

        // if re is not defined ( which means either an array or
        // string was passed as the first parameter ) create the
        // regular expression.
        if (!re) {
            re = new RegExp( "(" + reStr + ")", "ig" );
        }

        // iterate through each matched element
        return this.each( function() {
            // select all contents of this element
            $( this ).find( "*" ).andSelf().contents()

            // filter to only text nodes that aren't already highlighted
            .filter( function () {
                return this.nodeType === 3 && $( this ).closest( "." + hClass ).length === 0;
            })

            // loop through each text node
            .each( function () {
                var output;
                output = this.nodeValue
                    .replace( re, "<" + defaultTagName + " class='" + hClass + "'></" + defaultTagName +">" );
                if ( output !== this.nodeValue ) {
                    $( this ).wrap( "<p></p>" ).parent()
                        .html( output ).contents().unwrap();
                }
            });
        });
    };

    $.fn.highlightText.defaultTagName = "span";

})( jQuery );

$("body").highlightText("","currency-symbol");
$("span.currency-symbol").text("$");

回答by James

As you'll be using jQuery anyway, try:

由于您无论如何都会使用 jQuery,请尝试:

https://github.com/cowboy/jquery-replacetext

https://github.com/cowboy/jquery-replacetext

Then just do

然后就做

$("p").replaceText("£", "$")

It seems to do good job of only replacing text and not messing with other elements

它似乎在只替换文本而不与其他元素混淆方面做得很好

回答by Praxis Ashelin

You can use:

您可以使用:

str.replace(/text/g, "replaced text");

回答by Krishna

str.replace(/replacetext/g,'actualtext')

This replaces all instances of replacetextwith actualtext

这将替换的所有实例replacetextactualtext