HTML-кодировка теряется при чтении атрибута из поля ввода - PullRequest
715 голосов
/ 03 августа 2009

Я использую JavaScript, чтобы извлечь значение из скрытого поля и отобразить его в текстовом поле. Значение в скрытом поле кодируется.

Например,

<input id='hiddenId' type='hidden' value='chalk &amp; cheese' />

втягивается в

<input type='text' value='chalk &amp; cheese' />

через некоторый jQuery для получения значения из скрытого поля (именно в этот момент я теряю кодировку):

$('#hiddenId').attr('value')

Проблема в том, что когда я читаю chalk &amp; cheese из скрытого поля, JavaScript, похоже, теряет кодировку. Я не хочу, чтобы значение было chalk & cheese. Я хочу, чтобы литерал amp; был сохранен.

Существует ли библиотека JavaScript или метод jQuery, который будет кодировать строку в HTML-формате?

Ответы [ 25 ]

1054 голосов
/ 03 августа 2009

РЕДАКТИРОВАТЬ: Этот ответ был опубликован давно, и функция htmlDecode представила уязвимость XSS. Он был изменен, изменяя временный элемент с div на textarea, уменьшая вероятность XSS. Но в настоящее время я бы рекомендовал вам использовать API DOMParser, как это предлагается в Другой ответ .


Я использую эти функции:

function htmlEncode(value){
  // Create a in-memory element, set its inner text (which is automatically encoded)
  // Then grab the encoded contents back out. The element never exists on the DOM.
  return $('<textarea/>').text(value).html();
}

function htmlDecode(value){
  return $('<textarea/>').html(value).text();
}

По сути, в памяти создается элемент div, но он никогда не добавляется к документу.

В функции htmlEncode я устанавливаю innerText элемента и извлекаю закодированный innerHTML; в функции htmlDecode я устанавливаю значение innerHTML элемента, и получается innerText.

Проверьте работающий пример здесь .

546 голосов
/ 19 августа 2011

Трюк jQuery не кодирует кавычки, а в IE он лишит вас пробелов.

Основываясь на шаблонном теге escape в Django, который, я думаю, уже интенсивно используется / проверен, я сделал эту функцию, которая делает то, что нужно.

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

function htmlEscape(str) {
    return str
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
}

// I needed the opposite function today, so adding here too:
function htmlUnescape(str){
    return str
        .replace(/&quot;/g, '"')
        .replace(/&#39;/g, "'")
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/g, '&');
}

Обновление 2013-06-17:
В поисках самого быстрого выхода я нашел эту реализацию метода replaceAll:
http://dumpsite.com/forum/index.php?topic=4.msg29#msg29
(также упоминается здесь: Самый быстрый метод для замены всех вхождений символа в строке )
Некоторые результаты производительности здесь:
http://jsperf.com/htmlencoderegex/25

Строка результата идентична встроенным цепочкам replace выше. Я был бы очень рад, если бы кто-то мог объяснить, почему это быстрее!?

Обновление 2015-03-04:
Я только что заметил, что AngularJS использует именно метод, описанный выше:
https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js#L435

Они добавляют пару уточнений - они, кажется, обрабатывают неясную проблему Unicode , а также преобразуют все не алфавитно-цифровые символы в сущности. У меня сложилось впечатление, что в последнем нет необходимости, если для вашего документа указана кодировка UTF8.

Отмечу, что (4 года спустя) Джанго все еще не выполняет ни одну из этих вещей, поэтому я не уверен, насколько они важны:
https://github.com/django/django/blob/1.8b1/django/utils/html.py#L44

