HTML-кодировка теряется при чтении атрибута из поля ввода

741

Im использует 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, похоже, потерял кодировку. Чтобы выйти из " и ', я хочу, чтобы кодировка оставалась.

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

  • 0
    Можете ли вы показать Javascript, который вы используете?
  • 1
    добавил, как я получаю значение из скрытого поля
Показать ещё 4 комментария
Теги:
escaping
html-escape-characters

24 ответа

1098
Лучший ответ

Я использую следующие функции:

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

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

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

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

Проверьте приведенный ниже пример .

  • 95
    Это работает для большинства сценариев, но эта реализация htmlDecode устранит любые дополнительные пробелы. Поэтому для некоторых значений «input» введите! = HtmlDecode (htmlEncode (input)). Это было проблемой для нас в некоторых сценариях. Например, если input = "<p> \ t Hi \ n There </ p>", кодирование / декодирование в обе стороны даст "<p> Hi There </ p>". В большинстве случаев это нормально, но иногда это не так. :)
  • 0
    Зависит от браузера, в Firefox он включает пробелы, новые строки ... В IE он удаляет все.
Показать ещё 25 комментариев
562

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

На основе escape templatetag в 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 года спустя) Django все равно не делает ни одной из этих вещей, поэтому я не уверен, насколько они важны:
https://github.com/django/django/blob/1.8b1/django/utils/html.py#L44

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

        .replace(/\//g, '&#x2F;');
  • 2
    Вы также можете использовать &apos; вместо &#39;
  • 30
    @Ferruccio ... и по причинам, почему бы не использовать & apos; см: stackoverflow.com/questions/2083754/... blogs.msdn.com/b/kirillosenkov/archive/2010/03/19/... fishbowl.pastiche.org/2003/07/01/the_curse_of_apos
Показать ещё 28 комментариев
81

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

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

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

Изображение 2231

Демо: Изображение 2232

Вывод:

Изображение 2233

Script:

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>
  • 17
    Возникает вопрос: почему это уже не глобальная функция в JS ?!
  • 2
    версия non-regex .replace() недавно предложенная @SEoF, оказывается значительно быстрее: jsperf.com/htmlencoderegex/22
Показать ещё 11 комментариев
32

Я знаю, что это старый, но я хотел опубликовать вариант принятого ответа, который будет работать в 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();
} 
28

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

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

> _.escape( "chalk & cheese" );
  "chalk &amp; cheese"
  • 0
    У Лодаша тоже есть похожий метод.
12

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

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

ИЛИ

Uncaught TypeError: Object has no method 'html'

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

function htmlEncode(value){ 
    if (value) {
        return jQuery('<div/>').text(value).html(); 
    } else {
        return '';
    }
}
  • 8
    jQuery('<div/>').text(value || '').html()
  • 3
    @roufamatic - Хороший лайнер. Но проверка непустого value с помощью if избавляет от необходимости создавать DIV на лету и получать его значение. Это может быть намного более производительным, если htmlEncode вызывается много И, если это вероятно, что value будет пустым.
Показать ещё 2 комментария
9

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

function escapeHTML (str)
{
    var div = document.createElement('div');
    var text = document.createTextNode(str);
    div.appendChild(text);
    return div.innerHTML;
}
5

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

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

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

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

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

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

5

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

'<div class="article">This is an article</div>'.escapeHTML();
// -> "&lt;div class="article"&gt;This is an article&lt;/div&gt;"
  • 9
    Посмотрев на решение Prototype, это все, что он делает ... .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); Достаточно просто.
  • 4
    разве это не должно делать что-то с кавычками тоже? это не хорошо
Показать ещё 3 комментария
4

Вот простое решение для javascript. Он расширяет объект String с помощью метода "HTMLEncode", который может использоваться для объекта без параметра или с параметром.

String.prototype.HTMLEncode = function(str) {
  var result = "";
  var str = (arguments.length===1) ? str : this;
  for(var i=0; i<str.length; i++) {
     var chrcode = str.charCodeAt(i);
     result+=(chrcode>128) ? "&#"+chrcode+";" : str.substr(i,1)
   }
   return result;
}
// TEST
console.log("stetaewteaw æø".HTMLEncode());
console.log("stetaewteaw æø".HTMLEncode("æåøåæå"))

Я создал gist "метод HTMLEncode для javascript" .

3

На основе angular sanitize... (синтаксис модуля es6)

// ref: https://github.com/angular/angular.js/blob/v1.3.14/src/ngSanitize/sanitize.js
const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
const NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;

