HtmlSpecialChars equivalente em Javascript?

167

Aparentemente, isso é mais difícil de encontrar do que eu pensava. E até é tão simples ...

Existe uma função equivalente aos htmlspecialchars do PHP incorporados ao Javascript? Eu sei que é bastante fácil implementar isso você mesmo, mas usar uma função interna, se disponível, é apenas melhor.

Para aqueles que não estão familiarizados com o PHP, htmlspecialchars traduz coisas como <htmltag/>em&lt;htmltag/&gt;

Eu sei disso escape()e encodeURI()não funciona dessa maneira.

Bart van Heukelom
fonte
O php tem algumas ferramentas realmente boas, var_dump, print_r, htmlspecialchars etc. Infelizmente, suspeito que não seja o mesmo com js. O alerta é muito ruim. Uma maneira rápida de ver que alguma sequência inesperada (e invisível na caixa de alerta) está chegando é alertar o comprimento da sequência em vez da sequência.
Melsi
Possível duplicado de cordas Escaping HTML com jQuery
nhahtdh
Veja stackoverflow.com/a/12034334/8804293 , tem uma ótima resposta
Elijah Mock

Respostas:

330

Há um problema com o código da solução - ele escapará apenas da primeira ocorrência de cada caractere especial. Por exemplo:

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

Aqui está o código que funciona corretamente:

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

Atualizar

O código a seguir produzirá resultados idênticos aos acima, mas ele tem um desempenho melhor, principalmente em grandes blocos de texto (obrigado jbo5112 ).

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
Kip
fonte
5
coisa agradável sobre esta função é que ele funciona em node.js que não tem um dom por padrão
Booyaa
6
É mais rápido usar uma única função de substituição e mapeamento, e a substituição única é muito melhor. ( jsperf.com/escape-html-special-chars/11 )
jbo5112
1
@ jbo5112 bom ponto, eu não sabia que JS permitia retornos de chamada para substituição. Esse código é mais fácil de entender, e duvido que a remoção de alguns milissegundos de escapeHtml () faça a diferença, a menos que você o chame centenas de vezes seguidas por algum motivo.
Kip
Isso distorce os URLs no texto, o que os torna inutilizáveis ​​para plugins como o Autolinker.js . Existe alguma maneira de abordar isso?
Radek Matěj
4
@ RadekMatěj Mesmo nesse caso, é perfeitamente válido (de preferência, eu diria) que os dois e comerciais sejam codificados quando usados ​​em um documento HTML. Eu ainda consideraria um bug no plugin.
Kip
31

Isso é codificação HTML. Não há nenhuma função nativa de javascript para fazer isso, mas você pode pesquisar no Google e fazer algumas bem feitas.

Por exemplo, http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

EDIT:
Isto é o que eu testei:

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

Resultado: &lt;htmltag/&gt;

okw
fonte
Pena, vou ter que usar uma função personalizada então.
Bart van Heukelom
Você pode tentar o método no link que eu incluí na minha postagem. Conceito bastante elegante, de fato.
OKW
@okw: Ok, primeiro você vinculou a isso: yuki-onna.co.uk/html/encode.html, que faz exatamente o que encodeURIComponentfaz e não faz o que o OP pediu. Então você pode editar por favor? Não consigo desfazer meu -1.
Crescent Fresh
Sim, o código dessa página parece lógico, mas eu não testei. Embora o novo link funcione, eu mesmo o verifiquei. Já atualizei a postagem há algum tempo.
OKW
@BeauCielBleu: Não. Os únicos nós criados são um único divelemento e um nó de texto. Criar um nó de texto com o texto `<img src = bogus onerror = alert (1337)>` apenas criará um nó de texto, não um imgelemento.
Tim Down
26

Vale a pena ler: 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 : Execute isso apenas uma vez. E não o execute em cadeias já codificadas, por exemplo, &amp;torna-se&amp;amp;

Chris Jacob
fonte
3
Essa deve ser a resposta aceita e mais votada. Não sei por que não teve votos. Esse é o benchmarking como o mais rápido, com uma string de entrada longa (326KB no Google) e curta no jsperf ( jsperf.com/escape-html-special-chars/11 ). Por favor vote nisso.
Jbo5112
Qual é a diferença entre este e a resposta que obteve os votos mais altos ?. Por que a função interna adicional? Uma explicação pode ajudar os usuários a entender melhor
Kosem
19

