Unescape HTML实体在JavaScript?

我有一些与XML-RPC后端通信的JavaScript代码。 XML-RPC返回格式为

的字符串
<img src='myimage.jpg'>

然而,当我使用JavaScript将字符串插入到HTML中时,它们会逐字呈现。我看到的不是图像,而是字符串:

<img src='myimage.jpg'>

我猜想HTML是通过XML-RPC通道转义的。

如何在JavaScript中解除字符串转义?我尝试了本页上的技术,不成功:http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

诊断这个问题的其他方法是什么?

298801 次浏览

编辑:你应该使用DOMParser API作为Wladimir建议,我编辑了我之前的答案,因为发布的函数引入了安全漏洞。

下面的代码片段是老答案的代码,只做了一些小修改:使用textarea而不是div减少了XSS漏洞,但在IE9和Firefox中仍然有问题。

function htmlDecode(input){
var e = document.createElement('textarea');
e.innerHTML = input;
// handle case of empty input
return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}


htmlDecode("&lt;img src='myimage.jpg'&gt;");
// returns "<img src='myimage.jpg'>"

基本上,我以编程方式创建了一个DOM元素,将编码的HTML分配给它的innerHTML,并从innerHTML插入上创建的文本节点检索nodeValue。因为它只是创建了一个元素,而没有添加它,所以没有修改站点HTML。

它将跨浏览器(包括旧的浏览器)工作,并接受所有HTML字符实体

编辑:此代码的旧版本不能在有空白输入的IE上工作,如这里在jsFiddle(在IE中查看)所示。上面的版本适用于所有输入。

更新:这似乎不适用于大字符串,它还引入了安全漏洞,见注释。

不是对你的问题的直接回应,但它不是更好为您的RPC返回一些结构(是XML或JSON或其他)与那些图像数据(在您的例子中的url)在该结构?

然后你可以在javascript中解析它,并使用javascript本身构建<img>

你从RPC接收到的结构可能是这样的:

{"img" : ["myimage.jpg", "myimage2.jpg"]}

我认为这样更好,因为将来自外部源代码的代码注入您的页面看起来不太安全。想象一下,有人劫持了您的XML-RPC脚本,并在其中放入了一些您不想要的东西(甚至是一些javascript……)

如果你正在使用jQuery:

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

否则,使用严格的软件编码器对象,它具有出色的htmlDecode()函数。

你需要解码所有编码的HTML实体或只是&amp;本身吗?

如果你只需要处理&amp;,那么你可以这样做:

var decoded = encoded.replace(/&amp;/g, '&');

如果你需要解码所有HTML实体,那么你可以不使用jQuery:

var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;

请注意下面Mark的评论,他强调了这个答案的早期版本中的安全漏洞,并建议使用textarea而不是div来减轻潜在的XSS漏洞。无论使用jQuery还是纯JavaScript,这些漏洞都存在。

克里斯的回答很好。优雅,但如果value为未定义的则失败。简单的改进就能让它稳固:

function htmlDecode(value) {
return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}

element.innerText也可以。

首先在body的某处创建<span id="decodeIt" style="display:none;"></span>

接下来,将要解码为innerHTML的字符串赋值如下:

document.getElementById("decodeIt").innerHTML=stringtodecode

最后,

stringtodecode=document.getElementById("decodeIt").innerText

下面是整个代码:

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText
var htmlEnDeCode = (function() {
var charToEntityRegex,
entityToCharRegex,
charToEntity,
entityToChar;


function resetCharacterEntities() {
charToEntity = {};
entityToChar = {};
// add the default set
addCharacterEntities({
'&amp;'     :   '&',
'&gt;'      :   '>',
'&lt;'      :   '<',
'&quot;'    :   '"',
'&#39;'     :   "'"
});
}


function addCharacterEntities(newEntities) {
var charKeys = [],
entityKeys = [],
key, echar;
for (key in newEntities) {
echar = newEntities[key];
entityToChar[key] = echar;
charToEntity[echar] = key;
charKeys.push(echar);
entityKeys.push(key);
}
charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
}


function htmlEncode(value){
var htmlEncodeReplaceFn = function(match, capture) {
return charToEntity[capture];
};


return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
}


function htmlDecode(value) {
var htmlDecodeReplaceFn = function(match, capture) {
return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
};


return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
}


resetCharacterEntities();


return {
htmlEncode: htmlEncode,
htmlDecode: htmlDecode
};
})();

