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.


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

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;


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;


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.


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

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.


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

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

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

* 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.


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. :)


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.


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

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

0

Um revertido:

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

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>


-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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.