【问题标题】:Unescape HTML entities in JavaScript?在 JavaScript 中转义 HTML 实体?
【发布时间】:2024-01-07 08:32:01
【问题描述】:

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

还有哪些其他方法可以诊断问题?

【问题讨论】:

  • 本文中包含的巨大功能似乎可以正常工作:blogs.msdn.com/b/aoakley/archive/2003/11/12/49645.aspx 我认为这不是最聪明的解决方案,但确实有效。
  • 由于包含 HTML 实体的字符串与 escaped 或 URI encoded strings 不同,因此这些函数将不起作用。
  • @Matias 注意到自该函数于 2003 年创建以来,新的命名实体已添加到 HTML(例如通过 HTML 5 规范)——例如,它无法识别 &amp;zopf;。这是一个不断发展的规范的问题;因此,您应该选择一个实际维护的工具来解决它。
  • 我刚刚意识到将这个问题与编码 HTML 实体混淆是多么容易。我刚刚意识到我不小心在这个问题上发布了错误问题的答案!不过我已经删除了。

标签: javascript html escaping xml-rpc


【解决方案1】:

这里给出的大多数答案都有一个巨大的缺点:如果您尝试转换的字符串不受信任,那么您最终会得到Cross-Site Scripting (XSS) vulnerability。对于accepted answer 中的函数,请考虑以下几点:

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

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

这可以通过使用DOMParser 来避免,all modern browsers 支持:

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 版本(通常这些浏览器数量还不足以打扰)。

【讨论】:

  • 我认为这个答案是最好的,因为它提到了 XSS 漏洞。
  • 请注意(根据您的参考)DOMParser 在 Firefox 12.0 之前不支持 "text/html"there are still some latest versions of browsers that do not even support DOMParser.prototype.parseFromString()。根据您的参考,DOMParser 仍然是一项实验性技术,替代品使用innerHTML 属性,正如您在回复my approach 时也指出的那样,该属性存在此 XSS 漏洞(应该由浏览器供应商)。
  • @PointedEars:谁在乎 2016 年的 Firefox 12?有问题的是最高 9.0 的 Internet Explorer 和最高 7.0 的 Safari。如果一个人负担得起不支持它们(希望很快每个人都支持),那么 DOMParser 是最好的选择。如果不是 - 是的,只处理实体将是一种选择。
  • @PointedEars: &lt;script&gt; 标签不被执行不是一种安全机制,如果设置innerHTML 可以运行同步脚本作为副作用,此规则只是避免了棘手的时间问题。清理 HTML 代码是一件棘手的事情,innerHTML 甚至没有尝试过——因为网页实际上可能打算设置内联事件处理程序。这根本不是针对不安全数据的机制,句号。
  • @ИльяЗеленько:您是否打算在紧密循环中使用此代码,或者为什么性能很重要?你的回答又容易受到 XSS 的攻击,真的值得吗?
【解决方案2】:

您需要解码所有编码的 HTML 实体还是只解码 &amp;amp; 本身?

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

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

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

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

请注意以下 Mark 的 cmets,其中突出显示了此答案早期版本中的安全漏洞,并建议使用 textarea 而不是 div 来缓解潜在的 XSS 漏洞。无论您使用 jQuery 还是纯 JavaScript,这些漏洞都存在。

【讨论】:

  • 当心!这可能是不安全的。如果encoded='&lt;img src="bla" onerror="alert(1)"&gt;' 则上面的 sn-p 将显示警报。这意味着如果您的编码文本来自用户输入,则使用此 sn-p 对其进行解码可能会出现 XSS 漏洞。
  • @MarkAmery 我不是安全专家,但看起来如果您在获取文本后立即将 div 设置为 null,则不会触发 img 中的警报 - jsfiddle.net/Mottie/gaBeb/128跨度>
  • @Mottie 请注意确定哪个浏览器适合您,但在 OS X 上的 Chrome 上,alert(1) 仍然会为我触发。如果您想要此 hack 的安全变体,请尝试using a textarea
  • +1 用于简单的正则表达式替换仅一种 html 实体的替代方案。如果您希望将 html 数据从例如 python 烧瓶应用程序插入到模板中,请使用此选项。
  • 如何在节点服务器上做到这一点?