const decodeElem = document.createElement('pre');


/**
 * Decodes html encoded text, so that the actual string may
 * be used.
 * @param value
 * @returns {string} decoded text
 */
export function decode(value) {
  if (!value) return '';
  decodeElem.innerHTML = value.replace(/</g, '&lt;');
  return decodeElem.textContent;
}


/**
 * Encodes all potentially dangerous characters, so that the
 * resulting string can be safely inserted into attribute or
 * element text.
 * @param value
 * @returns {string} encoded text
 */
export function encode(value) {
  if (value === null || value === undefined) return '';
  return String(value).
    replace(/&/g, '&amp;').
    replace(SURROGATE_PAIR_REGEXP, value => {
      var hi = value.charCodeAt(0);
      var low = value.charCodeAt(1);
      return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
    }).
    replace(NON_ALPHANUMERIC_REGEXP, value => {
      return '&#' + value.charCodeAt(0) + ';';
    }).
    replace(/</g, '&lt;').
    replace(/>/g, '&gt;');
}

export default {encode,decode};
  • 0
    Хотя мне действительно нравится этот ответ, и на самом деле я думаю, что это хороший подход, у меня есть сомнения, является ли побитовый оператор if (value === null | value === undefined) return ''; опечатка или на самом деле особенность? Если это так, зачем использовать этот, а не общий || ? Спасибо!!
  • 1
    @AlejandroVales Я уверен, что это была опечатка ... исправлено.
Показать ещё 3 комментария
2

У меня была аналогичная проблема и решить ее с помощью функции encodeURIComponent из JavaScript (документация)

Например, в вашем случае, если вы используете:

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

и

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

вы получите chalk%20%26%20cheese. Сохраняются даже пробелы.

В моем случае мне пришлось кодировать одну обратную косую черту, и этот код отлично работает

encodeURIComponent('name/surname')

и я получил name%2Fsurname

2

Вам не нужно выходить/кодировать значения, чтобы передавать их из одного поля ввода в другое.

<form>
 <input id="button" type="button" value="Click me">
 <input type="hidden" id="hiddenId" name="hiddenId" value="I like cheese">
 <input type="text" id="output" name="output">
</form>
<script>
    $(document).ready(function(e) {
        $('#button').click(function(e) {
            $('#output').val($('#hiddenId').val());
        });
    });
</script>

JS не идет вставлять необработанный HTML-код или что-то еще; он просто сообщает DOM установить свойство value (или атрибут; не уверен). В любом случае, DOM обрабатывает любые проблемы с кодировкой для вас. Если вы не делаете что-то странное, например, используя document.write или eval, HTML-кодирование будет эффективно прозрачным.

Если вы говорите о создании нового текстового поля для хранения результата... это все равно так же просто. Просто передайте статическую часть HTML в jQuery, а затем установите остальные свойства/атрибуты объекта, который он возвращает вам.

$box = $('<input type="text" name="whatever">').val($('#hiddenId').val());
2

afaik в javascript нет никаких прямых методов кодирования/декодирования HTML.

Однако, что вы можете сделать, это использовать JS для создания произвольного элемента, установить его внутренний текст, а затем прочитать его с помощью innerHTML.

скажем, с jQuery это должно работать:

var helper = $('chalk & cheese').hide().appendTo('body');
var htmled = helper.html();
helper.remove();

или что-то в этом роде

  • 0
    Я нахожу понижение голоса немного забавным, учитывая, что этот ответ почти идентичен ответу, у которого более 870 голосов, и который был опубликован чуть позже.
1

Здесь немного, что эмулирует функцию Server.HTMLEncode из Microsoft ASP, написанную на чистом JavaScript:

function htmlEncode(s) {
  var ntable = {
    "&": "amp",
    "<": "lt",
    ">": "gt",
    "\"": "quot"
  };
  s = s.replace(/[&<>"]/g, function(ch) {
    return "&" + ntable[ch] + ";";
  })
  s = s.replace(/[^ -\x7e]/g, function(ch) {
    return "&#" + ch.charCodeAt(0).toString() + ";";
  });
  return s;
}

Результат не кодирует апострофы, а кодирует другие специальные HTML-символы и любой символ вне диапазона 0x20-0x7e.

1

Я столкнулся с некоторыми проблемами с обратной косой чертой в моей строке "Домен\Пользователь".

Я добавил это к другим экранам из ответа Anentropic

.replace(/\\/g, '&#92;')

