HtmlSpecialChars equivalente in Javascript?


167

Apparentemente, questo è più difficile da trovare di quanto pensassi. Ed è anche così semplice ...

Esiste una funzione equivalente a htmlspecialchars di PHP integrata in Javascript? So che è abbastanza facile implementarlo da soli, ma l'uso di una funzione integrata, se disponibile, è semplicemente più bello.

Per chi non conosce PHP, htmlspecialchars traduce roba come <htmltag/>in&lt;htmltag/&gt;

Lo so escape()e encodeURI()non funziona così.


php ha degli strumenti davvero buoni, var_dump, print_r, htmlspecialchars ecc. Sfortunatamente sospetto che non sia lo stesso con js. js alert è così scarso. Un modo rapido per vedere che sta arrivando una stringa inaspettata (e invisibile nella finestra di avviso) è quello di avvisare la lunghezza della stringa anziché la stringa stessa.
Melsi,

Possibile duplicato delle stringhe HTML di
escape

Vedi stackoverflow.com/a/12034334/8804293 , ha un'ottima risposta
Elijah Mock

Risposte:


330

Si è verificato un problema con il codice della soluzione: sfuggirà solo alla prima occorrenza di ciascun carattere speciale. Per esempio:

escapeHtml('Kip\'s <b>evil</b> "test" code\'s here');
Actual:   Kip&#039;s &lt;b&gt;evil</b> &quot;test" code's here
Expected: Kip&#039;s &lt;b&gt;evil&lt;/b&gt; &quot;test&quot; code&#039;s here

Ecco il codice che funziona correttamente:

function escapeHtml(text) {
  return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
}

Aggiornare

Il codice seguente produrrà risultati identici a quanto sopra, ma funziona meglio, in particolare su grandi blocchi di testo (grazie a jbo5112 ).

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

5
la cosa bella di questa funzione è che funziona in node.js che di default non ha un dom
booyaa,

6
È più veloce utilizzare una singola funzione di sostituzione e mappatura e la singola sostituzione scala molto meglio. ( jsperf.com/escape-html-special-chars/11 )
jbo5112

1
@ jbo5112 buon punto, non mi rendevo conto che JS consentiva i callback per la sostituzione. Questo codice è più facile da capire, e dubito che radere qualche millisecondo da escapeHtml () farà la differenza a meno che non lo chiami centinaia di volte di fila per qualche motivo.
Kip

Ciò distorcerà gli URL nel testo rendendoli inutilizzabili per plugin come Autolinker.js . C'è un modo per affrontare questo?
Radek Matěj,

4
@ RadekMatěj Anche in quel caso è perfettamente valido (preferibilmente direi) che entrambe le e commerciali devono essere codificate se utilizzate in un documento HTML. Lo considererei comunque un bug con il plugin.
Salta il

31

Questa è la codifica HTML. Non esiste una funzione javascript nativa per farlo, ma puoi google e ottenerne alcuni ben fatti.

Ad esempio http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

EDIT:
Questo è quello che ho testato:

var div = document.createElement('div');
  var text = document.createTextNode('<htmltag/>');
  div.appendChild(text);
  console.log(div.innerHTML);

Produzione: &lt;htmltag/&gt;


Peccato, allora dovrò usare una funzione personalizzata.
Bart van Heukelom,

Puoi provare il metodo nel link che ho incluso nel mio post. Davvero un bel concetto.
Ok

@okw: Ok, per prima cosa ti sei collegato a questo: yuki-onna.co.uk/html/encode.html che fa esattamente ciò che encodeURIComponentfa e non è affatto quello che l'OP ha chiesto. Quindi puoi modificare per favore? Non riesco a annullare il mio -1.
Crescent Fresh,

Sì, il codice di quella pagina sembra logico ma non l'ho provato. Anche se il nuovo link funziona, l'ho verificato da solo. Ho già aggiornato il post qualche tempo fa.
Ok