这是ExtJS的源代码。

CMS的答案很好,除非你想要取消转义的HTML非常长,超过65536个字符。因为在Chrome中,内部HTML被分割成许多子节点,每个子节点最长65536个,你需要将它们连接起来。这个函数也适用于很长的字符串:

function unencodeHtmlContent(escapedHtml) {
var elem = document.createElement('div');
elem.innerHTML = escapedHtml;
var result = '';
// Chrome splits innerHTML into many child nodes, each one at most 65536.
// Whereas FF creates just one single huge child node.
for (var i = 0; i < elem.childNodes.length; ++i) {
result = result + elem.childNodes[i].nodeValue;
}
return result;
}

有关更多信息,请参阅这个关于innerHTML最大长度的答案

Matthias Bynens有一个这样的库:https://github.com/mathiasbynens/he

例子:

console.log(
he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ")
);
// Logs "Jörg & Jürgen rocked to & fro"

我建议使用它,而不是设置元素的HTML内容,然后读取它的文本内容。这种方法是可行的,但如果用于不可信的用户输入,则具有欺骗性的危险,并提供XSS机会。

如果你真的不能忍受在库中加载,你可以使用这个答案中描述的textarea黑客来解决一个几乎重复的问题,与已经建议的各种类似方法不同,它没有我所知道的安全漏洞:

function decodeEntities(encodedString) {
var textArea = document.createElement('textarea');
textArea.innerHTML = encodedString;
return textArea.value;
}


console.log(decodeEntities('1 &amp; 2')); // '1 & 2'

但是请注意安全问题,影响类似的方法,我在链接的答案中列出!这种方法是一种hack,将来对textarea的允许内容的更改(或特定浏览器中的错误)可能会导致依赖于它的代码有一天突然出现XSS漏洞。

这里给出的大多数答案都有一个巨大的缺点:如果你试图转换的字符串不受信任,那么你将最终得到跨站脚本(XSS)漏洞。对于接受的答案中的函数,考虑以下内容:

htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

这里的字符串包含一个未转义的HTML标记,因此htmlDecode函数将实际运行字符串中指定的JavaScript代码,而不是解码任何内容。

这可以通过使用DOMParser来避免,它在所有现代浏览器中得到了支持:

function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}


console.log(  htmlDecode("&lt;img src='myimage.jpg'&gt;")  )
// "<img src='myimage.jpg'>"


console.log(  htmlDecode("<img src='dummy' onerror='alert(/xss/)'>")  )
// ""

该函数保证不会运行任何JavaScript代码作为副作用。任何HTML标记将被忽略,只返回文本内容。

兼容性注意:使用DOMParser解析HTML至少需要Chrome 30、Firefox 12、Opera 17、Internet Explorer 10、Safari 7.1或Microsoft Edge。因此,所有没有支持的浏览器都已经超过了它们的EOL,截至2017年,唯一能在野外看到的是旧的Internet Explorer和Safari版本(通常这些版本仍然不够多)。

jQuery将为您编码和解码。但是,您需要使用textarea标签,而不是div。

var str1 = 'One & two & three';
var str2 = "One &amp; two &amp; three";
  

$(document).ready(function() {
$("#encoded").text(htmlEncode(str1));
$("#decoded").text(htmlDecode(str2));
});


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