Com o jQuery, pode ser assim:

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

De questão relacionada Escapando seqüências de caracteres HTML com jQuery

Conforme mencionado no comentário, aspas duplas e aspas simples são deixadas como estão para esta implementação. Isso significa que esta solução não deve ser usada se você precisar criar atributo de elemento como uma string html bruta.

Alexander Yanovets
fonte
2
alguma idéia se houver alguma sobrecarga nisso - adicionar um objeto fictício ao DOM?
Kip
e existem outras vantagens (por exemplo, se você tiver caracteres unicode ou algo assim)?
Kip
4
Algo que encontrei com isso: aspas duplas e aspas simples são deixadas como estão. Isso torna isso problemático se você deseja usá-lo em um valor de atributo.
Kip
1
Para pequenos pedaços de texto, isso leva 30x, enquanto a execução de todas as substituições. Escala melhor embora. Com algo tão gigantesco quanto uma página de resultado de pesquisa do Google (326KB), é 25 a 30% mais rápido do que o substitui ou é feito em javascript direto. No entanto, todos eles perdem consistentemente para uma única substituição e uma função de mapeamento.
Jbo5112
4
como as pessoas votam nesta resposta: a resposta possui jquery: +1 - NÃO escapa aspas simples e duplas: ummmm .. (coçando a cabeça) .. +1. <!-- Caps rage begin --> Esta resposta deve ter pontuação NEGATIVA, uma vez que nem chega perto de responder à pergunta "HtmlSpecialChars equivalente". <!-- Caps rage end -->Ele não escapa citações de Jesus Cristo e outras divindades. OMG você jquery pessoas.
Sharky
19

Aqui está uma função para escapar do HTML:

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

E para decodificar:

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

O Underscore.js fornece uma função para isso:

_.escape(string)

Escapa uma string para inserção em HTML, substituindo &, <,>, "e 'caracteres.

http://underscorejs.org/#escape

Não é uma função Javascript interna, mas se você já estiver usando o Underscore, é uma alternativa melhor do que escrever sua própria função se as seqüências de caracteres para converter não forem muito grandes.

mer10z_tech
fonte
5

Ainda outra abordagem é renunciar a todo o mapeamento de caracteres e converter todos os caracteres indesejados em suas respectivas referências numéricas de caracteres, por exemplo:

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

Observe que o RegEx especificado lida apenas com os caracteres específicos dos quais o OP queria escapar, mas, dependendo do contexto em que o HTML usado será usado, esses caracteres podem não ser suficientes. Artigo de Ryan Grove O escape de HTML é mais do que &, <,>, e " é uma boa leitura do tópico. E, dependendo do seu contexto, o seguinte RegEx pode muito bem ser necessário para evitar a injeção de XSS:

var regex = /[&<>"'` !@$%()=+{}[\]]/g
Fredric
fonte
3
String.prototype.escapeHTML = function() {
        return this.replace(/&/g, "&amp;")
                   .replace(/</g, "&lt;")
                   .replace(/>/g, "&gt;")
                   .replace(/"/g, "&quot;")
                   .replace(/'/g, "&#039;");
    }

amostra:

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

Provavelmente, você não precisa dessa função. Como seu código já está no navegador *, você pode acessar o DOM diretamente, em vez de gerar e codificar HTML, que precisará ser decodificado para trás pelo navegador para ser realmente usado.

Use a innerTextpropriedade para inserir texto sem formatação no DOM com segurança e muito mais rápido do que usar qualquer uma das funções de escape apresentadas. Ainda mais rápido do que atribuir uma sequência pré-codificada estática parainnerHTML .

Use classListpara editar classes, datasetdefinir data-atributos esetAttribute para outros.

Todos estes irão lidar com a fuga para você. Mais precisamente, não é necessário escapar e nenhuma codificação será executada por baixo **, pois você está trabalhando em torno do HTML, a representação textual do DOM.

// use existing element
var author = 'John "Superman" Doe <[email protected]>';
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>

* Esta resposta não se destina a usuários JavaScript do servidor (Node.js, etc.) )

** A menos que você o converta explicitamente em HTML real posteriormente. Por exemplo, acessando innerHTML- é o que acontece quando você executa $('<div/>').text(value).html();sugestões em outras respostas. Portanto, se seu objetivo final é inserir alguns dados no documento, dessa maneira, você fará o trabalho duas vezes. Além disso, você pode ver que no HTML resultante nem tudo é codificado, apenas o mínimo necessário para que seja válido. Isso é feito de acordo com o contexto, é por isso que esse método jQuery não codifica aspas e, portanto, não deve ser usado como um escaper de uso geral. O escape de aspas é necessário quando você está construindo HTML como uma sequência com dados não confiáveis ​​ou contendo aspas no lugar do valor de um atributo. Se você usa a API do DOM, não precisa se preocupar em escapar.

do utilizador
fonte
Obrigado por isso! Passei muito tempo procurando uma solução tão simples. Uma coisa importante que eu descobri é que se o seu texto contém novas linhas, então você terá que quer substituí-los com quebras de linha HTML (algo como el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')), ou definir o CSS white-spacepropriedade para preoupre-wrap
stellatedHexahedron
@stellatedHexahedron, obrigado por levantar esta questão. Alterei minha resposta para recomendar em innerTextvez de textContent. Embora um pouco mais lento e com algumas outras diferenças ao ler a propriedade, é mais intuitivo, pois ele faz a <br>substituição automaticamente ao atribuir a ela.
usuário
2

Para usuários do Node.JS (ou usuários que utilizam o tempo de execução do Jade no navegador), você pode usar a função de escape do Jade.

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

Não faz sentido escrever você mesmo se alguém o está mantendo. :)

BMiner
fonte
1

Estou elaborando um pouco a resposta do okw.

Você pode usar as funções DOM do navegador para isso.

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

utils.escapeHTML('<escapeThis>&')

Isso retorna &lt;escapeThis&gt;&amp;

Ele usa a função padrão createElementpara criar um elemento invisível, depois usa a função textContentpara definir qualquer string como seu conteúdo e, em seguida, innerHTMLpara obter o conteúdo em sua representação HTML.

Jonas Eberle
fonte
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;
 }

fonte
0

Espero que isso vença a corrida devido ao seu desempenho e, o mais importante, não a uma lógica encadeada usando .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>'));
Arejado
fonte
0

Um revertido:

function decodeHtml(text) {
    return text
        .replace(/&amp;/g, '&')
        .replace(/&lt;/ , '<')
        .replace(/&gt;/, '>')
        .replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'");
}
Gleb Dolzikov
fonte
A questão não é perguntar como decodificar entidades. Isso faz o oposto do que a pergunta está pedindo.
Quentin
Isso substituirá apenas as primeiras instâncias de &lt;e &gr;em uma string.
Quentin
Isso decodificará apenas os cinco caracteres que (fora dos documentos não Unicode) devem ser escapados; não decodificará os que podem ser escapados.
Quentin
Isso não leva em consideração as regras para quando o ponto-e-vírgula é opcional.
Quentin
Se o HTML disser:, To write a greater than sign in HTML type &amp;gt;ele será exibido incorretamente em >vez de&gt;
Quentin
0

A OWASP recomenda que "[e] xcept para caracteres alfanuméricos, [você deve] escape de todos os caracteres com valores ASCII menores que 256 com o&#xHH; formato (ou uma entidade nomeada, se disponível) para impedir a desativação de um atributo [um]".

Então, aqui está uma função que faz isso, com um exemplo de uso:

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>

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

Esta solução usa o código numérico dos caracteres, por exemplo, <é substituído por&#60; .

Embora seu desempenho seja um pouco pior que a solução usando um mapa , ele tem as vantagens:

  • Não depende de uma biblioteca ou DOM
  • Muito fácil de lembrar (você não precisa memorizar os 5 caracteres de escape HTML)
  • Pouco código
  • Razoavelmente rápido (ainda é mais rápido que 5 substituições em cadeia)
user202729
fonte