@BeauCielBleu: No. Gli unici nodi creati sono un singolo divelemento e un nodo di testo. La creazione di un nodo di testo con il testo `<img src = bogus onerror = alert (1337)>` creerà semplicemente un nodo di testo, non un imgelemento.
Tim Down,

26

Merita una lettura: http://bigdingus.com/2007/12/29/html-escaping-in-javascript/

escapeHTML: (function() {
 var MAP = {
   '&': '&amp;',
   '<': '&lt;',
   '>': '&gt;',
   '"': '&#34;',
   "'": '&#39;'
 };
  var repl = function(c) { return MAP[c]; };
  return function(s) {
    return s.replace(/[&<>'"]/g, repl);
  };
})()

Nota : eseguirlo una sola volta. E non eseguirlo su stringhe già codificate, ad esempio &amp;diventa&amp;amp;


3
Questa dovrebbe essere la risposta accettata e più votata. Non sono sicuro del perché non abbia avuto voti. Questo è il benchmarking come il più veloce sia con una stringa di input lunga (326 KB di ricerca di Google) sia con una stringa di input breve su jsperf ( jsperf.com/escape-html-special-chars/11 ). Si prega di votare questo.
jbo5112,

Qual è la differenza tra questa la risposta che ha ottenuto i voti più alti? Perché la funzione interna aggiuntiva ?. Una spiegazione potrebbe aiutare gli utenti a capire meglio
Kosem

19

Con jQuery può essere così:

var escapedValue = $('<div/>').text(value).html();

Dalla domanda correlata Escaping delle stringhe HTML con jQuery

Come menzionato nel commento, le virgolette doppie e le virgolette singole vengono lasciate così come sono per questa implementazione. Ciò significa che questa soluzione non deve essere utilizzata se è necessario creare l'attributo element come stringa HTML non elaborata.


2
qualche idea se c'è qualche sovraccarico in questo - l'aggiunta di un oggetto fittizio al DOM?
Kip

e ci sono altri vantaggi (diciamo, se hai caratteri unicode o qualcosa del genere)?
Kip

4
Qualcosa che ho trovato con questo: virgolette doppie e virgolette singole vengono lasciate così come sono. Ciò rende questo problematico se si desidera utilizzarlo in un valore di attributo.
Kip

1
Per piccoli pezzi di testo, questo richiede 30 volte il tempo necessario per eseguire tutti i rimpiazzi. Si adatta meglio però. Con qualcosa di gigantesco come una pagina dei risultati di ricerca di Google (326KB), è più veloce del 25-30% rispetto ai sostituti o lo fa in javascript direttamente. Tuttavia, tutti perdono costantemente per una singola sostituzione e una funzione di mappatura.
jbo5112,

4
come la gente vota su questa risposta: la risposta ha jquery: +1 - NON sfugge alle virgolette singole e doppie: ummmm .. (grattando la testa) .. +1. <!-- Caps rage begin --> Questa risposta dovrebbe avere un punteggio NEGATIVO poiché NON VIENE ANCHE VICINO A RISPONDERE ALLA DOMANDA "Equivalente HtmlSpecialChars". <!-- Caps rage end -->da-non-non-fuga-citazioni-Gesù Cristo-e-altri-divinità. OMG , gente gioiosa.
Sharky,

19

Ecco una funzione per sfuggire all'HTML:

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

E per decodificare:

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

6

Underscore.js fornisce una funzione per questo:

_.escape(string)

Esce da una stringa per l'inserimento in HTML, sostituendo i caratteri &, <,>, "e '.

http://underscorejs.org/#escape

Non è una funzione Javascript integrata, ma se stai già utilizzando Underscore è un'alternativa migliore rispetto alla scrittura della tua funzione se le stringhe da convertire non sono troppo grandi.


5

Un altro punto a riguardo è quello di rinunciare del tutto alla mappatura dei caratteri e di convertire invece tutti i caratteri indesiderati nei rispettivi riferimenti numerici, ad esempio:

function escapeHtml(raw) {
    return raw.replace(/[&<>"']/g, function onReplace(match) {
        return '&#' + match.charCodeAt(0) + ';';
    });
}

Si noti che il RegEx specificato gestisce solo i caratteri specifici che l'OP ha voluto sfuggire ma, a seconda del contesto in cui verrà utilizzato l'HTML di escape, questi caratteri potrebbero non essere sufficienti. L'articolo di Ryan Grove C'è molto di più nell'escaping HTML rispetto a &, <,>, e " è una buona lettura sull'argomento. E a seconda del contesto, potrebbe essere necessario il seguente RegEx per evitare l'iniezione XSS:

var regex = /[&<>"'` !@$%()=+{}[\]]/g

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

campione :

var toto = "test<br>";
alert(toto.escapeHTML());

3

È probabile che non ti serva una tale funzione. Poiché il tuo codice è già nel browser *, puoi accedere direttamente al DOM invece di generare e codificare HTML che dovrà essere decodificato all'indietro dal browser per essere effettivamente utilizzato.

Utilizzare la innerTextproprietà per inserire il testo normale nel DOM in modo sicuro e molto più veloce rispetto all'utilizzo di una qualsiasi delle funzioni di escape presentate. Ancora più veloce dell'assegnazione di una stringa pre-codificata statica a innerHTML.

Utilizzare classListper modificare le classi, datasetper impostare data-attributi e setAttributeper gli altri.

Tutti questi gestiranno la fuga per te. Più precisamente, non è necessario alcun escape e nessuna codifica verrà eseguita al di sotto di **, poiché si sta lavorando su HTML, la rappresentazione testuale di DOM.

// use existing element
var author = 'John "Superman" Doe <john@example.com>';
var el = document.getElementById('first');
el.dataset.author = author;
el.textContent = 'Author: '+author;

// or create a new element
var a = document.createElement('a');
a.classList.add('important');
a.href = '/search?q=term+"exact"&n=50';
a.textContent = 'Search for "exact" term';
document.body.appendChild(a);

// actual HTML code
console.log(el.outerHTML);
console.log(a.outerHTML);
.important { color: red; }
<div id="first"></div>

* Questa risposta non è destinata agli utenti JavaScript lato server (Node.js, ecc. )

** A meno che non lo si converta esplicitamente in HTML effettivo in seguito. Ad esempio accedendo innerHTML: questo è ciò che accade quando esegui $('<div/>').text(value).html();suggerimenti in altre risposte. Quindi, se il tuo obiettivo finale è quello di inserire alcuni dati nel documento, facendolo in questo modo farai il lavoro due volte. Inoltre puoi vedere che nel codice HTML risultante non tutto è codificato, ma solo il minimo necessario per essere valido. Viene fatto in base al contesto, ecco perché questo metodo jQuery non codifica le virgolette e quindi non dovrebbe essere usato come un escaper generico. L'escape delle virgolette è necessaria quando si costruisce HTML come stringa con dati non attendibili o contenenti virgolette al posto del valore di un attributo. Se usi l'API DOM, non devi preoccuparti di scappare affatto.


Grazie per questo! Ho passato molto tempo a cercare una soluzione così semplice. Una cosa importante che ho scoperto è che se il tuo testo contiene nuove righe, dovrai sostituirle con interruzioni di riga HTML (qualcosa del genere el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')) o impostare la white-spaceproprietà CSS su preoppurepre-wrap
stellatedHexahedron

@stellatedHexahedron, grazie per aver sollevato questo problema. Ho cambiato la mia risposta per raccomandare innerTextinvece di textContent. Mentre è un po 'più lento e presenta alcune differenze nella lettura della proprietà, è più intuitivo in quanto esegue <br>automaticamente la sostituzione durante l'assegnazione.
utente

2

Per gli utenti Node.JS (o gli utenti che utilizzano il runtime Jade nel browser), è possibile utilizzare la funzione di escape di Jade.

require('jade').runtime.escape(...);

Non ha senso scriverlo tu stesso se qualcun altro lo sta mantenendo. :)


1

Sto elaborando un po 'la risposta di okw.

È possibile utilizzare le funzioni DOM del browser per questo.

var utils = {
    dummy: document.createElement('div'),
    escapeHTML: function(s) {
        this.dummy.textContent = s
        return this.dummy.innerHTML
    }
}

utils.escapeHTML('<escapeThis>&')

Questo ritorna &lt;escapeThis&gt;&amp;

Utilizza la funzione standard createElementper creare un elemento invisibile, quindi utilizza la funzione textContentper impostare qualsiasi stringa come contenuto e quindi innerHTMLper ottenere il contenuto nella sua rappresentazione HTML.


0
function htmlspecialchars(str) {
 if (typeof(str) == "string") {
  str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
  str = str.replace(/"/g, "&quot;");
  str = str.replace(/'/g, "&#039;");
  str = str.replace(/</g, "&lt;");
  str = str.replace(/>/g, "&gt;");
  }
 return str;
 }

0

Spero che questo vinca la gara a causa delle sue prestazioni e, soprattutto, non di una logica incatenata che utilizza .replace ('&', '&'). Replace ('<', '<') ...

var mapObj = {
   '&':"&amp;",
   '<':"&lt;",
   '>':"&gt;",
   '"':"&quot;",
   '\'':"&#039;"
};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");

function escapeHtml(str) 
{   
    return str.replace(re, function(matched)
    {
        return mapObj[matched.toLowerCase()];
    });
}

console.log('<script type="text/javascript">alert('Hello World');</script>');
console.log(escapeHtml('<script type="text/javascript">alert('Hello World');</script>'));

0

Uno invertito:

function decodeHtml(text) {
    return text
        .replace(/&amp;/g, '&')
        .replace(/&lt;/ , '<')
        .replace(/&gt;/, '>')
        .replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'");
}

La domanda non sta chiedendo come decodificare le entità. Questo fa l'opposto di ciò che la domanda sta ponendo.
Quentin,

Questo sostituirà solo le prime istanze di &lt;e &gr;in una stringa.
Quentin,

Questo decodificherà solo i cinque caratteri che (al di fuori dei documenti non Unicode) devono essere sfuggiti, non decodificherà quelli che possono essere sfuggiti.
Quentin,

Questo non tiene conto delle regole per quando il punto e virgola è facoltativo.
Quentin,

Se l'HTML dice:, To write a greater than sign in HTML type &amp;gt;verrà visualizzato in modo errato >invece di&gt;
Quentin

0

OWASP consiglia che "[e] xcept per i caratteri alfanumerici, [dovresti] sfuggire a tutti i caratteri con valori ASCII inferiori a 256 con il&#xHH; formato (o un'entità denominata se disponibile) per impedire il passaggio da [un] attributo".

Quindi ecco una funzione che lo fa, con un esempio di utilizzo:

function escapeHTML(unsafe) {
  return unsafe.replace(
    /[\u0000-\u002F]|[\u003A-\u0040]|[\u005B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).substr(-4, 4) + ';'
  )
}
document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('this should break it! " | / % * + , - / ; < = > ^') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
<div></div>


-1
function htmlEscape(str){
    return str.replace(/[&<>'"]/g,x=>'&#'+x.charCodeAt(0)+';')
}

Questa soluzione utilizza ad esempio il codice numerico dei caratteri < viene sostituito da&#60; .

Sebbene le sue prestazioni siano leggermente peggiori rispetto alla soluzione che utilizza una mappa , presenta i vantaggi:

  • Non dipende da una libreria o DOM
  • Abbastanza facile da ricordare (non è necessario memorizzare i 5 caratteri di escape HTML)
  • Piccolo codice
  • Abbastanza veloce (è ancora più veloce di 5 concatenati)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.