function htmlEncode(value) {
return $('<textarea/>').text(value).html();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>


<div id="encoded"></div>
<div id="decoded"></div>

一个javascript解决方案,捕捉常见的:

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

这是https://stackoverflow.com/a/4835406/2738039的相反

从JavaScript解释HTML(文本或其他)的一个更现代的选项是DOMParser API (请参阅MDN)中的HTML支持。这允许您使用浏览器的原生HTML解析器将字符串转换为HTML文档。自2014年底以来,所有主流浏览器的新版本都支持它。

如果我们只想解码一些文本内容,我们可以把它作为文档主体中的唯一内容,解析文档,并取出它的.body.textContent

var encodedStr = 'hello &amp; world';


var parser = new DOMParser;
var dom = parser.parseFromString(
'<!doctype html><body>' + encodedStr,
'text/html');
var decodedString = dom.body.textContent;


console.log(decodedString);

我们可以在DOMParser的规范草案中看到,JavaScript没有为已解析的文档启用,因此我们可以在没有安全问题的情况下执行此文本转换。

parseFromString(str, type)方法必须运行以下步骤,具体取决于类型:

  • < p > "text/html"

    HTML parser解析str,并返回新创建的Document

    脚本标记必须设置为“disabled”。

    请注意

    script元素被标记为不可执行,而noscript的内容被解析为标记。

    李< /引用> < / >

这超出了这个问题的范围,但是请注意,如果你把解析的DOM节点本身(不仅仅是它们的文本内容)移动到活动文档DOM,它们的脚本可能会被重新启用,并且可能存在安全问题。我还没有研究过,所以请谨慎行事。

我在我的项目中使用这个:受到其他答案的启发,但有一个额外的安全参数,在处理装饰字符时很有用

var decodeEntities=(function(){


var el=document.createElement('div');
return function(str, safeEscape){


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


str=str.replace(/\</g, '&lt;');


el.innerHTML=str;
if(el.innerText){


str=el.innerText;
el.innerText='';
}
else if(el.textContent){


str=el.textContent;
el.textContent='';
}


if(safeEscape)
str=str.replace(/\</g, '&lt;');
}
return str;
}
})();

它的可用性如下:

var label='safe <b> character &eacute;ntity</b>';
var safehtml='<div title="'+decodeEntities(label)+'">'+decodeEntities(label, true)+'</div>';

对于只说一句话的男人:

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;


console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));

其他答案都有问题。

document.createElement('div')方法(包括使用jQuery的方法)执行传递给它的任何javascript(一个安全问题),DOMParser.parseFromString()方法修饰空白。这是一个纯javascript解决方案,没有任何问题:

function htmlDecode(html) {
var textarea = document.createElement("textarea");
html= html.replace(/\r/g, String.fromCharCode(0xe000)); // Replace "\r" with reserved unicode character.
textarea.innerHTML = html;
var result = textarea.value;
return result.replace(new RegExp(String.fromCharCode(0xe000), 'g'), '\r');
}

TextArea是专门用来避免执行js代码。它通过了这些:

htmlDecode('&lt;&amp;&nbsp;&gt;'); // returns "<& >" with non-breaking space.
htmlDecode('  '); // returns "  "
htmlDecode('<img src="dummy" onerror="alert(\'xss\')">'); // Does not execute alert()
htmlDecode('\r\n') // returns "\r\n", doesn't lose the \r like other solutions.

诀窍是使用浏览器的功能来解码特殊的HTML字符,但不允许浏览器执行结果,就像它是实际的HTML一样…这个函数使用一个正则表达式来识别和替换编码的HTML字符,一次一个字符。

function unescapeHtml(html) {
var el = document.createElement('div');
return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
el.innerHTML = enc;
return el.innerText
});
}

如果你正在寻找它,像我一样-同时有一个很好的和安全的JQuery方法。

https://api.jquery.com/jquery.parsehtml/

你可以用f.ex。在控制台中输入以下内容:

var x = "test &amp;";
> undefined
$.parseHTML(x)[0].textContent
> "test &"

