HtmlSpecialChars эквивалент в Javascript?


167

Видимо, найти это сложнее, чем я думал. И это даже так просто ...

Есть ли функция, эквивалентная PHP htmlspecialchars, встроенная в Javascript? Я знаю, что это довольно легко реализовать самостоятельно, но использование встроенной функции, если она доступна, просто приятнее.

Для тех, кто не знаком с PHP, htmlspecialchars переводит такие вещи, как <htmltag/>в&lt;htmltag/&gt;

Я знаю это escape()и encodeURI()не работаю таким образом.


В php есть несколько действительно хороших инструментов: var_dump, print_r, htmlspecialchars и т. д. К сожалению, я подозреваю, что с js все не так. JS оповещение так плохо. Быстрый способ увидеть, что появляется какая-то неожиданная (и невидимая в окне предупреждения) строка, - предупредить длину строки вместо строки itslef.
Мелси

Возможные дубликаты Escaping HTML-строк с jQuery
nhahtdh

См. Stackoverflow.com/a/12034334/8804293 , у него есть отличный ответ
Элайджа Мок

Ответы:


330

Существует проблема с вашим кодом решения - он будет избегать только первое вхождение каждого специального символа. Например:

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

Вот код, который работает правильно:

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

Обновить

Следующий код даст результаты, идентичные приведенным выше, но он работает лучше, особенно на больших блоках текста (спасибо jbo5112 ).

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

5
хорошая особенность этой функции в том, что она работает в node.js, который по умолчанию не имеет dom
booyaa

6
Быстрее использовать одну функцию замены и сопоставления, а одна замена масштабируется намного лучше. ( jsperf.com/escape-html-special-chars/11 )
jbo5112

1
@ jbo5112 хороший момент, я не осознавал, что JS допускает обратные вызовы для замены. Этот код проще для понимания, и я сомневаюсь, что сокращение на несколько миллисекунд от escapeHtml () будет иметь значение, если вы по какой-то причине не вызываете его сотни раз подряд.
Кип

Это искажает URL-адреса в тексте, что делает их непригодными для плагинов, таких как Autolinker.js . Есть ли способ как подойти к этому?
Радек Матей

4
@ RadekMatěj Даже в этом случае вполне допустимо (желательно, я бы сказал, лучше) кодировать оба амперсанда при использовании в HTML-документе. Я все еще считаю это ошибкой с плагином.
Кип

31

Это кодировка HTML. Для этого нет встроенной функции javascript, но вы можете подключиться к Google и получить некоторые из них.

Например, http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

РЕДАКТИРОВАТЬ:
Это то, что я проверял:

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

Вывод: &lt;htmltag/&gt;


Жаль, я просто должен использовать пользовательскую функцию тогда.
Барт ван Хейкелом

Вы можете попробовать метод по ссылке, которую я включил в свой пост. Довольно опрятная концепция.
ОК

@okw: Хорошо, сначала вы связались с этим: yuki-onna.co.uk/html/encode.html, который делает именно то, что encodeURIComponentделает, а вовсе не то, что попросил ОП. Так что вы можете отредактировать, пожалуйста? Я не могу отменить мой -1.
Crescent Fresh

Да, код этой страницы выглядит логичным, но я не проверял его. Хотя новая ссылка работает, я сам ее проверил. Я уже обновил пост некоторое время назад.
ОК

@BeauCielBleu: Нет. Единственные узлы, которые создаются, это один divэлемент и текстовый узел. Создание текстового узла с текстом `<img src = bogus onerror = alert (1337)>` просто создаст текстовый узел, а не imgэлемент.
Тим Даун

26

Стоит прочитать: 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);
  };
})()

Примечание : запускайте только один раз. И не запускайте его на уже закодированных строках, например, &amp;становится&amp;amp;


3
Это должен быть принятый и получивший наибольшее количество голосов ответ. Я не уверен, почему у него не было голосов. Это самый быстрый бенчмаркинг с длинной (326 ГБ результаты поиска Google) и короткой строкой ввода в jsperf ( jsperf.com/escape-html-special-chars/11 ). Пожалуйста, проголосуйте за это.
jbo5112

В чем разница между ответом, получившим наибольшее количество голосов? Почему дополнительная внутренняя функция? Объяснение может помочь пользователям лучше понять
Kosem

19

С jQuery это может быть так:

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

Из смежного вопроса Экранирование строк HTML с помощью jQuery

Как упомянуто в комментарии, двойные кавычки и одинарные кавычки остаются для этой реализации как есть. Это означает, что это решение не следует использовать, если вам нужно сделать атрибут элемента в виде необработанной HTML-строки.


2
Любая идея, если есть какие-либо накладные расходы на это - добавление фиктивного объекта в DOM?
Кип

и есть ли другие преимущества (скажем, если у вас есть символы юникода или что-то)?
Кип

4
Что-то, что я нашел с этим: двойные кавычки и одинарные кавычки остаются как есть. Это делает это проблематичным, если вы хотите использовать его в качестве значения атрибута.
Кип

1
Для небольших фрагментов текста это занимает 30 раз, пока выполняются все замены. Это действительно масштабируется все же лучше. Что-то такое гигантское, как страница результатов поиска Google (326 КБ), это на 25-30% быстрее, чем заменяет или делает это в прямом JavaScript. Однако все они последовательно проигрывают одной замене и функции отображения.
jbo5112

4
как люди голосуют за этот ответ: в ответе есть jquery: +1 - НЕ избегает одинарных и двойных кавычек: мммм .. (почесывая голову) .. +1. <!-- Caps rage begin --> Этот ответ должен иметь ОТРИЦАТЕЛЬНУЮ оценку, так как ДАЖЕ НЕ ЗАКРЫВАЕТСЯ, ЧТОБЫ ОТВЕТИТЬ НА ВОПРОС «HtmlSpecialChars эквивалент». <!-- Caps rage end -->он-делает-не-спусковое кавычки-господи-Christ-и-другие божествам. OMG вы JQuery люди.
Sharky

