我可以在 JavaScript 中转义 HTML 特殊字符吗?

Posted

技术标签:

【中文标题】我可以在 JavaScript 中转义 HTML 特殊字符吗?【英文标题】:Can I escape HTML special chars in JavaScript? 【发布时间】:2021-11-02 01:18:29 【问题描述】:

我想通过 javascript 函数将文本显示为 html。如何在 JavaScript 中转义 HTML 特殊字符?有 API 吗?

【问题讨论】:

这不是重复的,因为这个问题不涉及 jQuery。我只对这个感兴趣,因为我不使用 jQuery... HtmlSpecialChars equivalent in Javascript?的可能重复 请注意,浏览器正在使用new HTML Sanitizer API。 【参考方案1】:

这是一个几乎适用于所有网络浏览器的解决方案:

function escapeHtml(unsafe)

    return unsafe
         .replace(/&/g, "&")
         .replace(/</g, "&lt;")
         .replace(/>/g, "&gt;")
         .replace(/"/g, "&quot;")
         .replace(/'/g, "&#039;");
 

如果您只支持现代网络浏览器(2020+),那么您可以使用新的replaceAll 功能:

const escapeHtml = (unsafe) => 
    return unsafe.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&#039;');

【讨论】:

为什么是“'”而不是“’” ? 因为:***.com/questions/2083754/… 我认为replace() 调用中的正则表达式是不必要的。普通的旧单字符串也可以。 @StepanYakovenko 最好用 CSS 处理。事实上,用&amp;nbsp; 替换每个空格将防止空格上的文本中断(&amp;nbsp; 表示“不间断空格”)。 是否有任何标准 API 或者这是唯一的方法?【参考方案2】:

function escapeHtml(html)
  var text = document.createTextNode(html);
  var p = document.createElement('p');
  p.appendChild(text);
  return p.innerHTML;


// Escape while typing & print result
document.querySelector('input').addEventListener('input', e => 
  console.clear();
  console.log( escapeHtml(e.target.value) );
);
&lt;input style='width:90%; padding:6px;' placeholder='&amp;lt;b&amp;gt;cool&amp;lt;/b&amp;gt;'&gt;

【讨论】:

在这里工作,但不能在浏览器中离线工作 请注意,这不会转义引号("'),因此如果在 HTML 标记属性中使用此函数中的字符串,它们仍然会造成损坏。【参考方案3】:

你可以使用jQuery的.text() function。

例如:

http://jsfiddle.net/9H6Ch/

来自关于 .text() 函数的 jQuery 文档:

我们需要注意这个方法 转义提供为的字符串 必要的,以便它将呈现 在 HTML 中正确。为此,它调用 DOM 方法 .createTextNode(), 不将字符串解释为 HTML。

以前版本的 jQuery 文档是这样写的(强调):

我们需要注意,此方法会根据需要对提供的字符串进行转义,以便在 HTML 中正确呈现。为此,它调用 DOM 方法 .createTextNode(),将特殊字符替换为其 HTML 实体等效项(例如 &amplt; 表示

【讨论】:

如果您只想像这样转换,甚至可以在新元素上使用它:const str = "foo&lt;&gt;'\"&amp;"; $('&lt;div&gt;').text(str).html() 产生 foo&amp;lt;&amp;gt;'"&amp;amp; 请注意,这会留下引号 '" 未转义,这可能会绊倒您【参考方案4】:

使用Lodash:

_.escape('fred, barney, & pebbles');
// => 'fred, barney, &amp; pebbles'

Source code

【讨论】:

与此相反的是什么?与此相反的函数的名称? 下划线中的相同功能:underscorejs.org/#escape & underscorejs.org/#unescape【参考方案5】:

我想我找到了正确的方法......

// Create a DOM Text node:
var text_node = document.createTextNode(unescaped_text);

// Get the HTML element where you want to insert the text into:
var elem = document.getElementById('msg_span');

// Optional: clear its old contents
//elem.innerHTML = '';

// Append the text node into it:
elem.appendChild(text_node);

【讨论】:

我今天学到了一些关于 HTML 的新东西。 w3schools.com/jsref/met_document_createtextnode.asp. 请注意,如果您尝试像这样访问它,文本节点的内容不会被转义:document.createTextNode("&lt;script&gt;alert('Attack!')&lt;/script&gt;").textContent 如果您所做的只是设置文本,这是正确的方法。这也是 textContent 但显然它没有得到很好的支持。但是,如果您正在构建一个包含某些部分文本的字符串,那么这将不起作用,那么您仍然需要转义。 我真的很喜欢这个,因为它正确地使用了 DOM。与大多数其他选项相比,它感觉不那么“hacky”。​​【参考方案6】:

到目前为止,这是我见过的最快的方法。此外,它无需添加、删除或更改页面上的元素即可完成所有操作。

function escapeHTML(unsafeText) 
    let div = document.createElement('div');
    div.innerText = unsafeText;
    return div.innerHTML;

【讨论】:

警告:它不会转义引号,因此您不能在 HTML 代码中使用属性值内的输出。例如。 var divCode = '&lt;div data-title="' + escapeHTML('Jerry "Bull" Winston') + '"&gt;Div content&lt;/div&gt;' 将产生无效的 HTML! 使用div.textContent 而不是div.innerText 可能会更惯用。 只是想知道,反复调用这个最终会留下充满额外 div 元素的文档吗?还是会被垃圾回收?【参考方案7】:

找到更好的解决方案很有趣:

var escapeHTML = function(unsafe) 
  return unsafe.replace(/[&<"']/g, function(m) 
    switch (m) 
      case '&':
        return '&amp;';
      case '<':
        return '&lt;';
      case '"':
        return '&quot;';
      default:
        return '&#039;';
    
  );
;

我不解析 &gt;,因为它不会破坏结果中的 XML/HTML 代码。

以下是基准:http://jsperf.com/regexpairs 另外,我创建了一个通用的escape函数:http://jsperf.com/regexpairs2

【讨论】:

有趣的是,使用开关比地图快得多。我没想到会这样!感谢分享! Unicode 字符比你可能编码和考虑的要多得多。我根本不推荐这种手动方法。 为什么要转义多字节字符?只需在任何地方使用 UTF-8。 跳过 > 可能会破坏代码。您必须记住, 内部也是 html。在这种情况下,跳过 > 会中断。如果您只是在标签之间转义,那么您可能只需要转义 【参考方案8】:

显示未编码文本的最简洁和高效的方法是使用textContent 属性。

Faster 比使用 innerHTML。这还没有考虑转义开销。

document.body.textContent = 'a &lt;b&gt; c &lt;/b&gt;';

【讨论】:

@ZzZombo,它不适用于样式和脚本标签是完全正常的。当您向它们添加内容时,您添加的是 code,而不是 text,在这种情况下使用 innerHTML。此外,您不需要对其进行转义,这是两个不被解析为 HTML 的特殊标签。解析时,它们的内容被视为文本,直到满足结束序列&lt;/【参考方案9】:

DOM 元素支持通过分配给 innerText 将文本转换为 HTML。 innerText 不是一个函数,但分配给它就像文本被转义一样。

document.querySelectorAll('#id')[0].innerText = 'unsafe " String >><>';

【讨论】:

至少在 Chrome 中分配多行文本会添加 &lt;br&gt; 元素来代替换行符,这可能会破坏某些元素,例如样式或脚本。 createTextNode 不容易出现这个问题。 innerText 有一些遗留/规范问题。最好使用textContent【参考方案10】:

您可以对字符串中的每个字符进行编码:

function encode(e)return e.replace(/[^]/g,function(e)return"&#"+e.charCodeAt(0)+";")

或者只针对主要角色担心(&、inebreaks、、“和'),例如:

function encode(r)
return r.replace(/[\x26\x0A\<>'"]/g,function(r)return"&#"+r.charCodeAt(0)+";")


test.value=encode('How to encode\nonly html tags &<>\'" nice & fast!');

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
&lt;textarea id=test rows="9" cols="55"&gt;&amp;#119;&amp;#119;&amp;#119;&amp;#46;&amp;#87;&amp;#72;&amp;#65;&amp;#75;&amp;#46;&amp;#99;&amp;#111;&amp;#109;&lt;/textarea&gt;

【讨论】:

编写自己的转义函数通常是个坏主意。在这方面其他答案更好。【参考方案11】:

如果您已经在应用程序中使用模块,则可以使用escape-html 模块。

import escapeHtml from 'escape-html';
const unsafeString = '<script>alert("XSS");</script>';
const safeString = escapeHtml(unsafeString);

【讨论】:

【参考方案12】:

书本

OWASP recommends 表示“[e]除了字母数字字符,[您应该] 使用&amp;#xHH; 格式(或命名实体,如果可用)转义所有 ASCII 值小于 256 的字符,以防止切换出 [an ] 属性。”

所以这里有一个函数可以做到这一点,并带有一个用法示例:

function escapeHTML(unsafe) 
  return unsafe.replace(
    /[\u0000-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).slice(-4) + ';'
  )


document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('"fakeclass" onclick="alert("test")') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
&lt;div&gt;&lt;/div&gt;

您应该验证我提供的实体范围,以自己验证函数的安全性。你也可以使用这个正则表达式,它具有更好的可读性并且应该涵盖相同的字符代码,但在我的浏览器中性能降低了大约 10%:

/(?![0-9A-Za-z])[\u0000-\u00FF]/g

【讨论】:

【参考方案13】:

我在构建 DOM 结构时遇到了这个问题。这个问题帮我解决了。我想使用双 V 形作为路径分隔符,但添加一个新的文本节点直接导致显示转义字符代码,而不是字符本身:

var _div = document.createElement('div');
var _separator = document.createTextNode('&raquo;');
//_div.appendChild(_separator); /* This resulted in '&raquo;' being displayed */
_div.innerHTML = _separator.textContent; /* This was key */

【讨论】:

【参考方案14】:

只需在&lt;pre&gt;&lt;code class="html-escape"&gt;....&lt;/code&gt;&lt;/pre&gt; 之间编写代码即可。确保在代码标签中添加类名。它将转义所有用&lt;pre&gt;&lt;code class="html-escape"&gt;....&lt;/code&gt;&lt;/pre&gt;.

编写的 HTML sn-p

const escape = 
    '"': '&quot;',
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',

const codeWrappers = document.querySelectorAll('.html-escape')
if (codeWrappers.length > 0) 
    codeWrappers.forEach(code => 
        const htmlCode = code.innerHTML
        const escapeString = htmlCode.replace(/"|&|<|>/g, function (matched) 
            return escape[matched];
        );
        code.innerHTML = escapeString
    )
<pre>
    <code class="language-html html-escape">
        <div class="card">
            <div class="card-header-img" style="background-image: url('/assets/card-sample.png');"></div>
            <div class="card-body">
                <p class="card-title">Card Title</p>
                <p class="card-subtitle">Srcondary text</p>
                <p class="card-text">Greyhound divisively hello coldly wonderfully marginally far upon
                    excluding.</p>
                <button class="btn">Go to </button>
                <button class="btn btn-outline">Go to </button>
            </div>
        </div>
    </code>
</pre>

【讨论】:

【参考方案15】:

使用它从 JavaScript 中的字符串中删除 HTML 标记:

const strippedString = htmlString.replace(/(<([^>]+)>)/gi, "");

console.log(strippedString);

【讨论】:

转义不代表删除【参考方案16】:

试试这个,使用 prototype.js 库:

string.escapeHTML();

Try a demo

【讨论】:

这需要“prototype.js”库,这在演示中并未立即显现。 :(【参考方案17】:

我想出了这个解决方案。

假设我们要向元素中添加一些 HTML,其中包含来自用户或数据库的不安全数据。

var unsafe = 'some unsafe data like <script>alert("oops");</script> here';

var html = '';
html += '<div>';
html += '<p>' + unsafe + '</p>';
html += '</div>';

element.html(html);

对于 XSS 攻击是不安全的。现在添加: $(document.createElement('div')).html(unsafe).text();

原来如此

var unsafe = 'some unsafe data like <script>alert("oops");</script> here';

var html = '';
html += '<div>';
html += '<p>' + $(document.createElement('div')).html(unsafe).text(); + '</p>';
html += '</div>';

element.html(html);

对我来说,这比使用.replace() 容易得多,而且它会删除!!!所有可能的 HTML 标记(我希望如此)。

【讨论】:

这是一个危险的想法,它会将不安全的 HTML 字符串解析为 HTML,如果元素附加到 DOM,它将执行。改用 .innerText。 这不安全。它将&amp;lt;script&amp;gt; 转换为&lt;script&gt;

以上是关于我可以在 JavaScript 中转义 HTML 特殊字符吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 JavaScript 中转义 HTML 实体?

使用 Javascript 在 HTML5 数据属性中转义引号

rails 3 - 在javascript响应中转义部分生成的html

在java中转义javascript字符串

在 Rails 和 Javascript 中转义

如何在 JavaScript 中转义单引号 ( ' )? [复制]