因此$. parsehtml (x)返回一个数组,如果文本中有HTML标记,则返回数组。Length大于1。

你可以使用Lodash的unescape / escape函数https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';


const str = unescape('fred, barney, &amp; pebbles');

str将变成'fred, barney, & pebbles'

我尝试了一切方法来移除&JSON数组。上面的例子都不是,但是https://stackoverflow.com/users/2030321/chris给出了一个很好的解决方案,让我解决了我的问题。

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText

我没有使用,因为我不知道如何将它插入一个模态窗口,将JSON数据拉到一个数组中,但我确实尝试了基于示例的这一点,并且它工作:

var modal = document.getElementById('demodal');
$('#ampersandcontent').text(replaceAll(data[0],"&amp;", "&"));
我喜欢它,因为它简单,而且有效,但不确定为什么它没有被广泛使用。搜索hi &找到一个简单的解决方案。 我继续寻求对语法的理解,以及使用它是否有任何风险。

有一种变体的效率是最高层的答案的80%。

参见基准测试:https://jsperf.com/decode-html12345678/1

性能测试

console.log(decodeEntities('test: &gt'));


function decodeEntities(str) {
// this prevents any overhead from creating the object each time
const el = decodeEntities.element || document.createElement('textarea')


// strip script/html tags
el.innerHTML = str
.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '')
.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');


return el.value;
}

如果你需要留下标记,然后删除两个.replace(...)调用(如果你不需要脚本,你可以留下第一个)。

不客气只是一个信使……全部归功于ourcodeworld.com,链接如下。

window.htmlentities = {
/**
* Converts a string to its html characters completely.
*
* @param {String} str String with unescaped HTML characters
**/
encode : function(str) {
var buf = [];


for (var i=str.length-1;i>=0;i--) {
buf.unshift(['&#', str[i].charCodeAt(), ';'].join(''));
}


return buf.join('');
},
/**
* Converts an html characterSet into its original character.
*
* @param {String} str htmlSet entities
**/
decode : function(str) {
return str.replace(/&#(\d+);/g, function(match, dec) {
return String.fromCharCode(dec);
});
}
};

全部资料:https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-using-pure-javascript

var encodedStr = 'hello &amp; world';


var parser = new DOMParser;
var dom = parser.parseFromString(
'<!doctype html><body>' + encodedStr,
'text/html');
var decodedString = dom.body.textContent;


console.log(decodedString);

我疯狂地做了这个函数,即使不是完全的,也应该是非常详尽的:

function removeEncoding(string) {
return string.replace(/&Agrave;/g, "À").replace(/&Aacute;/g, "Á").replace(/&Acirc;/g, "Â").replace(/&Atilde;/g, "Ã").replace(/&Auml;/g, "Ä").replace(/&Aring;/g, "Å").replace(/&agrave;/g, "à").replace(/&acirc;/g, "â").replace(/&atilde;/g, "ã").replace(/&auml;/g, "ä").replace(/&aring;/g, "å").replace(/&AElig;/g, "Æ").replace(/&aelig;/g, "æ").replace(/&szlig;/g, "ß").replace(/&Ccedil;/g, "Ç").replace(/&ccedil;/g, "ç").replace(/&Egrave;/g, "È").replace(/&Eacute;/g, "É").replace(/&Ecirc;/g, "Ê").replace(/&Euml;/g, "Ë").replace(/&egrave;/g, "è").replace(/&eacute;/g, "é").replace(/&ecirc;/g, "ê").replace(/&euml;/g, "ë").replace(/&#131;/g, "ƒ").replace(/&Igrave;/g, "Ì").replace(/&Iacute;/g, "Í").replace(/&Icirc;/g, "Î").replace(/&Iuml;/g, "Ï").replace(/&igrave;/g, "ì").replace(/&iacute;/g, "í").replace(/&icirc;/g, "î").replace(/&iuml;/g, "ï").replace(/&Ntilde;/g, "Ñ").replace(/&ntilde;/g, "ñ").replace(/&Ograve;/g, "Ò").replace(/&Oacute;/g, "Ó").replace(/&Ocirc;/g, "Ô").replace(/&Otilde;/g, "Õ").replace(/&Ouml;/g, "Ö").replace(/&ograve;/g, "ò").replace(/&oacute;/g, "ó").replace(/&ocirc;/g, "ô").replace(/&otilde;/g, "õ").replace(/&ouml;/g, "ö").replace(/&Oslash;/g, "Ø").replace(/&oslash;/g, "ø").replace(/&#140;/g, "Œ").replace(/&#156;/g, "œ").replace(/&#138;/g, "Š").replace(/&#154;/g, "š").replace(/&Ugrave;/g, "Ù").replace(/&Uacute;/g, "Ú").replace(/&Ucirc;/g, "Û").replace(/&Uuml;/g, "Ü").replace(/&ugrave;/g, "ù").replace(/&uacute;/g, "ú").replace(/&ucirc;/g, "û").replace(/&uuml;/g, "ü").replace(/&#181;/g, "µ").replace(/&#215;/g, "×").replace(/&Yacute;/g, "Ý").replace(/&#159;/g, "Ÿ").replace(/&yacute;/g, "ý").replace(/&yuml;/g, "ÿ").replace(/&#176;/g, "°").replace(/&#134;/g, "†").replace(/&#135;/g, "‡").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&#177;/g, "±").replace(/&#171;/g, "«").replace(/&#187;/g, "»").replace(/&#191;/g, "¿").replace(/&#161;/g, "¡").replace(/&#183;/g, "·").replace(/&#149;/g, "•").replace(/&#153;/g, "™").replace(/&copy;/g, "©").replace(/&reg;/g, "®").replace(/&#167;/g, "§").replace(/&#182;/g, "¶").replace(/&Alpha;/g, "Α").replace(/&Beta;/g, "Β").replace(/&Gamma;/g, "Γ").replace(/&Delta;/g, "Δ").replace(/&Epsilon;/g, "Ε").replace(/&Zeta;/g, "Ζ").replace(/&Eta;/g, "Η").replace(/&Theta;/g, "Θ").replace(/&Iota;/g, "Ι").replace(/&Kappa;/g, "Κ").replace(/&Lambda;/g, "Λ").replace(/&Mu;/g, "Μ").replace(/&Nu;/g, "Ν").replace(/&Xi;/g, "Ξ").replace(/&Omicron;/g, "Ο").replace(/&Pi;/g, "Π").replace(/&Rho;/g, "Ρ").replace(/&Sigma;/g, "Σ").replace(/&Tau;/g, "Τ").replace(/&Upsilon;/g, "Υ").replace(/&Phi;/g, "Φ").replace(/&Chi;/g, "Χ").replace(/&Psi;/g, "Ψ").replace(/&Omega;/g, "Ω").replace(/&alpha;/g, "α").replace(/&beta;/g, "β").replace(/&gamma;/g, "γ").replace(/&delta;/g, "δ").replace(/&epsilon;/g, "ε").replace(/&zeta;/g, "ζ").replace(/&eta;/g, "η").replace(/&theta;/g, "θ").replace(/&iota;/g, "ι").replace(/&kappa;/g, "κ").replace(/&lambda;/g, "λ").replace(/&mu;/g, "μ").replace(/&nu;/g, "ν").replace(/&xi;/g, "ξ").replace(/&omicron;/g, "ο").replace(/&piρ;/g, "ρ").replace(/&rho;/g, "ς").replace(/&sigmaf;/g, "ς").replace(/&sigma;/g, "σ").replace(/&tau;/g, "τ").replace(/&phi;/g, "φ").replace(/&chi;/g, "χ").replace(/&psi;/g, "ψ").replace(/&omega;/g, "ω").replace(/&bull;/g, "•").replace(/&hellip;/g, "…").replace(/&prime;/g, "′").replace(/&Prime;/g, "″").replace(/&oline;/g, "‾").replace(/&frasl;/g, "⁄").replace(/&weierp;/g, "℘").
replace(/&image;/g, "ℑ").replace(/&real;/g, "ℜ").replace(/&trade;/g, "™").replace(/&alefsym;/g, "ℵ").replace(/&larr;/g, "←").replace(/&uarr;/g, "↑").replace(/&rarr;/g, "→").replace(/&darr;/g, "↓").replace(/&barr;/g, "↔").replace(/&crarr;/g, "↵").replace(/&lArr;/g, "⇐").replace(/&uArr;/g, "⇑").replace(/&rArr;/g, "⇒").replace(/&dArr;/g, "⇓").replace(/&hArr;/g, "⇔").replace(/&forall;/g, "∀").replace(/&part;/g, "∂").replace(/&exist;/g, "∃").replace(/&empty;/g, "∅").replace(/&nabla;/g, "∇").replace(/&isin;/g, "∈").replace(/&notin;/g, "∉").replace(/&ni;/g, "∋").replace(/&prod;/g, "∏").replace(/&sum;/g, "∑").replace(/&minus;/g, "−").replace(/&lowast;/g, "∗").replace(/&radic;/g, "√").replace(/&prop;/g, "∝").replace(/&infin;/g, "∞").replace(/&OEig;/g, "Œ").replace(/&oelig;/g, "œ").replace(/&Yuml;/g, "Ÿ").replace(/&spades;/g, "♠").replace(/&clubs;/g, "♣").replace(/&hearts;/g, "♥").replace(/&diams;/g, "♦").replace(/&thetasym;/g, "ϑ").replace(/&upsih;/g, "ϒ").replace(/&piv;/g, "ϖ").replace(/&Scaron;/g, "Š").replace(/&scaron;/g, "š").replace(/&ang;/g, "∠").replace(/&and;/g, "∧").replace(/&or;/g, "∨").replace(/&cap;/g, "∩").replace(/&cup;/g, "∪").replace(/&int;/g, "∫").replace(/&there4;/g, "∴").replace(/&sim;/g, "∼").replace(/&cong;/g, "≅").replace(/&asymp;/g, "≈").replace(/&ne;/g, "≠").replace(/&equiv;/g, "≡").replace(/&le;/g, "≤").replace(/&ge;/g, "≥").replace(/&sub;/g, "⊂").replace(/&sup;/g, "⊃").replace(/&nsub;/g, "⊄").replace(/&sube;/g, "⊆").replace(/&supe;/g, "⊇").replace(/&oplus;/g, "⊕").replace(/&otimes;/g, "⊗").replace(/&perp;/g, "⊥").replace(/&sdot;/g, "⋅").replace(/&lcell;/g, "⌈").replace(/&rcell;/g, "⌉").replace(/&lfloor;/g, "⌊").replace(/&rfloor;/g, "⌋").replace(/&lang;/g, "⟨").replace(/&rang;/g, "⟩").replace(/&loz;/g, "◊").replace(/&#039;/g, "'").replace(/&amp;/g, "&").replace(/&quot;/g, "\"");
}

这样用:

let decodedText = removeEncoding("Ich hei&szlig;e David");
console.log(decodedText);

打印:Ich Heiße David

另外,这花了一个半小时才完成。

这是我迄今为止尝试过的最全面的解决方案:

const STANDARD_HTML_ENTITIES = {
nbsp: String.fromCharCode(160),
amp: "&",
quot: '"',
lt: "<",
gt: ">"
};


const replaceHtmlEntities = plainTextString => {
return plainTextString
.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
.replace(
/&(nbsp|amp|quot|lt|gt);/g,
(a, b) => STANDARD_HTML_ENTITIES[b]
);
};

这个问题没有指定x的起源,但如果可以的话,防御恶意(或只是意想不到的,来自我们自己的应用程序)输入是有意义的。例如,假设x的值为&amp; <script>alert('hello');</script>。在jQuery中处理这个问题的一个安全而简单的方法是:

var x    = "&amp; <script>alert('hello');</script>";
var safe = $('<div />').html(x).text();


// => "& alert('hello');"

通过https://gist.github.com/jmblog/3222899找到。我看不出有太多理由避免使用这个解决方案,因为它至少和一些替代方案一样短,如果不是更短的话而且提供了对XSS的防御。

(我最初是作为评论发布这篇文章的,但由于同一线程中的后续评论要求我这样做,所以我将其作为回答添加进来)。

function decodeHTMLContent(htmlText) {
var txt = document.createElement("span");
txt.innerHTML = htmlText;
return txt.innerText;
}


var result = decodeHTMLContent('One &amp; two &amp; three');
console.log(result);

要在JavaScript中解转义HTML实体*,可以使用小型库html-escaper: npm install html-escaper

import {unescape} from 'html-escaper';


unescape('escaped string');

unescape函数从Lodash下划线,如果你正在使用它。


*)请注意,这些函数并不涵盖所有HTML实体,而只是最常见的,即&<>'"。要解除所有HTML实体的转义,可以使用库。

闭包可以避免创建不必要的对象。

const decodingHandler = (() => {
const element = document.createElement('div');
return text => {
element.innerHTML = text;
return element.textContent;
};
})();

一种更简洁的方式

const decodingHandler = (() => {
const element = document.createElement('div');
return text => ((element.innerHTML = text), element.textContent);
})();

我知道这里有很多好的答案,但由于我实现了一个有点不同的方法,我想分享一下。

这段代码是一种非常安全的安全方法,因为转义处理程序依赖于浏览器,而不是函数。因此,如果将来发现新的漏洞,将覆盖此解决方案。

const decodeHTMLEntities = text => {
// Create a new element or use one from cache, to save some element creation overhead
const el = decodeHTMLEntities.__cache_data_element
= decodeHTMLEntities.__cache_data_element
|| document.createElement('div');
    

const enc = text
// Prevent any mixup of existing pattern in text
.replace(/⪪/g, '⪪#')
// Encode entities in special format. This will prevent native element encoder to replace any amp characters
.replace(/&([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+);/gi, '⪪$1⪫');


// Encode any HTML tags in the text to prevent script injection
el.textContent = enc;


// Decode entities from special format, back to their original HTML entities format
el.innerHTML = el.innerHTML
.replace(/⪪([a-z1-8]{2,31}|#x[0-9a-f]+|#\d+)⪫/gi, '&$1;')
.replace(/#⪫/g, '⪫');
   

// Get the decoded HTML entities
const dec = el.textContent;
    

// Clear the element content, in order to preserve a bit of memory (it is just the text may be pretty big)
el.textContent = '';


return dec;
}


// Example
console.log(decodeHTMLEntities("<script>alert('&awconint;&CounterClockwiseContourIntegral;&#x02233;&#8755;⪪#x02233⪫');</script>"));
// Prints: <script>alert('∳∳∳∳⪪##x02233⪫');</script>

顺便说一下,我选择使用字符,因为它们很少被使用,因此通过匹配它们来影响性能的几率显著降低。

// decode-html.js v1
function decodeHtml(html) {
const textarea = document.createElement('textarea');
textarea.innerHTML = html;
const decodedHtml = textarea.textContent;
textarea.remove();
return decodedHtml;
};


// encode-html.js v1
function encodeHtml(html) {
const textarea = document.createElement('textarea');
textarea.textContent = html;
const encodedHtml = textarea.innerHTML;
textarea.remove();
return encodedHtml;
};


// example of use:
let htmlDecoded = 'one & two & three';
let htmlEncoded = 'one &amp; two &amp; three';


console.log(1, htmlDecoded);
console.log(2, encodeHtml(htmlDecoded));


console.log(3, htmlEncoded);
console.log(4, decodeHtml(htmlEncoded));