HtmlSpecialChars在JavaScript中等价于什么?

显然,这比我想象的要难找。它甚至是如此简单……

是否有一个函数相当于PHP的htmlspecialchars函数内置于JavaScript?我知道自己实现它相当容易,但如果可用的话,使用内置函数会更好。

对于那些不熟悉PHP的人来说,htmlspecialchars将诸如<htmltag/>之类的东西转换为&lt;htmltag/&gt;

我知道escape()encodeURI()不是这样工作的。

232748 次浏览

这就是HTML编码。没有原生javascript函数可以做到这一点,但你可以谷歌,并做一些漂亮的。

例如http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

< p > 编辑: < br > 这是我所测试的:

var div = document.createElement('div');
var text = document.createTextNode('<htmltag/>');
div.appendChild(text);
console.log(div.innerHTML);

输出:&lt;htmltag/&gt;

使用jQuery可以像这样:

var escapedValue = $('<div/>').text(value).html();

从相关问题使用jQuery转义HTML字符串

正如注释中提到的,双引号和单引号在此实现中保持原样。这意味着如果您需要将元素属性作为原始html字符串,则不应使用此解决方案。

您的解决方案代码有一个问题——它只转义每个特殊字符的第一次出现。例如:

escapeHtml('Kip\'s <b>evil</b> "test" code\'s here');
Actual:   Kip&#039;s &lt;b&gt;evil</b> &quot;test" code's here
Expected: Kip&#039;s &lt;b&gt;evil&lt;/b&gt; &quot;test&quot; code&#039;s here

下面是正常工作的代码:

function escapeHtml(text) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

更新

下面的代码将产生与上面相同的结果,但它的性能更好,特别是在大文本块上(感谢jbo5112)。

function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
  

return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

对于Node.js用户(或在浏览器中使用Jade运行时的用户),可以使用Jade的转义函数。

require('jade').runtime.escape(...);

如果别人在维护它,你自己写它就没有任何意义了。:)

