使用jQuery转义HTML字符串

655

有没有人知道在jQuery中从字符串中轻松转义HTML的方法?我需要能够传递任意字符串并使其正确地转义以在HTML页面中显示(防止JavaScript/HTML注入攻击)。我相信可以扩展jQuery来做到这一点,但是我目前还不了解该框架足够的知识以实现此目标。


另请参见性能测试:https://jsperf.com/jquery-html-vs-text-vs-innerhtml-vs-innertext-textconte/8 - Christophe Roussy
27个回答

662

还有mustache.js提供的解决方案

var entityMap = {
  '&': '&',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#39;',
  '/': '&#x2F;',
  '`': '&#x60;',
  '=': '&#x3D;'
};

function escapeHtml (string) {
  return String(string).replace(/[&<>"'`=\/]/g, function (s) {
    return entityMap[s];
  });
}

抱歉打扰了,这个能否反转一下?我不懂正则表达式,所以需要帮助。 - Tony Patino
如果有单元测试会很方便,但这似乎是更好的解决方案。 - spy
如果字符串已经被转义了怎么办?此表达式匹配 &,除非它后面跟着 (1-8个字母后面跟着0-2个数字或者 # 后面跟着一个1-4位的十进制或十六进制数字) 再跟着 ;。 模式:/([<>"'`=\/]|&(?!([a-zA-Z]{1,8}\d{0,2}|#(\d{1,4}|x[a-zA-Z\d]{1,4}));))/g 用法:escapeHtml('&quot;This&quot;&#9;&#x3D;&#x9;a &v3ry; &dumb; quote.') 结果:'&quot;This&quot;&#9;&#x3D;&#x9;a &amp;v3ry; &dumb; quote.' 在 DOM 中:'"This"\t=\ta &amp;v3ry; &amp;dumb; quote.' 页面显示:"This" = a &v3ry; &dumb; quote. - Travis Bemrose

485

由于您正在使用jQuery,因此您可以直接设置元素的text属性:

// before:
// <div class="someClass">text</div>
var someHtmlString = "<script>alert('hi!');</script>";

// set a DIV's text:
$("div.someClass").text(someHtmlString);
// after: 
// <div class="someClass">&lt;script&gt;alert('hi!');&lt;/script&gt;</div>

// get the text in a string:
var escaped = $("<div>").text(someHtmlString).html();
// value: 
// &lt;script&gt;alert('hi!');&lt;/script&gt;

1
这安全吗?https://www.linkedin.com/pulse/htmlencode-htmldecode-jquery-roman-x-shafigullin/ - paaacman
@paaacman 使用jQuery设置属性使用.text().attr()是安全的,但像示例中那样构建HTML字符串肯定会遇到问题。 - travis

185

12
如上所述,该解决方案不能保留空格。 - geofflee

61

如果您需要对HTML进行转义,我只能想到三个非常必要的字符:

html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

根据你的使用情况,你可能还需要做一些像将"转换为&quot;之类的事情。如果列表变得足够大,我会使用数组:

var escaped = html;
var findReplace = [[/&/g, "&amp;"], [/</g, "&lt;"], [/>/g, "&gt;"], [/"/g, "&quot;"]]
for(var item in findReplace)
    escaped = escaped.replace(findReplace[item][0], findReplace[item][1]);

encodeURIComponent()只会将内容进行URL转义,而不是HTML转义。


14
如果所处理的 HTML 已经含有转义实体,这个正则表达式会产生奇怪的结果。例如,对 "Tom & Jerry" 进行转义会得到 "Tom &amp; Jerry"。 - Ryan
12
请使用var在本地声明item变量。无论如何,循环数组时不要使用for...in循环!请改用普通的for循环。另外,请注意使用encodeURIComponent而不是escapeURIComponent - Marcel Korpel
3
如果你正在使用标签属性,那么你还需要转义引号和/或双引号。在 PHP 的 htmlspecialchars 文档中包含了一个有用的转换列表。http://www.php.net/htmlspecialchars - geofflee
4
提醒新人,如果你的网站中有非英语字符,请不要使用这个。显然,由于带有重音符号(例如“é”)的字符无法正常显示:&eacute;。以下为参考的HTML实体列表:http://www.w3schools.com/tags/ref_entities.asp。 - LoganWolfer
13
值得注意的是,尽管这种解决方案存在无法正确处理已编码字符串的问题,但同样值得注意的是,这个页面上的大多数(可能所有)解决方案都存在这个问题。 - mklement0
显示剩余5条评论

42

使用下划线很容易:

_.escape(string) 

Underscore是一个实用程序库,它提供了许多本机js不提供的功能。还有lodash,它与underscore具有相同的API,但被重写为更高效。


2
而反之则是 _.unescape(string) - qräbnö

39

我写了一个小函数,可以实现以下功能。它只转义"&<>(但通常这就足够了)。相比之前提出的解决方案,它更加优雅,因为它只使用一个.replace()来完成所有转换。(编辑2:降低了代码复杂度,使函数变得更小、更简洁。如果您对原始代码感兴趣,请参见本答案结尾。)

function escapeHtml(text) {
    'use strict';
    return text.replace(/[\"&<>]/g, function (a) {
        return { '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' }[a];
    });
}

这是纯粹的JavaScript,没有使用jQuery。

同样需要转义 /'

针对mklement的评论做出的编辑。

上述函数可以轻松扩展以包括任何字符。要指定更多要转义的字符,只需将它们插入到正则表达式中的字符类中(即在/[...]/g中)并作为chr对象的一个条目。(编辑2: 用相同的方式缩短了这个函数。)

function escapeHtml(text) {
    'use strict';
    return text.replace(/[\"&'\/<>]/g, function (a) {
        return {
            '"': '&quot;', '&': '&amp;', "'": '&#39;',
            '/': '&#47;',  '<': '&lt;',  '>': '&gt;'
        }[a];
    });
}

请注意上面使用 &#39; 来代替撇号(也可以使用符号实体 &apos;,它在 XML 中被定义,但最初并未包含在 HTML 规范中,因此可能不受所有浏览器支持。参见:HTML 字符实体编码的维基百科文章)。我还记得在某处读到过,使用十进制实体比使用十六进制更受广泛支持,但现在似乎找不到这个来源了。(而且几乎没有浏览器不支持十六进制实体的情况。)

注意:/' 添加到转义字符列表中并不是特别有用,因为它们在 HTML 中没有任何特殊含义,也不需要被转义。

原始的escapeHtml函数

编辑 2:原始函数使用一个变量(chr)来存储.replace()回调所需的对象。该变量还需要一个额外的匿名函数来限定其作用域,使函数(不必要地)变得更大和更复杂。

var escapeHtml = (function () {
    'use strict';
    var chr = { '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' };
    return function (text) {
        return text.replace(/[\"&<>]/g, function (a) { return chr[a]; });
    };
}());

我还没有测试哪个版本更快。如果您测试了,请随意在此处添加信息和链接。


37

我意识到我来参加这个派对有点晚了,但我有一个非常简单的解决方案,不需要使用jQuery。

escaped = new Option(unescaped).innerHTML;

编辑:这不会转义引号。唯一需要转义引号的情况是如果内容将被粘贴到HTML字符串内的属性中。很难想象这样做会是一个好的设计。

编辑3:要获得最快的解决方案,请查看Saram上面的答案。这是最短的答案。


32

这是一个干净、清晰的JavaScript函数。它可以将文本转义,例如将 "a few < many" 转换为 "a few &lt; many"。

function escapeHtmlEntities (str) {
  if (typeof jQuery !== 'undefined') {
    // Create an empty div to use as a container,
    // then put the raw text in and get the HTML
    // equivalent out.
    return jQuery('<div/>').text(str).html();
  }

  // No jQuery, so use string replace.
  return str
    .replace(/&/g, '&amp;')
    .replace(/>/g, '&gt;')
    .replace(/</g, '&lt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

32

经过最近的测试,我可以推荐最快速且完全跨浏览器兼容的本地JavaScript(DOM)解决方案:fastestcross browser compatible native javaScript

function HTMLescape(html){
    return document.createElement('div')
        .appendChild(document.createTextNode(html))
        .parentNode
        .innerHTML
}

如果您将其重复多次,可以使用一次准备好的变量完成:

//prepare variables
var DOMtext = document.createTextNode("test");
var DOMnative = document.createElement("span");
DOMnative.appendChild(DOMtext);

//main work for each case
function HTMLescape(html){
  DOMtext.nodeValue = html;
  return DOMnative.innerHTML
}

看看我的最终表现比较堆栈问题)。


2
需要使用两个节点吗?只用一个节点怎么样:var p = document.createElement('p'); p.textContent = html; return p.innerHTML; - Dan Dascalescu
2
根据MDNtextContent函数仅被Chrome 1+,Firefox 2,IE9,Opera 9.64和Safari 3支持(后两者注明“可能更早”)。因此,它将破坏OP的“完全跨浏览器兼容”的声明。 - zb226
p.innerText = html; return p.innerHTML - Bekim Bacaj

24
尝试使用与jQuery兼容的Underscore.string库。
_.str.escapeHTML('<div>Blah blah blah</div>')

输出:

'&lt;div&gt;Blah blah blah&lt;/div&gt;'

20
主要的下划线库现在拥有一个“_.escape()”实用函数。 - codeape

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接