Обновление 2016-04-06:
Вы также можете избежать косой черты /. Это не требуется для правильной кодировки HTML, однако OWASP рекомендует в качестве меры безопасности против XSS. (спасибо @JNF за предложение об этом в комментариях)

        .replace(/\//g, '&#x2F;');
79 голосов
/ 12 марта 2013

Вот не-jQuery-версия, которая значительно быстрее, чем jQuery .html() и .replace(). Это сохраняет все пробелы, но, как и версия jQuery, не обрабатывает кавычки.

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

Скорость: http://jsperf.com/htmlencoderegex/17

speed test

Демо: jsFiddle

Выход:

output

Сценарий:

function htmlEncode( html ) {
    return document.createElement( 'a' ).appendChild( 
        document.createTextNode( html ) ).parentNode.innerHTML;
};

function htmlDecode( html ) {
    var a = document.createElement( 'a' ); a.innerHTML = html;
    return a.textContent;
};

document.getElementById( 'text' ).value = htmlEncode( document.getElementById( 'hidden' ).value );

//sanity check
var html = '<div>   &amp; hello</div>';
document.getElementById( 'same' ).textContent = 
      'html === htmlDecode( htmlEncode( html ) ): ' 
    + ( html === htmlDecode( htmlEncode( html ) ) );

HTML:

<input id="hidden" type="hidden" value="chalk    &amp; cheese" />
<input id="text" value="" />
<div id="same"></div>
32 голосов
/ 26 октября 2010

Я знаю, что это старый, но я хотел опубликовать вариант принятый ответ , который будет работать в IE без удаления строк:

function multiLineHtmlEncode(value) {
    var lines = value.split(/\r\n|\r|\n/);
    for (var i = 0; i < lines.length; i++) {
        lines[i] = htmlEncode(lines[i]);
    }
    return lines.join('\r\n');
}

function htmlEncode(value) {
    return $('<div/>').text(value).html();
} 
29 голосов
/ 10 января 2014

Подчеркивание предоставляет _.escape() и _.unescape() методы, которые делают это.

> _.unescape( "chalk &amp; cheese" );
  "chalk & cheese"

> _.escape( "chalk & cheese" );
  "chalk &amp; cheese"
12 голосов
/ 03 ноября 2010

Хороший ответ. Обратите внимание, что если значение для кодирования равно undefined или null в jQuery 1.4.2, вы можете получить такие ошибки, как:

jQuery("<div/>").text(value).html is not a function

OR

Uncaught TypeError: Object has no method 'html'

Решение состоит в том, чтобы изменить функцию для проверки фактического значения:

function htmlEncode(value){ 
    if (value) {
        return jQuery('<div/>').text(value).html(); 
    } else {
        return '';
    }
}
11 голосов
/ 13 октября 2013

Для тех, кто предпочитает простой javascript, вот метод, который я успешно использовал:

function escapeHTML (str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}
6 голосов
/ 24 января 2010

FWIW, кодировка не теряется. Кодировка используется анализатором разметки (браузером) во время загрузки страницы. Как только источник прочитан и проанализирован, и браузер загрузил DOM в память, кодировка была проанализирована в том, что он представляет. Таким образом, к тому времени, когда ваш JS выполняется для чтения чего-либо в памяти, символ, который он получает, представляет собой то, что представляет кодировка.

Возможно, я здесь работаю строго по семантике, но я хотел, чтобы вы поняли цель кодирования. Слово «потерял» звучит так, как будто что-то не работает так, как должно.

5 голосов
/ 03 августа 2009

Прототип имеет встроенный класс String . Так что, если вы используете / планируете использовать Prototype, он делает что-то вроде:

'<div class="article">This is an article</div>'.escapeHTML();
// -> "&lt;div class="article"&gt;This is an article&lt;/div&gt;"
5 голосов
/ 26 июля 2015

Быстрее без Jquery. Вы можете закодировать каждый символ в вашей строке:

function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}

Или просто нацеливайтесь на главных героев, о которых нужно беспокоиться (&, inebreaks, <,>, "и '), например:

function encode(r){
return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"})
}

test.value=encode('Encode HTML entities!\n\n"Safe" escape <script id=\'\'> & useful in <pre> tags!');

testing.innerHTML=test.value;

/*************
* \x26 is &ampersand (it has to be first),
* \x0A is newline,
*************/
<textarea id=test rows="9" cols="55"></textarea>

<div id="testing">www.WHAK.com</div>
...