HtmlSpecialChars是否等同于Javascript?


167

显然,这比我想象的要难找到。而且它是如此简单...

是否有等效于Javascript中内置的PHP htmlspecialchars的函数?我知道自己很容易实现,但是使用内置函数(如果可用)会更好。

对于那些不熟悉PHP的人,htmlspecialchars会将类似的内容<htmltag/>转换为&lt;htmltag/&gt;

我知道这一点escape()encodeURI()因此不能这样工作。


php有一些非常好的工具,var_dump,print_r,htmlspecialchars等。不幸的是,我怀疑与js并不相同。js警报太差了。看到一些意外的字符串(在警报框中不可见)的一种快速方法是警报字符串的长度,而不是字符串itslef。
梅尔西(Melsi)2014年


参见stackoverflow.com/a/12034334/8804293,它有一个很好的答案
Elijah Mock

Answers:


330

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

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]; });
}

5
这个函数的
好处

6
使用单个替换和映射功能更快,并且单个替换扩展性更好。(jsperf.com/escape-html-special-chars/11
jbo5112

1
@ jbo5112好点,我没有意识到JS允许使用回调进行替换。这段代码更容易理解,而且我怀疑,除非因某种原因连续调用数百次,否则将escapeHtml()缩短几毫秒会有所作为。
2014年

这将扭曲文本的URL,这使得它们无法像插件Autolinker.js。有什么办法可以解决这个问题吗?
RadekMatěj'17

4
@RadekMatěj即使在那种情况下,对于在HTML文档中使用的两个“&”号都可以进行编码是完全有效的(最好是我认为)。我仍然认为它是插件的错误。
Kip

31

那就是HTML编码。没有本机的javascript函数可以执行此操作,但是您可以使用google并完成一些很好的操作。

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

编辑:
这是我测试过:

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

输出: &lt;htmltag/&gt;


太糟糕了,我只需要使用自定义函数即可。
Bart van Heukelom

您可以在我帖子中包含的链接中尝试该方法。确实很整洁。
okw

@okw:好的,首先您要链接到此:yuki-onna.co.uk/html/encode.html,它确实encodeURIComponent执行操作,而根本不执行OP的要求。那你可以编辑吗?我似乎无法撤消-1。

是的,该页面的代码看起来合乎逻辑,但我没有对其进行测试。新链接虽然有效,但我已亲自对其进行了验证。我已经有一段时间更新了帖子。
okw

@BeauCielBleu:否。创建的唯一节点是单个div元素和文本节点。用文本`<img src = bogus onerror = alert(1337)>`创建文本节点只会创建一个文本节点,而不是img元素。
Tim Down

26

值得一读:http : //bigdingus.com/2007/12/29/html-escaping-in-javascript/

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;


3
这应该是被接受并获得最高投票的答案。我不确定为什么没有投票。在jsperf(jsperf.com/escape-html-special-chars/11)上使用长输入字符串(326KB Google搜索结果)和短输入字符串时,这是最快的基准测试。请对此投票。
jbo5112

这与获得最高票数的答案有什么区别?为什么还要附加内部功能?一种解释可以帮助用户更好地理解
Kosem

19

使用jQuery可以像这样:

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

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

如注释中所述,在此实现中,双引号和单引号保持原样。这意味着如果需要将element属性设置为原始html字符串,则不应使用此解决方案。


2
不知道是否有任何开销-向DOM添加虚拟对象吗?
基普(Kip)

还有其他优势(例如,如果您具有Unicode字符或其他字符)?
基普(Kip)

4
我发现的一些东西:双引号和单引号保持原样。如果要在属性值中使用它,这将带来问题。
Kip

1
对于小块文本,这需要30倍的时间,只要运行所有替换操作即可。它确实可以更好地扩展。与Google搜索结果页(326KB)一样巨大,它比替换或使用纯JavaScript的速度快25-30%。但是,它们始终都丢失了单个替换和映射功能。
jbo5112

4
人们如何在这个答案上投票:答案有jquery:+1-不能转义单引号和双引号:ummmm ..(抓头).. +1。<!-- Caps rage begin --> 该答案的得分为负,因为它甚至没有回答“ HtmlSpecialChars等效”问题。 <!-- Caps rage end -->它不会逃脱引用耶稣基督和其他神灵。天哪,你是jquery人。
2014年

19

这是一个转义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];});
}

6

Underscore.js为此提供了一个功能:

_.escape(string)

转义用于插入HTML的字符串,并替换&,<,>,“和”字符。

http://underscorejs.org/#escape

它不是内置的Javascript函数,但是如果您已经在使用Underscore,则它比写自己的函数(如果要转换的字符串不太大)更好。


5

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

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

请注意,指定的RegEx仅处理OP想要转义的特定字符,但是根据要使用转义的HTML的上下文,这些字符可能不足。Ryan Grove的文章对HTML进行转义的内容不仅仅包括&,<,>和“,这是该主题的不错阅读。而且,根据您的上下文,可能非常需要以下RegEx以避免XSS注入:

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

3
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());

3

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

Use innerText属性可以安全地将纯文本插入DOM中,并且比使用任何提供的转义功能要快得多。甚至比将静态预编码的字符串分配给还要innerHTML

使用classList到编辑类,dataset以一套data-属性和setAttribute他人。

所有这些都将为您处理转义。更准确地说,因为您正在处理DOM的文本表示形式HTML,所以不需要转义,并且在其下不会进行编码**。

// 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,则完全不必担心转义。


谢谢你!我已经花了很长时间寻找这样一个简单的解决方案。我发现的一件重要事情是,如果您的文本包含换行符,那么您将不得不用HTML换行符(如el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>'))替换它们,或将CSS white-space属性设置为prepre-wrap
stellatedHexahedron

@stellatedHexahedron,感谢您提出此问题。我已将答案更改为推荐innerText而不是textContent。虽然读取属性时速度较慢并且存在其他一些差异,但更为直观的​​是,<br>在分配属性时会自动进行替换。
用户

2

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

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

如果其他人正在维护它,则自己编写它是没有意义的。:)


1

我正在详细说明okw的答案。

您可以为此使用浏览器的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表示形式获取该内容。


0
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;
 }

0

希望这能因其性能而赢得比赛,最重要的是不要使用.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>'));

0

反转一:

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

问题不在于如何解码实体。这与问题的要求相反。
昆汀

这只会替换第一个实例&lt;,并&gr;在一个字符串。
昆汀

这只会解码必须转义的五个字符(非Unicode文档之外),不会解码可能转义的五个字符。
Quentin

这没有考虑分号何时是可选的规则。
Quentin

如果HTML To write a greater than sign in HTML type &amp;gt;显示:,它将显示错误,>而不是&gt;
Quentin

0

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

因此,下面是一个使用示例的函数:

function escapeHTML(unsafe) {
  return unsafe.replace(
    /[\u0000-\u002F]|[\u003A-\u0040]|[\u005B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).substr(-4, 4) + ';'
  )
}
document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('this should break it! " | / % * + , - / ; < = > ^') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
<div></div>


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

此解决方案使用字符的数字代码,例如<替换为&#60;

尽管其性能比使用map的解决方案稍差,但它具有以下优点:

  • 不依赖于库或DOM
  • 很容易记住(您不需要记住5个HTML转义字符)
  • 小码
  • 相当快(比5个链式替换还快)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.