【解决方案3】:

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

以下 sn-p 是旧答案的代码,稍作修改:使用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 Character Entities

编辑:此代码的旧版本不适用于具有空白输入的 IE,如 here on jsFiddle 所示(在 IE 中查看)。以上版本适用于所有输入。

更新:似乎这不适用于大字符串,并且还引入了一个安全漏洞,请参阅 cmets。

【讨论】:

  • @S.Mark: &amp;apos; 不属于 HTML 4 实体,这就是原因! w3.org/TR/html4/sgml/entities.htmlfishbowl.pastiche.org/2003/07/01/the_curse_of_apos
  • 另请参阅@kender 关于此方法安全性差的说明。
  • 查看我给@kender 的关于他所做的糟糕测试的说明;)
  • 此函数存在安全隐患,即使元素未添加到 DOM,JavaScript 代码也会运行。所以这只有在输入字符串是可信的情况下才可以使用。我添加了my own answer,解释了这个问题并提供了一个安全的解决方案。作为副作用,如果存在多个文本节点,结果不会被截断。
  • 如果 JS 没有在浏览器中运行,即使用 Node,这不起作用。
【解决方案4】:

从 JavaScript 解释 HTML(文本和其他)的更现代的选项是 DOMParser API (see here in 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);

我们可以在the draft specification for DOMParser 中看到解析后的文档没有启用 JavaScript,因此我们可以在没有安全问题的情况下执行此文本转换。

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

  • "text/html"

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

    脚本标志必须设置为“禁用”。

    笔记

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

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

【讨论】:

【解决方案5】:

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 内容然后读回其文本内容的 hack。此类方法可行,但如果用于不受信任的用户输入,则具有欺骗性的危险性并会带来 XSS 机会。

如果你真的不忍心加载库,你可以使用this answer 中描述的textarea hack 来解决一个几乎重复的问题,与建议的各种类似方法不同,它没有安全性我知道的漏洞:

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 漏洞。

【讨论】:

  • Matthias Bynens 的图书馆 he 绝对很棒!非常感谢您的推荐!
【解决方案6】:

如果您使用的是 jQuery:

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

否则,使用Strictly Software's Encoder Object,它具有出色的htmlDecode()功能。

【讨论】:

  • 请勿(重复 NOT)将其用于用户生成的内容,而不是由 this 用户生成的内容。如果 value 中有
  • 我在网站上的任何地方都找不到许可证。你知道许可证是什么吗?
  • 源头中有一个许可证,它是 GPL。
  • 是的,该函数为 XSS 开辟了道路:尝试 htmlDecode(" 123 >")
  • $('
    ')
    是什么意思?
【解决方案7】:
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 的源代码。

【讨论】:

  • -1;这无法处理绝大多数命名实体。例如,htmlEnDecode.htmlDecode('&amp;euro;') 应该返回 '€',而是返回 '&amp;euro;'
【解决方案8】:

可以使用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, &amp; pebbles'

【讨论】:

  • 可能更好地执行“从'lodash/unescape'导入_unescape;”所以它不会与已弃用的同名javascript函数冲突:unescape
【解决方案9】:

诀窍是利用浏览器的强大功能来解码特殊的 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
    });
}

【讨论】:

  • 正则表达式可以更紧密地与/\&amp;#?[0-9a-z]+;/gi 匹配,因为# 应该只作为第二个字符出现。
  • 这是最好的答案。避免 XSS 漏洞,并且不去除 HTML 标签。
【解决方案10】:

element.innerText 也可以解决问题。