值得一读: http://bigdingus.com/2007/12/29/html-escaping-in-javascript/ < / p >
escapeHTML: (function() {
var MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;',
"'": '&#39;'
};
var repl = function(c) { return MAP[c]; };
return function(s) {
return s.replace(/[&<>'"]/g, repl);
};
})()

请注意:只运行一次。不要在已经编码的字符串上运行,例如&amp;变成&amp;amp;

function htmlspecialchars(str) {
if (typeof(str) == "string") {
str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
str = str.replace(/"/g, "&quot;");
str = str.replace(/'/g, "&#039;");
str = str.replace(/</g, "&lt;");
str = str.replace(/>/g, "&gt;");
}
return str;
}

我希望这能赢得比赛,因为它的性能和最重要的不是使用.replace('&','&').replace('<','<')的链式逻辑…

var mapObj = {
'&':  "&amp;",
'<':  "&lt;",
'>':  "&gt;",
'"':  "&quot;",
'\'': "&#039;"
};
var re = new RegExp(Object.keys(mapObj).join("|"), "gi");


function escapeHtml(str)
{
return str.replace(re, function(matched)
{
return mapObj[matched.toLowerCase()];
});
}


console.log('<script type="text/javascript">alert('Hello World');</script>');
console.log(escapeHtml('<script type="text/javascript">alert('Hello World');</script>'));

使用:

String.prototype.escapeHTML = function() {
return this.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}

示例:

var toto = "test<br>";
alert(toto.escapeHTML());

Underscore.js为此提供了一个函数:

_.escape(string)

转义插入HTML的字符串,替换&, <, >, "和'字符。

http://underscorejs.org/#escape

它不是内置的JavaScript函数,但如果您已经在使用Underscore.js,如果要转换的字符串不是太大,那么它是比编写自己的函数更好的选择。

还有一种方法是完全放弃所有的字符映射,而是将所有不需要的字符转换为它们各自的数字字符引用,例如:

function escapeHtml(raw) {
return raw.replace(/[&<>"']/g, function onReplace(match) {
return '&#' + match.charCodeAt(0) + ';';
});
}

请注意,指定的RegEx只处理OP想要转义的特定字符,但是,根据转义HTML将要使用的上下文,这些字符可能是不够的。Ryan Grove的文章HTML转义不仅仅是&, <, >和”是一个很好的话题。根据您的上下文,为了避免XSS注入,很可能需要以下RegEx:

var regex = /[&<>"'` !@$%()=+{}[\]]/g

反一:

function decodeHtml(text) {
return text
.replace(/&amp;/g, '&')
.replace(/&lt;/ , '<')
.replace(/&gt;/, '>')
.replace(/&quot;/g,'"')
.replace(/&#039;/g,"'");
}

这里有一个转义HTML的函数:

function escapeHtml(str)
{
var map =
{
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return str.replace(/[&<>"']/g, function(m) {return map[m];});
}

为了解码:

function decodeHtml(str)
{
var map =
{
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#039;': "'"
};
return str.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, function(m) {return map[m];});
}

你可能不需要这样的函数。由于您的代码已经在浏览器中*,您可以直接访问DOM,而不是生成和编码HTML,浏览器必须向后解码才能实际使用。

使用innerText属性可以安全地将纯文本插入到DOM中,并且比使用任何给出的转义函数都快得多。甚至也比将静态预编码字符串赋值给innerHTML要好。

使用classList编辑类,使用dataset设置data-属性,使用setAttribute设置其他属性。

所有这些都能帮你逃脱。更准确地说,不需要转义,也不需要在**下面执行编码,因为您正在处理HTML (DOM的文本表示)。

// use existing element
var author = 'John "Superman" Doe <john@example.com>';
var el = document.getElementById('first');
el.dataset.author = author;
el.textContent = 'Author: '+author;


// or create a new element
var a = document.createElement('a');
a.classList.add('important');
a.href = '/search?q=term+"exact"&n=50';
a.textContent = 'Search for "exact" term';
document.body.appendChild(a);


// actual HTML code
console.log(el.outerHTML);
console.log(a.outerHTML);
.important { color: red; }
<div id="first"></div>

*这个答案不是为服务器端JavaScript用户准备的(Node.js, 等。)

**除非你之后显式地将其转换为实际的HTML。例如,通过访问innerHTML -这是当你运行其他答案中建议的$('<div/>').text(value).html();时发生的事情。因此,如果您的最终目标是将一些数据插入到文档中,那么通过这种方式,您将完成两次工作。此外,您还可以看到,在生成的HTML中,并不是所有内容都进行了编码,只有有效所需的最小值进行了编码。它是上下文依赖的,这就是为什么这个jQuery方法不编码引号,因此不应该用作通用的逃脱器。当您将HTML构造为一个字符串,在属性值的位置使用不受信任的数据或包含引号的数据时,需要使用引号转义。如果使用DOM API,则根本不必关心转义。

function htmlEscape(str){
return str.replace(/[&<>'"]/g,x=>'&#'+x.charCodeAt(0)+';')
}

这个解决方案使用字符的数字代码,例如<&#60;取代。

虽然它的性能比使用地图的解决方案略差,但它有以下优点:

  • 不依赖于库或DOM
  • 非常容易记住(你不需要记住5个HTML转义字符)
  • 少的代码
  • 相当快(仍然比5个链式替换快)

我正在详细说明一点o.k.w。的回答

为此,您可以使用浏览器的DOM函数。

var utils = {
dummy: document.createElement('div'),
escapeHTML: function(s) {
this.dummy.textContent = s
return this.dummy.innerHTML
}
}


utils.escapeHTML('<escapeThis>&')

返回&lt;escapeThis&gt;&amp;

它使用标准函数createElement创建一个不可见元素,然后使用函数textContent设置任意字符串作为其内容,然后使用innerHTML获取其HTML表示形式的内容。

照章办事

OWASP建议, [e]除字母数字字符外,[你应该]转义所有ASCII值小于256的字符,使用&#xHH;格式(或命名实体,如果可用),以防止切换出[一个]属性。

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

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>'
<div></div>

You should verify the entity ranges I have provided to validate the safety of the function yourself. You could also use this regular expression which has better readability and should cover the same character codes, but is about 10% less performant in my browser:

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

这与这个问题没有直接关系,但在JS中可以通过:

> String.fromCharCode(8212);
> "—"

这也适用于TypeScript。

// Codificamos los caracteres: &, <, >, ", '
function encodeHtml(str) {


var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};


return str.replace(/[&<>"']/g, function(m) {return map[m];});
}


// Decodificamos los caracteres: &amp; &lt; &gt; &quot; &#039;
function decodeHtml(str) {


var map = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#039;': "'"
};


return str.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, function(m) {return map[m];});
}


var str = `atttt ++ ' ' " " " " " + {}-´ñ+.'aAAAaaaa"`;


var str2 = `atttt ++ &#039; &#039; &quot; &quot; &quot; &quot; &quot; + {}-´ñ+.&#039;aAAAaaaa&quot;`;




console.log(encodeHtml(str));
console.log(decodeHtml(str2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="string">
<b>- String de entrada</b>: atttt ++ ' ' " " " " " + {}-´ñ+.'aAAAaaaa"
<br>
- mira la consola 👇
</div>