web-dev-qa-db-fra.com

Codage HTML perdu lorsque l'attribut est entré dans le champ de saisie

J'utilise JavaScript pour extraire une valeur d'un champ caché et l'afficher dans une zone de texte. La valeur dans le champ masqué est codée.

Par exemple,

<input id='hiddenId' type='hidden' value='chalk &amp; cheese' />

est tiré dans

<input type='text' value='chalk &amp; cheese' />

via jQuery pour obtenir la valeur du champ caché (c’est à ce stade que je perds le codage):

$('#hiddenId').attr('value')

Le problème est que, lorsque je lis chalk &amp; cheese dans le champ masqué, JavaScript semble perdre le codage. Pour échapper à " et ', je veux que l'encodage reste.

Existe-t-il une bibliothèque JavaScript ou une méthode jQuery qui encodera une chaîne au format HTML?

708
AJM

J'utilise ces fonctions:

function htmlEncode(value){
  // Create a in-memory div, set its inner text (which jQuery automatically encodes)
  // Then grab the encoded contents back out. The div never exists on the page.
  return $('<div/>').text(value).html();
}

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

Fondamentalement, un élément div est créé en mémoire, mais il n'est jamais ajouté au document.

Sur la fonction htmlEncode, je définis la innerText de l'élément et récupère la valeur encodée innerHTML; sur la fonction htmlDecode, je définis la valeur innerHTML de l'élément et la variable innerText est extraite.

Vérifiez un exemple en cours d'exécution ici .

1050
CMS

L'astuce jQuery ne code pas les guillemets et, dans IE, elle supprime votre espace.

Sur la base du escape templatetag de Django, que je suppose déjà très utilisé/testé, j'ai créé cette fonction qui fait le nécessaire.

C’est sans doute plus simple (et peut-être plus rapide) que l’une quelconque des solutions de contournement pour le problème de suppression d’espaces blancs - et elle code les guillemets, ce qui est essentiel si vous voulez utiliser le résultat dans une valeur d’attribut, par exemple.

function htmlEscape(str) {
    return str
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

// I needed the opposite function today, so adding here too:
function htmlUnescape(str){
    return str
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, "'")
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&');
}