【讨论】:

    【解决方案11】:

    如果您正在寻找它,就像我一样 - 同时有一个不错且安全的 JQuery 方法。

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

    你可以 f.ex.在控制台中输入:

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

    所以 $.parseHTML(x) 返回一个数组,如果你的文本中有 HTML 标记,array.length 将大于 1。

    【讨论】:

    • 非常适合我,这正是我想要的,谢谢。
    • 如果x 的值为&lt;script&gt;alert('hello');&lt;/script&gt;,则上述内容将崩溃。在当前的 jQuery 中,它实际上不会尝试运行脚本,但[0] 将产生undefined,因此对textContent 的调用将失败并且您的脚本将停在那里。 $('&lt;div /&gt;').html(x).text(); 看起来更安全 - 通过 gist.github.com/jmblog/3222899
    • @AndrewHodgkinson 是的,但问题是“在 JavaScript 中解码 & 返回 &” - 所以你首先要测试 x 的内容,或者确保你只在正确的情况下使用它。
    • 我真的不明白这是怎么回事。上面的代码适用于所有情况。以及您将如何“确保” x 的值需要修复?如果上面的脚本示例警告“&”怎么办?所以它真的需要纠正吗?我们不知道 OP 的字符串来自哪里,因此必须考虑恶意输入。
    • @AndrewHodgkinson 我喜欢你的考虑,但这不是这里的问题。不过,请随意回答这个问题。我猜你可以删除脚本标签,f.ex.
    【解决方案12】:

    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>

    【讨论】:

    • -1 因为旧 jQuery 版本存在(令人惊讶的)安全漏洞,其中一些可能仍然拥有重要的用户群——这些版本将在 HTML 中将 detect and explicitly evaluate scripts 传递给 .html()。因此,即使使用textarea 也不足以确保此处的安全性;我建议not using jQuery for this task and writing equivalent code with the plain DOM API。 (是的,jQuery 的旧行为既疯狂又可怕。)
    • 感谢您指出这一点。但是,该问题不包括检查脚本注入的要求。该问题专门询问Web服务器呈现的html。保存到 Web 服务器的 Html 内容可能应该在保存之前进行脚本注入验证。
    【解决方案13】:

    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 最大长度的答案:https://*.com/a/27545633/694469

    【讨论】:

      【解决方案14】:

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

      import {unescape} from 'html-escaper';
      
      unescape('escaped string');
      

      或者unescape函数来自LodashUnderscore,如果你正在使用它。


      *) 请注意,这些函数并未涵盖所有 HTML 实体,而仅涵盖最常见的实体,即&amp;&lt;&gt;'"。要取消转义所有 HTML 实体,您可以使用 he 库。

      【讨论】:

        【解决方案15】:

        首先在正文某处创建&lt;span id="decodeIt" style="display:none;"&gt;&lt;/span&gt;

        接下来,将要解码为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
        

        【讨论】:

        • -1;在不受信任的输入上使用这是非常不安全的。例如,考虑一下如果stringtodecode 包含类似&lt;script&gt;alert(1)&lt;/script&gt; 的内容会发生什么。
        【解决方案16】:

        不是对您的问题的直接回应,但是如果您的 RPC 返回一些结构(无论是 XML 还是 JSON 或其他)以及该结构中的那些图像数据(在您的示例中为 url),这不是更好吗?

        然后你可以在你的javascript中解析它并使用javascript本身构建&lt;img&gt;

        您从 RPC 收到的结构可能如下所示:

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

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

        【讨论】:

        • 上面的@CMS方法有这个安全漏洞吗?
        • 我刚刚检查了传递给 htmlDecode 函数的以下参数: htmlDecode("<img src='myimage.jpg'><script>document.write('xxxxx');</ script>") 并且它创建了可能不好的 元素,恕我直言。而且我仍然认为返回结构而不是要插入的文本更好,例如,您可以很好地处理错误。
        • 我刚试过htmlDecode("&amp;lt;img src='myimage.jpg'&amp;gt;&amp;lt;script&amp;gt;alert('xxxxx');&amp;lt;/script&amp;gt;"),什么也没发生。我按预期得到了解码后的 html 字符串。
        【解决方案17】:

        对于单线的人:

        const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;
        
        console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));
        

        【讨论】:

          【解决方案18】:

          不客气...只是一个信使...全部归功于 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

          【讨论】:

          • 这是一个不完整的解决方案;它只处理十进制数字字符引用,而不是命名字符引用或十六进制数字字符引用。
          【解决方案19】:

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

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

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

          (我最初将此作为评论发布,但由于同一线程中的后续评论要求我这样做,因此我将其添加为答案)。

          【讨论】:

            【解决方案20】:

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

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

            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>
            

            顺便说一句,我选择使用字符,因为它们很少使用,所以通过匹配它们影响性能的机会明显更低。

            【讨论】:

              【解决方案21】:

              Chris 的回答很好很优雅,但如果值为 undefined,它会失败。只需简单的改进就可以使其稳固:

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

              【讨论】:

              • 如果要改进,那就做:return (typeof value !== 'string') ? '' : $('&lt;div/&gt;').html(value).text();
              【解决方案22】:

              捕捉常见问题的 javascript 解决方案:

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

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

              【讨论】:

              • 如果您使用map[c] || '',无法识别的将不会显示为undefined
              • 覆盖范围非常有限; -1.
              • +1,更多是unescapeHtml(str){ var map = {amp: '&amp;', lt: '&lt;', le: '≤', gt: '&gt;', ge: '≥', quot: '"', '#039': "'"} return str.replace(/&amp;([^;]+);/g, (m, c) =&gt; map[c]|| '') }
              • 手动覆盖。不推荐。
              【解决方案23】:

              我尝试了一切从 JSON 数组中删除 & 的方法。以上例子都没有,但https://*.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 & low 以找到一个简单的解决方案。 我继续寻求对语法的理解,以及使用它是否有任何风险。还没有找到任何东西。

              【讨论】:

              • 您的第一个建议有点棘手,但不费吹灰之力就可以很好地工作。另一方面,第二个仅使用蛮力来解码字符。这意味着完成完整的解码功能可能需要大量的精力和时间。这就是为什么没有人使用这种方式来解决 OP 的问题。
              【解决方案24】:

              我很疯狂地完成了这个应该很漂亮,如果不是完全,详尽的功能:

              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

              附:这需要一个半小时才能完成。

              【讨论】:

              • 不适用于"Ich Hei&amp;#xDF;e David"
              【解决方案25】:

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

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

              【讨论】:

              【解决方案26】:

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

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

              【讨论】:

                【解决方案27】:

                我在我的项目中使用它:灵感来自other answers,但带有一个额外的安全参数,在处理装饰字符时很有用

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

                【讨论】:

                  【解决方案28】:
                  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);
                  

                  【讨论】:

                  • @Wladimir Palant(AdBlock Plus 的作者)之前已经给出了 DOMParser 的答案 4 years。您在发布之前阅读过之前的答案吗?
                  【解决方案29】:

                  这里的所有其他答案都有问题。

                  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.
                  

                  【讨论】:

                  • 不,使用不同的标签不能解决问题。这仍然是一个 XSS 漏洞,试试htmlDecode("&lt;/textarea&gt;&lt;img src=x onerror=alert(1)&gt;")。在我已经在 Sergio Belevskij 的回答中指出了这个问题之后,你发布了这个。
                  • 我无法重现您描述的问题。我在这个 JsFiddle 中有你的代码,运行时没有警报显示。 jsfiddle.net/edsjt15g/1可以看看吗?你用的是什么浏览器?
                  • 我正在使用 Firefox。 Chrome 确实以不同的方式处理这种情况,因此代码不会执行 - 但是你不应该依赖它。
                  【解决方案30】:

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

                  【讨论】:

                  • 这个答案比 前的 textarea 好多少?
                  • 提出一个安全问题。没有什么能阻止您在其中添加 &lt;img&gt; 并运行任意 JS。 不要在生产中使用它或任何类似的东西(或用于业余项目,如果其他人会使用它)。
                  最近更新 更多