我有一些与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/

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


当前回答

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

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

其他回答

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

下面的代码片段是老答案的代码,只做了一些小修改:使用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中的视图)上所证明的那样。上面的版本适用于所有输入。

更新:这似乎不工作与大字符串,它也引入了一个安全漏洞,见评论。

// 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));

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

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>';

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

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

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

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

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

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

这可以通过使用所有现代浏览器都支持的DOMParser来避免:

html解码(输入)功能 瓦尔多克=新住户。parseFromString(输入,“短信/ html”); 归来医生。documentElement textContent; 的 控制台.log(htmlDecode(“< img src=‘myimage.jpg’>’) <img src='myimage.jpg'> ' 控制台(htmlDecode(“<img src=‘dummy’on误差=‘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版本(通常这些版本仍然不够多)。