HTML实体解码

347

如何使用JavaScript或JQuery对HTML实体进行编码和解码?

var varTitle = "Chris' corner";

我希望它是:

var varTitle = "Chris' corner";

16
请参考这个回答:点击此处。看起来比下面给出的更好。 - Déjà vu
1
还可以查看 npm 上的 ent 模块!https://github.com/substack/node-ent - TehShrike
1
我认为@ringø(哇,用户名奇怪地相似...)的意思是要链接到这个答案 - rinogo
3
我认为这个答案更好。显然,he库专门为此目的而设计。你可能可以通过像这里大多数答案中的自定义实现来节省几行代码,但它们都在某种程度上存在限制。 - Mr5o1
更简洁的方式: https://dev59.com/o3I-5IYBdhLWcg3wVWpi#64587244 - weiya ou
17个回答

312
我建议不要使用被接受为答案的jQuery代码。虽然它没有将需要解码的字符串插入到页面中,但会创建一些脚本和HTML元素。这比我们需要的代码多得多。相反,我建议使用更安全、更优化的函数。
var decodeEntities = (function() {
  // this prevents any overhead from creating the object each time
  var element = document.createElement('div');

  function decodeHTMLEntities (str) {
    if(str && typeof str === 'string') {
      // strip script/html tags
      str = str.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
      str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');
      element.innerHTML = str;
      str = element.textContent;
      element.textContent = '';
    }

    return str;
  }

  return decodeHTMLEntities;
})();

http://jsfiddle.net/LYteC/4/

要使用此功能,只需调用decodeEntities("&amp;"),它将使用与jQuery版本相同的基础技术——但没有jQuery的开销,并在清理输入中的HTML标签后进行。请参见Mike Samuel's comment有关如何过滤HTML标签的接受答案的评论。
通过在项目中添加以下行,可以轻松地将此功能用作jQuery插件。
jQuery.decodeEntities = decodeEntities;

9
注意:IE8不支持textContent属性,因此如果您的目标浏览器之一仍然是IE8,则必须找到另一种解决方案。我刚刚浪费了一个小时来尝试弄清楚这个问题,因为我们需要专门解码实体来补偿另一个IE8的错误。 - Greg Charles
@RobertK -- 我用了这个方法:jQuery('<div />').html(str).text();,所以 jQuery 的开发人员找到了一种方法。我已经在页面上使用了 jQuery,但如果真的不需要它,你可以进入代码并查看他们是如何实现的。 - Greg Charles
6
小心使用删除HTML标签的代码行。你不应该在处理HTML/XML时使用正则表达式。Bobince早已明确了这一点。 - Qix - MONICA WAS MISTREATED
1
虽然这很好,但用户应该意识到它非常危险。它可能看起来已经正确地剥离了“危险的东西”,但它很容易被攻破。除非你喜欢被 XSS 攻击,请不要在不可信的用户输入上使用此功能。 - goat
3
@Qix,我不完全理解这里的问题。HTML/XML绝对不应该像人们经常做的那样使用正则表达式进行“解析”。如果您只是尝试对其进行标记化,则据我所知,正则表达式确实是一个理想的解决方案。除非我错过了什么,否则完全剥离标记不需要超出词法分析之外的任何东西,因此在这里使用正则表达式没有任何好处。 - Darren Ringer
显示剩余13条评论

260

你可以尝试类似以下的方法:

var Title = $('<textarea />').html("Chris&apos; corner").text();
console.log(Title);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

JS Fiddle

更互动版本:

$('form').submit(function() {
  var theString = $('#string').val();
  var varTitle = $('<textarea />').html(theString).text();
  $('#output').text(varTitle);
  return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="#" method="post">
  <fieldset>
    <label for="string">Enter a html-encoded string to decode</label>
    <input type="text" name="string" id="string" />
  </fieldset>
  <fieldset>
    <input type="submit" value="decode" />
  </fieldset>
</form>

<div id="output"></div>

JS Fiddle


6
@chris 和 @david - 这段代码创建了一个空的 div(与 DOM 分离),设置它的 innerHTML,最后将其作为普通文本检索。它不是“用 DIV 包围它”,而是“把它放在一个 div 中”。我强调这一点非常重要,因为理解 jQuery 如何工作至关重要。 - Christian
41
不要在不可信的数据中使用此方法,请查看迈克在此处的评论:https://dev59.com/KHM_5IYBdhLWcg3w9oaG#2Z-dEYcBWogLw_1bFytL - salmatron
它很好,但并不总是有效。它只在 div 标签内起作用,但例如,如果您输入一些 PHP 或 CSS 而没有 HTML,则会让其通过。 - Paul Ledger
5
只是想插一句话。这个容易受到XSS攻击,试试看吧!https://dev59.com/5Yzda4cB1Zd3GeqPqsb5 - actual_kangaroo
2
这可能会对旧版本的jQuery容易受到XSS攻击(在此处查看更多信息)。我建议使用he库。您可以在另一个类似问题的答案中看到代码示例。 - ands
显示剩余5条评论

134

编辑2023年:现在你应该使用像he这样的库。

像Robert K说的那样,不要使用jQuery.html().text()来解码HTML实体,因为这是不安全的,因为用户输入永远不应该访问DOM。阅读关于XSS为什么不安全的内容。

而是尝试使用Underscore.js实用工具库,它带有escapeunescape方法:

_.escape(string)

转义字符串以便插入到HTML中,替换&<>"`'字符。
_.escape('Curly, Larry & Moe');
=> "Curly, Larry &amp; Moe"

_.unescape(string)

這是一個將逃逸字符替換為其未逃逸對應字符的過程,包括替換 &amp;&lt;&gt;&quot;&#96;&#x27;
_.unescape('Curly, Larry &amp; Moe');
=> "Curly, Larry & Moe"

为了支持解码更多的字符,只需复制下划线unescape方法,并将更多的字符添加到映射中。

2
@chovy,请使用最新的Underscore.js版本>=1.4.2,这样就不会出现TypeError错误了。 - Alan Hamlett
3
我喜欢这个答案,因为它不需要 DOM,在编写 JavaScript 时现在谁能保证访问 DOM API 呢?但不幸的是,它只适用于列出的实体,并且会将像   这样的内容保持不变。 - trey-jones
1
使用源代码控制库而不是从顶部的Stack Overflow答案中复制和粘贴一些随机代码,这是一个好习惯。如果JavaScript标准库也有这种低级函数就好了。 - Michael Bylstra
5
请记住,它无法解码编码的俄语或日语字符,例如 ハローワールド -> ハローワールド 不能使用此方法完成。 - daghan
7
_.unescape 只能处理少数几个值。所以像 _.unescape('&raquo;') 这样的用法仅会返回"&raquo;" - Dylan
显示剩余4条评论

107

这里是原作者的答案。

这是我最喜欢的解码HTML字符的方法。使用此代码的优点是标记也会被保留。

function decodeHtml(html) {
    var txt = document.createElement("textarea");
    txt.innerHTML = html;
    return txt.value;
}

例子:http://jsfiddle.net/k65s3/

输入:

Entity:&nbsp;Bad attempt at XSS:<script>alert('new\nline?')</script><br>

输出:

Entity: Bad attempt at XSS:<script>alert('new\nline?')</script><br>

2
这种方法在任何地方都适用,即使jquery不可用或尚未加载,因为它是纯javascript。 - IamSalik
2
这种技术有什么缺点吗?它似乎比上面的答案要容易得多。 - anthonygood
6
下次@insign请标明原作者或提供链接。https://dev59.com/o2s05IYBdhLWcg3wLe8j#7394787 - geauser
2
@geauser 是的,已完成。 - insign
1
这是整个列表中最好、最有用的答案。感谢 @insign。 - Samagra Singh Tomar
显示剩余6条评论

59
这是一个不需要创建 div 的快速方法,并解码“最常见”的 HTML 转义字符:

function decodeHTMLEntities(text) {
    var entities = [
        ['amp', '&'],
        ['apos', '\''],
        ['#x27', '\''],
        ['#x2F', '/'],
        ['#39', '\''],
        ['#47', '/'],
        ['lt', '<'],
        ['gt', '>'],
        ['nbsp', ' '],
        ['quot', '"']
    ];

    for (var i = 0, max = entities.length; i < max; ++i)
        text = text.replace(new RegExp('&' + entities[i][0] + ';', 'g'), entities[i][1]);

    return text;
}

console.log(decodeHTMLEntities('&amp; &quot;'));


15
对于大多数的 HTML 实体,你的答案并不奏效,将其扩展以包含这些实体将会十分重复和容易出错。例如,对于每个日语汉字字符都有一个实体,而这样的字符有几千个。此外,到那时,我不会感到惊讶如果你的答案比其他一些答案都要慢,因为你需要为每个字符串运行成千上万次的替换和正则表达式以进行解码。 - mmitchell
2
当你编码这些字符串时,它真的取决于你的目的。如果你的目标是通过像<或>这样的东西来避免触发HTML处理,那么完全没有必要通过字符实体语法对其他字符进行编码。大量的字符实体主要作为一种方便工具。我列出的实体是你必须转义的最少量,以避免数据与HTML混淆。 - William Lahti
1
关于速度问题,你提到了运行多个正则表达式的好点子。但是,当然,由于你把每个字符实体都放进那段代码中的想法毫无意义,而且说实话,真的很愚蠢,所以这不是问题。不过,可以使用 | 字符生成正则表达式并进行单个 replace() 调用。我认为你需要对其进行基准测试以查看哪种方法更快,但我的直觉告诉我,在 JavaScript 中,由于函数调用开销很高,使用 | 进行一次 replace() 会更快。 - William Lahti
1
好的,那么你的解决方案是不完整的。如果你在这方面做出了任何假设,那么应该在答案中注明,因为 OP 从未说明他们为什么要编码他们的 HTML 实体。 - mmitchell
1
当您尝试在JavaScript中复制htmlspecialchars_decode时,此代码块已足够完整。它并不打算复制html_entity_decode。我发现在这个主题上有很多噪音和许多臃肿/不安全的方法。这是编码伙伴,与Kip和Chris Jacob提供的出色编码解决方案相对应:https://dev59.com/SnI-5IYBdhLWcg3wkJQA。 - MichaelClark
显示剩余3条评论

37

这里是另一个版本:

function convertHTMLEntity(text){
    const span = document.createElement('span');

    return text
    .replace(/&[#A-Za-z0-9]+;/gi, (entity,position,text)=> {
        span.innerHTML = entity;
        return span.innerText;
    });
}

console.log(convertHTMLEntity('Large &lt; &#163; 500'));


由于您正在匹配A-Za-z,因此是否需要不区分大小写的选项? - tigrou
@tigrou 不需要,您可以删除该选项。 - Mirodil
这是最好的版本,谢谢! - Karambit
这是最好的答案。我会点赞它。 - Piyush
优秀的解决方案,但仍然不确定它是如何工作的。请解释一下你的代码。 - MosesK

22

受 Robert K 的解决方案启发,这个版本不会去掉 HTML 标签,并且同样安全。

var decode_entities = (function() {
    // Remove HTML Entities
    var element = document.createElement('div');

    function decode_HTML_entities (str) {

        if(str && typeof str === 'string') {

            // Escape HTML before decoding for HTML Entities
            str = escape(str).replace(/%26/g,'&').replace(/%23/g,'#').replace(/%3B/g,';');

            element.innerHTML = str;
            if(element.innerText){
                str = element.innerText;
                element.innerText = '';
            }else{
                // Firefox support
                str = element.textContent;
                element.textContent = '';
            }
        }
        return unescape(str);
    }
    return decode_HTML_entities;
})();

6
那些escape()unescape()函数已经被弃用了。 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/escape - Ben Creasy

16

jQuery提供了一种编码和解码html实体的方法。

如果你使用"

"标签,它会剥离掉所有的html。

function htmlDecode(value) {
    return $("<div/>").html(value).text();
}

function htmlEncode(value) {
    return $('<div/>').text(value).html();
}
如果使用"

1
喜欢它,对我很有效,在Chrome控制台中测试过了,确实<script>标签和任何属性都被完全删除了。 - OzzyTheGiant
1
我也喜欢这个解决方案。用纯JavaScript的方法是创建一个div,使用var div = document.createElement('div');,然后设置innerHTML并获取innerText以进行反转义;对于转义,则相反。 - bozdoz
jQuery的 text() 方法会剥离无效的HTML,例如在使用表格行时。 - Tim Vermaelen

13
为了增加一点"灵感来自Robert K"的内容到列表中,这里有另一个安全版本,它不会剥离HTML标签。它不是将整个字符串通过HTML解析器运行,而是仅提取实体并转换它们。
var decodeEntities = (function() {
    // this prevents any overhead from creating the object each time
    var element = document.createElement('div');

    // regular expression matching HTML entities
    var entity = /&(?:#x[a-f0-9]+|#[0-9]+|[a-z0-9]+);?/ig;

    return function decodeHTMLEntities(str) {
        // find and replace all the html entities
        str = str.replace(entity, function(m) {
            element.innerHTML = m;
            return element.textContent;
        });

        // reset the value
        element.textContent = '';

        return str;
    }
})();

11

这是完整版本。

function htmldecode(s){
    window.HTML_ESC_MAP = {
    "nbsp":" ","iexcl":"¡","cent":"¢","pound":"£","curren":"¤","yen":"¥","brvbar":"¦","sect":"§","uml":"¨","copy":"©","ordf":"ª","laquo":"«","not":"¬","reg":"®","macr":"¯","deg":"°","plusmn":"±","sup2":"²","sup3":"³","acute":"´","micro":"µ","para":"¶","middot":"·","cedil":"¸","sup1":"¹","ordm":"º","raquo":"»","frac14":"¼","frac12":"½","frac34":"¾","iquest":"¿","Agrave":"À","Aacute":"Á","Acirc":"Â","Atilde":"Ã","Auml":"Ä","Aring":"Å","AElig":"Æ","Ccedil":"Ç","Egrave":"È","Eacute":"É","Ecirc":"Ê","Euml":"Ë","Igrave":"Ì","Iacute":"Í","Icirc":"Î","Iuml":"Ï","ETH":"Ð","Ntilde":"Ñ","Ograve":"Ò","Oacute":"Ó","Ocirc":"Ô","Otilde":"Õ","Ouml":"Ö","times":"×","Oslash":"Ø","Ugrave":"Ù","Uacute":"Ú","Ucirc":"Û","Uuml":"Ü","Yacute":"Ý","THORN":"Þ","szlig":"ß","agrave":"à","aacute":"á","acirc":"â","atilde":"ã","auml":"ä","aring":"å","aelig":"æ","ccedil":"ç","egrave":"è","eacute":"é","ecirc":"ê","euml":"ë","igrave":"ì","iacute":"í","icirc":"î","iuml":"ï","eth":"ð","ntilde":"ñ","ograve":"ò","oacute":"ó","ocirc":"ô","otilde":"õ","ouml":"ö","divide":"÷","oslash":"ø","ugrave":"ù","uacute":"ú","ucirc":"û","uuml":"ü","yacute":"ý","thorn":"þ","yuml":"ÿ","fnof":"ƒ","Alpha":"Α","Beta":"Β","Gamma":"Γ","Delta":"Δ","Epsilon":"Ε","Zeta":"Ζ","Eta":"Η","Theta":"Θ","Iota":"Ι","Kappa":"Κ","Lambda":"Λ","Mu":"Μ","Nu":"Ν","Xi":"Ξ","Omicron":"Ο","Pi":"Π","Rho":"Ρ","Sigma":"Σ","Tau":"Τ","Upsilon":"Υ","Phi":"Φ","Chi":"Χ","Psi":"Ψ","Omega":"Ω","alpha":"α","beta":"β","gamma":"γ","delta":"δ","epsilon":"ε","zeta":"ζ","eta":"η","theta":"θ","iota":"ι","kappa":"κ","lambda":"λ","mu":"μ","nu":"ν","xi":"ξ","omicron":"ο","pi":"π","rho":"ρ","sigmaf":"ς","sigma":"σ","tau":"τ","upsilon":"υ","phi":"φ","chi":"χ","psi":"ψ","omega":"ω","thetasym":"ϑ","upsih":"ϒ","piv":"ϖ","bull":"•","hellip":"…","prime":"′","Prime":"″","oline":"‾","frasl":"⁄","weierp":"℘","image":"ℑ","real":"ℜ","trade":"™","alefsym":"ℵ","larr":"←","uarr":"↑","rarr":"→","darr":"↓","harr":"↔","crarr":"↵","lArr":"⇐","uArr":"⇑","rArr":"⇒","dArr":"⇓","hArr":"⇔","forall":"∀","part":"∂","exist":"∃","empty":"∅","nabla":"∇","isin":"∈","notin":"∉","ni":"∋","prod":"∏","sum":"∑","minus":"−","lowast":"∗","radic":"√","prop":"∝","infin":"∞","ang":"∠","and":"∧","or":"∨","cap":"∩","cup":"∪","int":"∫","there4":"∴","sim":"∼","cong":"≅","asymp":"≈","ne":"≠","equiv":"≡","le":"≤","ge":"≥","sub":"⊂","sup":"⊃","nsub":"⊄","sube":"⊆","supe":"⊇","oplus":"⊕","otimes":"⊗","perp":"⊥","sdot":"⋅","lceil":"⌈","rceil":"⌉","lfloor":"⌊","rfloor":"⌋","lang":"〈","rang":"〉","loz":"◊","spades":"♠","clubs":"♣","hearts":"♥","diams":"♦","\"":"quot","amp":"&","lt":"<","gt":">","OElig":"Œ","oelig":"œ","Scaron":"Š","scaron":"š","Yuml":"Ÿ","circ":"ˆ","tilde":"˜","ndash":"–","mdash":"—","lsquo":"‘","rsquo":"’","sbquo":"‚","ldquo":"“","rdquo":"”","bdquo":"„","dagger":"†","Dagger":"‡","permil":"‰","lsaquo":"‹","rsaquo":"›","euro":"€"};
    if(!window.HTML_ESC_MAP_EXP)
        window.HTML_ESC_MAP_EXP = new RegExp("&("+Object.keys(HTML_ESC_MAP).join("|")+");","g");
    return s?s.replace(window.HTML_ESC_MAP_EXP,function(x){
        return HTML_ESC_MAP[x.substring(1,x.length-1)]||x;
    }):s;
}

使用方法

htmldecode("&sum;&nbsp;&gt;&euro;");

1
小心处理 nbsp 字符,我不得不手动替换它,因为这个例子使用了普通空格。 - Guillaume Malartre
1
那么像 &#232; 这样的字符呢? - Kind Contributor

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