19

Вот функция для выхода из HTML:

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

И расшифровать:

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 предоставляет функцию для этого:

_.escape(string)

Экранирует строку для вставки в HTML, заменяя символы &, <,>, "и".

http://underscorejs.org/#escape

Это не встроенная функция Javascript, но если вы уже используете Underscore, это лучшая альтернатива, чем написание собственной функции, если строки для преобразования не слишком велики.


5

Еще один способ сделать это состоит в том, чтобы полностью отказаться от отображения всех символов и вместо этого преобразовать все нежелательные символы в их соответствующие числовые ссылки на символы, например:

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

Обратите внимание, что указанный RegEx обрабатывает только определенные символы, которые OP хотел экранировать, но, в зависимости от контекста, в котором будет использоваться экранированный HTML, этих символов может быть недостаточно. Статья Райана Гроува. В HTML есть нечто большее, чем &, <,>, и « является хорошим чтением по этой теме. И в зависимости от вашего контекста может потребоваться следующий RegEx, чтобы избежать внедрения 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;");
    }

образец :

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

3

Скорее всего, вам не нужна такая функция. Поскольку ваш код уже находится в браузере *, вы можете напрямую обращаться к DOM, а не генерировать и кодировать HTML-код, который должен быть декодирован в обратном направлении браузером для фактического использования.

Используйте innerTextсвойство для вставки простого текста в DOM безопасно и намного быстрее, чем с помощью любой из представленных escape-функций. Даже быстрее, чем присвоение статической предварительно кодированной строки innerHTML.

Используйте classListдля редактирования классов, datasetдля установки data-атрибутов и setAttributeдля других.

Все это поможет вам убежать. Точнее, экранирование не требуется, и кодирование не будет выполняться под **, поскольку вы работаете с HTML, текстовым представлением 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>

* Этот ответ не предназначен для пользователей JavaScript на стороне сервера (Node.js и т. Д. )

** Если вы не конвертируете его в фактический HTML впоследствии. Например, доступ innerHTML- это то, что происходит, когда вы запускаете $('<div/>').text(value).html();предложенный в других ответах. Поэтому, если ваша конечная цель - вставить некоторые данные в документ, сделав это таким образом, вы сделаете эту работу дважды. Также вы можете видеть, что в полученном HTML не все закодировано, только тот минимум, который необходим для его правильности. Это делается в зависимости от контекста, поэтому этот метод jQuery не кодирует кавычки и, следовательно, не должен использоваться в качестве escape-кода общего назначения. Экранирование кавычек необходимо, когда вы создаете HTML как строку с ненадежными или содержащими цитаты данными вместо значения атрибута. Если вы используете DOM API, вам вовсе не нужно заботиться о том, чтобы избежать этого.


Спасибо за это! Я долго пытался найти такое простое решение. Одна важная вещь, которую я обнаружил, заключается в том, что если ваш текст содержит новые строки, то вам придется либо заменить их на разрывы строк HTML (что-то вроде el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')), либо установить для white-spaceсвойства CSS значение preorpre-wrap
stellatedHexahedron

@stellatedHexahedron, спасибо, что подняли этот вопрос. Я изменил свой ответ, чтобы рекомендовать innerTextвместо textContent. Хотя он немного медленнее и имеет некоторые другие различия при чтении свойства, он более интуитивен в том смысле, что он выполняет <br>замену автоматически при назначении ему свойства .
пользователь

2

Для пользователей Node.JS (или пользователей, использующих Jade Runtime в браузере), вы можете использовать функцию escape Jade.

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

Нет смысла писать это самостоятельно, если кто-то другой поддерживает это. :)


1

Я немного уточняю ответ ОК.

Вы можете использовать функции DOM браузера для этого.

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

utils.escapeHTML('<escapeThis>&')

Это возвращает &lt;escapeThis&gt;&amp;

Он использует стандартную функцию createElementдля создания невидимого элемента, затем использует функцию, textContentчтобы установить любую строку в качестве ее содержимого, а затем innerHTMLполучить содержимое в своем представлении 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

Надеюсь, что это выиграет гонку из-за ее производительности и, что важнее всего, не цепочки логики с использованием .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

Перевернутый один:

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

Вопрос не в том, как декодировать объекты. Это противоположно тому, что задает вопрос.
Квентин

Это заменит только первые экземпляры &lt;и &gr;в строке.
Квентин

Это будет декодировать только пять символов, которые (за исключением документов, отличных от Unicode) должны быть экранированы, но не будет декодировать те, которые могут быть экранированы.
Квентин

Это не учитывает правила, когда точка с запятой является необязательной.
Квентин

Если HTML говорит:, To write a greater than sign in HTML type &amp;gt;он будет отображаться некорректно >вместо&gt;
Квентин

0

OWASP рекомендует, чтобы «[e] xcept для буквенно-цифровых символов, [вы должны] экранировать все символы со значениями ASCII меньше 256 с &#xHH;форматом (или именованным объектом, если он доступен), чтобы предотвратить переключение из атрибута [an]».

Итак, вот функция, которая делает это, с примером использования:

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

Это решение использует числовой код символов, например <, заменяется на &#60;.

Хотя его производительность немного хуже, чем у решения с использованием карты , у него есть свои преимущества:

  • Не зависит от библиотеки или DOM
  • Довольно легко запомнить (вам не нужно запоминать 5 экранирующих символов HTML)
  • Маленький код
  • Достаточно быстро (это все же быстрее, чем 5 цепочек замены)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.