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.