Mise à jour 2013-06-17:
Dans la recherche de l'échappement le plus rapide, j'ai trouvé cette implémentation d'une méthode replaceAll:
http://dumpsite.com/forum/index.php?topic=4.msg29#msg29
(également référencé ici: La méthode la plus rapide pour remplacer toutes les occurrences d'un caractère dans une chaîne } _)
Quelques résultats de performance ici:
http://jsperf.com/htmlencoderegex/25

Il donne une chaîne de résultat identique aux chaînes replace intégrées ci-dessus. Je serais très heureux si quelqu'un pouvait expliquer pourquoi c'est plus rapide!?

Mise à jour 2015-03-04:
Je viens de remarquer que AngularJS utilise exactement la méthode ci-dessus:
https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435

Ils ajoutent quelques améliorations - ils semblent gérer un problème obscur Unicode ainsi que convertir tous les caractères non alphanumériques en entités. J'avais l'impression que ce dernier n'était pas nécessaire tant que vous avez spécifié un jeu de caractères UTF8 pour votre document.

Je noterai que (4 ans plus tard) Django ne fait toujours aucune de ces choses, alors je ne suis pas sûr de leur importance:
https://github.com/Django/django/blob/1.8b1/Django/utils/html.py#L44

Mise à jour 2016-04-06:
Vous pouvez également souhaiter échapper à forward-slash /. Ceci n'est pas nécessaire pour un codage HTML correct. Toutefois, il s'agit de recommandé par OWASP en tant que mesure de sécurité anti-XSS. (merci à @JNF pour l'avoir suggéré dans les commentaires)

        .replace(/\//g, '&#x2F;');
541
Anentropic

Voici une version non-jQuery qui est considérablement plus rapide que la version jQuery .html() et la version .replace(). Cela préserve tous les espaces, mais comme la version jQuery, ne traite pas les guillemets.

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

Speed:http://jsperf.com/htmlencoderegex/17

speed test

Démo:  jsFiddle

Sortie:

output

Scénario:

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

function htmlDecode( html ) {
    var a = document.createElement( 'a' ); a.innerHTML = html;
    return a.textContent;
};

document.getElementById( 'text' ).value = htmlEncode( document.getElementById( 'hidden' ).value );

//sanity check
var html = '<div>   &amp; hello</div>';
document.getElementById( 'same' ).textContent = 
      'html === htmlDecode( htmlEncode( html ) ): ' 
    + ( html === htmlDecode( htmlEncode( html ) ) );

HTML:

<input id="hidden" type="hidden" value="chalk    &amp; cheese" />
<input id="text" value="" />
<div id="same"></div>
78
ThinkingStiff

Je sais que c’est un vieux problème, mais je voulais publier une variante de la réponse acceptée qui fonctionnera dans IE sans supprimer les lignes:

function multiLineHtmlEncode(value) {
    var lines = value.split(/\r\n|\r|\n/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('\r\n');
}

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

Underscore fournit _.escape() et _.unescape() les méthodes qui le font.

> _.unescape( "chalk &amp; cheese" );
  "chalk & cheese"

> _.escape( "chalk & cheese" );
  "chalk &amp; cheese"
28
TJ VanToll

Bonne réponse. Notez que si la valeur à coder est undefined ou null avec jQuery 1.4.2, des erreurs telles que:

jQuery("<div/>").text(value).html is not a function

OR

Uncaught TypeError: Object has no method 'html'

La solution consiste à modifier la fonction pour rechercher une valeur réelle:

function htmlEncode(value){ 
    if (value) {
        return jQuery('<div/>').text(value).html(); 
    } else {
        return '';
    }
}
12
leepowers

Pour ceux qui préfèrent le javascript simple, voici la méthode que j'ai utilisée avec succès:

function escapeHTML (str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}
11
backtestbroker.com

Prototype intègre-t-il la classe String . Donc, si vous utilisez/prévoyez d’utiliser Prototype, le résultat sera le suivant:

'<div class="article">This is an article</div>'.escapeHTML();
// -> "&lt;div class="article"&gt;This is an article&lt;/div&gt;"
5
Sinan Taifour

FWIW, l'encodage n'est pas perdu. Le codage est utilisé par l'analyseur de balises (navigateur) lors du chargement de la page. Une fois que la source est lue et analysée et que le DOM a chargé le DOM dans la mémoire, le codage a été analysé dans ce qu’il représente. Donc, au moment où votre JS est exécuté pour lire quelque chose en mémoire, le caractère obtenu correspond à ce que représente le codage.

Ici, je travaille peut-être strictement sur la sémantique, mais je voulais que vous compreniez le but de l’encodage. Le mot "perdu" donne l'impression que quelque chose ne fonctionne pas comme il se doit.

5
JAAulde

Plus rapide sans JQuery. Vous pouvez encoder chaque caractère de votre chaîne:

function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}

Ou ciblez simplement les personnages principaux pour vous inquiéter (&, inebreaks, <,>, "et ') comme:

function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!\n\n"Safe" escape <script id=\'\'> & useful in <pre> tags!');

testing.innerHTML=test.value;

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
<textarea id=test rows="9" cols="55"></textarea>

<div id="testing">www.WHAK.com</div>

5
Dave Brown

Voici une solution javascript simple. Il étend l'objet String avec une méthode "HTMLEncode" qui peut être utilisée sur un objet sans paramètre ou avec un paramètre.

String.prototype.HTMLEncode = function(str) {
  var result = "";
  var str = (arguments.length===1) ? str : this;
  for(var i=0; i<str.length; i++) {
     var chrcode = str.charCodeAt(i);
     result+=(chrcode>128) ? "&#"+chrcode+";" : str.substr(i,1)
   }
   return result;
}
// TEST
console.log("stetaewteaw æø".HTMLEncode());
console.log("stetaewteaw æø".HTMLEncode("æåøåæå"))

J'ai fait un Gist "méthode HTMLEncode pour javascript" .

4
Netsi1964

Basé sur angular's sanitize ... (syntaxe du module es6)

// ref: https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js
const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
const NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;

const decodeElem = document.createElement('pre');


/**
 * Decodes html encoded text, so that the actual string may
 * be used.
 * @param value
 * @returns {string} decoded text
 */
export function decode(value) {
  if (!value) return '';
  decodeElem.innerHTML = value.replace(/</g, '&lt;');
  return decodeElem.textContent;
}


/**
 * Encodes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} encoded text
 */
export function encode(value) {
  if (value === null || value === undefined) return '';
  return String(value).
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, value => {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, value => {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

export default {encode,decode};
3
Tracker1

Ma fonction pure-JS:

/**
 * HTML entities encode
 *
 * @param {string} str Input text
 * @return {string} Filtered text
 */
function htmlencode (str){

  var div = document.createElement('div');
  div.appendChild(document.createTextNode(str));
  return div.innerHTML;
}

JavaScript HTML Entities Encode & Decode

2
Nick Tsai

autant que je sache, il n’existe pas de méthodes simples d’encodage/décodage HTML en javascript.

Cependant, vous pouvez utiliser JS pour créer un élément arbitraire, définir son texte interne, puis le lire à l'aide de innerHTML. 

par exemple, avec jQuery, cela devrait fonctionner:

var helper = $('chalk & cheese').hide().appendTo('body');
var htmled = helper.html();
helper.remove();

ou quelque chose dans ce sens

2
Ken Egozi

Vous ne devriez pas avoir à échapper/encoder des valeurs pour les faire passer d'un champ de saisie à un autre.

<form>
 <input id="button" type="button" value="Click me">
 <input type="hidden" id="hiddenId" name="hiddenId" value="I like cheese">
 <input type="text" id="output" name="output">
</form>
<script>
    $(document).ready(function(e) {
        $('#button').click(function(e) {
            $('#output').val($('#hiddenId').val());
        });
    });
</script>

JS ne va pas insérer du HTML brut ou quoi que ce soit; il indique simplement au DOM de définir la propriété value (ou un attribut; pas sûr). Dans les deux cas, le DOM gère les problèmes d’encodage pour vous. Sauf si vous faites quelque chose de bizarre comme utiliser document.write ou eval, le codage HTML sera efficacement transparent.

Si vous parlez de générer une nouvelle zone de texte pour conserver le résultat ... c'est toujours aussi facile. Passez simplement la partie statique du code HTML à jQuery, puis définissez le reste des propriétés/attributs sur l'objet qu'il vous renvoie.

$box = $('<input type="text" name="whatever">').val($('#hiddenId').val());
2
cHao

J'ai eu un problème similaire et je l'ai résolu en utilisant la fonction encodeURIComponent de JavaScript ( documentation )

Par exemple, dans votre cas si vous utilisez:

<input id='hiddenId' type='hidden' value='chalk & cheese' />

et

encodeURIComponent($('#hiddenId').attr('value'))

vous obtiendrez chalk%20%26%20cheese. Même les espaces sont conservés.

Dans mon cas, j'ai dû encoder une barre oblique inverse et ce code fonctionne parfaitement

encodeURIComponent('name/surname')

et j'ai eu name%2Fsurname

2
Dmyan

J'ai rencontré des problèmes avec la barre oblique inverse dans ma chaîne Domain\User.

J'ai ajouté ceci aux autres échappatoires de la réponse d'Anentropic

.replace(/\\/g, '&#92;')

Ce que j'ai trouvé ici: Comment échapper à la barre oblique inverse en JavaScript?

1
spacebread
<script>
String.prototype.htmlEncode = function () {
    return String(this)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');

}

var aString = '<script>alert("I hack your site")</script>';
console.log(aString.htmlEncode());
</script>

Affichera: &lt;script&gt;alert(&quot;I hack your site&quot;)&lt;/script&gt;

.htmlEncode () sera accessible sur toutes les chaînes une fois définies.

1
Stuart Eske

HtmlEncodes la valeur donnée

  var htmlEncodeContainer = $('<div />');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }
1
Sky Yip

Voici un petit peu qui émule la fonction Server.HTMLEncode de l'ASP de Microsoft, écrite en JavaScript pur:

function htmlEncode(s) {
  var ntable = {
    "&": "amp",
    "<": "lt",
    ">": "gt",
    "\"": "quot"
  };
  s = s.replace(/[&<>"]/g, function(ch) {
    return "&" + ntable[ch] + ";";
  })
  s = s.replace(/[^ -\x7e]/g, function(ch) {
    return "&#" + ch.charCodeAt(0).toString() + ";";
  });
  return s;
}

Le résultat ne pas code les apostrophes, mais code les autres promotions HTML et tout caractère en dehors de la plage 0x20-0x7e.

1
ReWrite

Si vous voulez utiliser jQuery. J'ai trouvé ça:

http://www.jquerysdk.com/api/jQuery.htmlspecialchars

(partie du plugin jquery.string proposé par jQuery SDK)

Le problème avec Prototype, je crois, est qu’il étend les objets de base en JavaScript et sera incompatible avec tout jQuery que vous avez utilisé. Bien sûr, si vous utilisez déjà Prototype et pas jQuery, ce ne sera pas un problème.

EDIT: Il y a aussi ceci, qui est un port des utilitaires de chaîne de Prototype pour jQuery:

http://stilldesigning.com/dotstring/

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

Cela provient du code source ExtJS.

1
WaiKit Kung

En utilisant certaines des autres réponses ici, j’ai créé une version qui remplace tous les caractères pertinents en un seul passage, quel que soit le nombre de caractères codés distincts (un seul appel à replace()).

Il ne repose pas sur l'API DOM pour exister ou sur d'autres bibliothèques.

window.encodeHTML = (function() {
    function escapeRegex(s) {
        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    }
    var encodings = {
        '&'  : '&amp;',
        '"'  : '&quot;',
        '\'' : '&#39;',
        '<'  : '&lt;',
        '>'  : '&gt;',
        '\\' : '&#x2F;'
    };
    function encode(what) { return encodings[what]; };
    var specialChars = new RegExp('[' +
        escapeRegex(Object.keys(encodings).join('')) +
    ']', 'g');

    return function(text) { return text.replace(specialChars, encode); };
})();

Après avoir couru cette fois, vous pouvez maintenant appeler

encodeHTML('<>&"\'')

Pour obtenir &lt;&gt;&amp;&quot;&#39;

0
Hashbrown

Choisir ce que escapeHTML() fait dans le prototype.js

L'ajout de ce script vous aide à escapeHTML:

String.prototype.escapeHTML = function() { 
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
}

vous pouvez maintenant appeler la méthode escapeHTML sur les chaînes de votre script, comme suit:

var escapedString = "<h1>this is HTML</h1>".escapeHTML();
// gives: "&lt;h1&gt;this is HTML&lt;/h1&gt;"

J'espère que cela aidera toute personne à la recherche d'une solution simple sans avoir à inclure l'intégralité du prototype.js

0
new_user
function encodeHTML(str) {
    return document.createElement("a").appendChild( 
        document.createTextNode(str)).parentNode.innerHTML;
};

function decodeHTML(str) {
    var element = document.createElement("a"); 
    element.innerHTML = str;
    return element.textContent;
};
var str = "<"
var enc = encodeHTML(str);
var dec = decodeHTML(enc);
console.log("str: " + str, "\nenc: " + enc, "\ndec: " + dec);
0
Israel