web-dev-qa-db-fra.com

Unescape HTML entités en Javascript?

J'ai du code Javascript qui communique avec un serveur XML-RPC . Le XML-RPC renvoie des chaînes de la forme suivante:

<img src='myimage.jpg'>

Cependant, lorsque j'utilise le Javascript pour insérer les chaînes en HTML, elles sont rendues littéralement. Je ne vois pas d'image, je vois littéralement la chaîne:

<img src='myimage.jpg'>

Je suppose que le code HTML est échappé via le canal XML-RPC.

Comment puis-je décompresser la chaîne en Javascript? J'ai essayé les techniques sur cette page sans succès: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

Quels sont les autres moyens de diagnostiquer le problème?

118
Joseph Turian

J'utilise la méthode suivante:

function htmlDecode(input){
  var e = document.createElement('div');
  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'>"

Fondamentalement, je crée un élément DOM par programme, assigne le code HTML codé à son code innerHTML et récupère la valeur nodeValue à partir du noeud de texte créé lors de l'insertion de innerHTML. Comme il crée simplement un élément mais ne l'ajoute jamais, aucun site HTML n'est modifié.

Il fonctionnera sur plusieurs navigateurs (y compris les anciens navigateurs) et acceptera tous les Entités de caractères HTML .

EDIT: L'ancienne version de ce code ne fonctionnait pas sur IE avec des entrées vierges, comme en témoigne ici sur jsFiddle (vue dans IE). La version ci-dessus fonctionne avec toutes les entrées.

UPDATE: semble que cela ne fonctionne pas avec une chaîne longue, et introduit également une vulnérabilité security, voir les commentaires.

155
CMS

La plupart des réponses données ici présentent un inconvénient majeur: si la chaîne que vous essayez de convertir n’est pas fiable, vous vous retrouverez avec une vulnérabilité Cross-Site Scripting (XSS) . Pour la fonction dans réponse acceptée , tenez compte des éléments suivants:

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

La chaîne ici contient une balise HTML non échappée. Ainsi, au lieu de décoder quoi que ce soit, la fonction htmlDecode exécutera en réalité le code JavaScript spécifié dans la chaîne.

Cela peut être évité en utilisant DOMParser qui est pris en charge par tous les navigateurs modernes :

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

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

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

Il est garanti que cette fonction n'exécutera aucun code JavaScript en tant qu'effet secondaire. Toute balise HTML sera ignorée, seul le contenu textuel sera renvoyé.

Note de compatibilité: L'analyse de code HTML avec DOMParser nécessite au moins Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 ou Microsoft Edge. Ainsi, tous les navigateurs sans assistance ont dépassé leur fin de vie et, à compter de 2017, les versions les plus anciennes de Internet et de Safari (parfois les versions les plus anciennes) sont parfois anciennes.

235
Wladimir Palant

Si vous utilisez jQuery:

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

Sinon, utilisez Encoder Object de Strictly Software, qui possède une excellente fonction htmlDecode().

39
Chris Fulstow

L'astuce consiste à utiliser la puissance du navigateur pour décoder les caractères HTML spéciaux, sans toutefois permettre au navigateur d'exécuter les résultats comme s'il s'agissait de fichiers HTML réels ... Cette fonction utilise une expression régulière pour identifier et remplacer les caractères HTML codés, un caractère. à la fois.

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

La réponse du CMS fonctionne bien, sauf si le code HTML que vous souhaitez décompresser est très long, plus long que 65 536 caractères. Puis, dans Chrome, le code HTML interne est divisé en plusieurs nœuds enfants, d’une longueur maximale de 65 536 chacun, et vous devez les concaténer. Cette fonction fonctionne aussi pour les très longues chaînes:

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

Voir cette réponse à propos de innerHTML longueur maximale pour plus d’informations: https://stackoverflow.com/a/27545633/694469

5
KajMagnus

La réponse de Chris est agréable et élégante, mais elle échoue si la valeur est non définie Juste une simple amélioration le rend solide:

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

Ce n’est pas une réponse directe à votre question, mais ne serait-il pas préférable que votre RPC renvoie une structure (XML, JSON ou autre) contenant ces données d’image (les URL dans votre exemple) à l’intérieur de cette structure? 

Ensuite, vous pouvez simplement l'analyser dans votre javascript et construire le <img> à l'aide de javascript.

La structure que vous recevez de RPC pourrait ressembler à:

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

Je pense que c'est mieux ainsi, car l'injection d'un code provenant d'une source externe dans votre page n'a pas l'air très sécurisé. Imaginez une personne détournant votre script XML-RPC et y insérant quelque chose que vous ne voudriez pas (même un peu de javascript ...) 

3
kender

Vous êtes le bienvenu ... juste un messager ... le crédit complet va à ourcodeworld.com, lien ci-dessous.

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

Crédit complet: https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-using-pure-javascript

2
indospace.io

C'est un meilleur:

String::decode = ->
   $('<textarea />').html(this).text()

utilisation:

"&lt;img src='myimage.jpg'&gt;".decode();

à partir de: HTML Entity Decode

1
Sergio Belevskij

Toutes les autres réponses ici ont des problèmes.

Les méthodes document.createElement ('div') (y compris celles utilisant jQuery) exécutent tout code javascript qui y est passé (problème de sécurité) et la méthode DOMParser.parseFromString () élimine les espaces. Voici une solution purement javascript qui n'a pas de problème:

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 est utilisé spécifiquement pour éviter le code executig js. Il passe ces:

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.
0
Dwayne

J'utilise ceci dans mon projet: inspiré par d'autres réponses mais avec un paramètre supplémentaire sécurisé, peut être utile lorsque vous utilisez des personnages décorés

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

Et c'est utilisable comme:

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