Что я нашел здесь: Как избежать обратной косой черты в JavaScript?

1

HtmlEnкодирует заданное значение

  var htmlEncodeContainer = $('<div />');
  function htmlEncode(value) {
    if (value) {
      return htmlEncodeContainer.text(value).html();
    } else {
      return '';
    }
  }
1
<script>
String.prototype.htmlEncode = function () {
    return String(this)
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');

}

var aString = '<script>alert("I hack your site")</script>';
console.log(aString.htmlEncode());
</script>

Выведет: &lt;script&gt;alert(&quot;I hack your site&quot;)&lt;/script&gt;

.htmlEncode() будет доступен для всех строк, определенных после определения.

  • 0
    Как правило, расширение прототипов не очень хорошая идея.
1
var htmlEnDeCode = (function() {
    var charToEntityRegex,
        entityToCharRegex,
        charToEntity,
        entityToChar;

    function resetCharacterEntities() {
        charToEntity = {};
        entityToChar = {};
        // add the default set
        addCharacterEntities({
            '&amp;'     :   '&',
            '&gt;'      :   '>',
            '&lt;'      :   '<',
            '&quot;'    :   '"',
            '&#39;'     :   "'"
        });
    }

    function addCharacterEntities(newEntities) {
        var charKeys = [],
            entityKeys = [],
            key, echar;
        for (key in newEntities) {
            echar = newEntities[key];
            entityToChar[key] = echar;
            charToEntity[echar] = key;
            charKeys.push(echar);
            entityKeys.push(key);
        }
        charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
        entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
    }

    function htmlEncode(value){
        var htmlEncodeReplaceFn = function(match, capture) {
            return charToEntity[capture];
        };

        return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
    }

    function htmlDecode(value) {
        var htmlDecodeReplaceFn = function(match, capture) {
            return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
        };

        return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
    }

    resetCharacterEntities();

    return {
        htmlEncode: htmlEncode,
        htmlDecode: htmlDecode
    };
})();

Это из исходного кода ExtJS.

1

Если вы хотите использовать jQuery. Я нашел это:

http://www.jquerysdk.com/api/jQuery.htmlspecialchars

(часть плагина jquery.string, предлагаемого jQuery SDK)

Проблема с Prototype, я считаю, заключается в том, что она расширяет базовые объекты в JavaScript и будет несовместима с любым jQuery, который вы, возможно, использовали. Конечно, если вы уже используете Prototype, а не jQuery, это не будет проблемой.

РЕДАКТИРОВАТЬ: Также есть это, который является портом строковых утилит Prototype для jQuery:

http://stilldesigning.com/dotstring/

0

Моя функция чистого JS:

/**
 * HTML entities encode
 *
 * @param {string} str Input text
 * @return {string} Filtered text
 */
function htmlencode (str){

  var div = document.createElement('div');
  div.appendChild(document.createTextNode(str));
  return div.innerHTML;
}

HTML-объекты JavaScript кодируют и декодируют

0

Используя некоторые из других ответов здесь, я сделал версию, которая заменяет все соответствующие символы за один проход, независимо от количества различных кодированных символов (только один вызов для replace()), поэтому будет быстрее для больших строк.

Он не полагается на DOM API для существования или в других библиотеках.

window.encodeHTML = (function() {
    function escapeRegex(s) {
        return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
    }
    var encodings = {
        '&'  : '&amp;',
        '"'  : '&quot;',
        '\'' : '&#39;',
        '<'  : '&lt;',
        '>'  : '&gt;',
        '\\' : '&#x2F;'
    };
    function encode(what) { return encodings[what]; };
    var specialChars = new RegExp('[' +
        escapeRegex(Object.keys(encodings).join('')) +
    ']', 'g');

    return function(text) { return text.replace(specialChars, encode); };
})();

Запустив это однажды, вы можете позвонить

encodeHTML('<>&"\'')

Чтобы получить &lt;&gt;&amp;&quot;&#39;

0

Выбор того, что escapeHTML() в prototype.js

Добавление этого скрипта поможет вам избежатьHTML:

String.prototype.escapeHTML = function() { 
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
}

теперь вы можете вызвать метод escapeHTML для строк в вашем скрипте, например:

var escapedString = "<h1>this is HTML</h1>".escapeHTML();
// gives: "&lt;h1&gt;this is HTML&lt;/h1&gt;"

Надеюсь, что это поможет любому, кто ищет простое решение без необходимости включать весь prototype.js

Ещё вопросы

Сообщество Overcoder
Наверх
Меню