Unescape HTML сущности в Javascript? - PullRequest
133 голосов
/ 16 декабря 2009

У меня есть некоторый код Javascript, который связывается с бэкэндом XML-RPC. XML-RPC возвращает строки вида:

<img src='myimage.jpg'>

Однако, когда я использую Javascript для вставки строк в HTML, они отображаются буквально. Я не вижу изображения, я буквально вижу строку:

<img src='myimage.jpg'>

Я предполагаю, что HTML экранируется по каналу XML-RPC.

Как я могу удалить строку в Javascript? Я попробовал методы на этой странице, но безуспешно: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

Какие есть другие способы диагностики проблемы?

Ответы [ 11 ]

279 голосов
/ 03 декабря 2015

Большинство ответов, приведенных здесь, имеют огромный недостаток: если строка, которую вы пытаетесь преобразовать, не является доверенной, в результате вы получите уязвимость Cross-Site Scripting (XSS) . Для функции из принятого ответа рассмотрите следующее:

htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

Строка здесь содержит HTML-тег без экранирования, поэтому вместо расшифровки чего-либо, функция htmlDecode будет фактически выполнять код JavaScript, указанный внутри строки.

Этого можно избежать, используя DOMParser , который поддерживается во всех современных браузерах :

function htmlDecode(input)
{
  var doc = new DOMParser().parseFromString(input, "text/html");
  return doc.documentElement.textContent;
}

// This returns "<img src='myimage.jpg'>"
htmlDecode("&lt;img src='myimage.jpg'&gt;");

// This returns ""
htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

Эта функция гарантированно не запускает какой-либо код JavaScript в качестве побочного эффекта. Любые HTML-теги будут игнорироваться, будет возвращен только текстовый контент.

Примечание о совместимости : для анализа HTML с DOMParser требуется как минимум Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 или Microsoft Edge. Таким образом, все браузеры без поддержки уже прошли EOL, и по состоянию на 2017 год единственными, которые все еще можно увидеть в дикой природе, иногда являются старые версии Internet Explorer и Safari (обычно их все еще недостаточно, чтобы беспокоиться).

158 голосов
/ 16 декабря 2009

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

Следующий фрагментстарый код ответа с небольшой модификацией: использование textarea вместо div уменьшает уязвимость XSS, но все еще проблематично в IE9 и Firefox.

function htmlDecode(input){
  var e = document.createElement('textarea');
  e.innerHTML = input;
  // handle case of empty input
  return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}

htmlDecode("&lt;img src='myimage.jpg'&gt;"); 
// returns "<img src='myimage.jpg'>"

В основном я создаю DOMэлемент программно, назначить закодированный HTML-код его innerHTML и извлечь nodeValue из текстового узла, созданного при вставке innerHTML.Поскольку он просто создает элемент, но никогда не добавляет его, HTML-код сайта не изменяется.

Он будет работать в кросс-браузерном режиме (включая старые браузеры) и будет принимать все HTML-символы . * 1016.*

EDIT: старая версия этого кода не работала в IE с пустыми вводами, о чем свидетельствует здесь, в jsFiddle (просмотр в IE).Вышеприведенная версия работает со всеми входными данными.

ОБНОВЛЕНИЕ: похоже, это не работает с большой строкой, а также вводит уязвимость безопасности , см. Комментарии.

37 голосов
/ 16 декабря 2009

Если вы используете jQuery:

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

В противном случае используйте Объект кодировщика строго программного обеспечения , который имеет превосходную функцию htmlDecode().

6 голосов
/ 20 октября 2017

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

function unescapeHtml(html) {
    var el = document.createElement('div');
    return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
        el.innerHTML = enc;
        return el.innerText
    });
}
5 голосов
/ 18 декабря 2014

Ответ CMS работает нормально, если только HTML-код, который вы хотите удалить, не очень длинный, длиннее 65536 символов. Потому что тогда в Chrome внутренний HTML разбивается на множество дочерних узлов, каждый длиной не более 65536, и вам нужно их объединить. Эта функция работает также для очень длинных строк:

function unencodeHtmlContent(escapedHtml) {
  var elem = document.createElement('div');
  elem.innerHTML = escapedHtml;
  var result = '';
  // Chrome splits innerHTML into many child nodes, each one at most 65536.
  // Whereas FF creates just one single huge child node.
  for (var i = 0; i < elem.childNodes.length; ++i) {
    result = result + elem.childNodes[i].nodeValue;
  }
  return result;
}

См. Этот ответ о innerHTML Максимальная длина для получения дополнительной информации: https://stackoverflow.com/a/27545633/694469

4 голосов
/ 26 июня 2012

Крис - хороший и элегантный ответ, но он терпит неудачу, если значение undefined . Просто простое улучшение делает его твердым:

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}
3 голосов
/ 16 декабря 2009

Не прямой ответ на ваш вопрос, но не лучше ли вашему RPC вернуть некоторую структуру (будь то XML, JSON или что-то еще) с этими данными изображения (URL в вашем примере) внутри этой структуры?

Тогда вы можете просто разобрать его в своем javascript и построить <img>, используя сам javascript.

Структура, которую вы получаете от RPC, может выглядеть так:

{"img" : ["myimage.jpg", "myimage2.jpg"]}

Я думаю, что так будет лучше, поскольку внедрение кода, полученного из внешнего источника, на вашу страницу не выглядит очень безопасным. Представьте, как кто-то захватывает ваш XML-RPC-скрипт и помещает туда что-то, что вам не нужно (даже некоторый javascript ...)

1 голос
/ 20 февраля 2016

Это лучше:

String::decode = ->
   $('<textarea />').html(this).text()

использование:

"&lt;img src='myimage.jpg'&gt;".decode();

из: HTML Entity Decode

0 голосов
/ 13 марта 2019

Есть вариант, который на 80% продуктивнее ответов на самом верху.

См. Эталонный тест: https://jsperf.com/decode-html12345678/1

performance test

console.log(decodeEntities('test: &gt'));

function decodeEntities(str) {
  // this prevents any overhead from creating the object each time
  const el = decodeEntities.element || document.createElement('textarea')

  // strip script/html tags
  el.innerHTML = str
    .replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '')
    .replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');

  return el.value;
}

Если вам нужно оставить теги, удалите два вызова .replace(...) (вы можете оставить первый, если вам не нужны скрипты).

0 голосов
/ 25 сентября 2017

Все остальные ответы здесь имеют проблемы.

Методы document.createElement ('div') (включая методы, использующие jQuery) выполняют любой переданный ему JavaScript (проблема безопасности), а метод DOMParser.parseFromString () удаляет пробелы. Вот чистое решение javascript, которое не имеет ни одной проблемы:

function htmlDecode(html) {
    var textarea = document.createElement("textarea");
    html= html.replace(/\r/g, String.fromCharCode(0xe000)); // Replace "\r" with reserved unicode character.
    textarea.innerHTML = html;
    var result = textarea.value;
    return result.replace(new RegExp(String.fromCharCode(0xe000), 'g'), '\r');
}

TextArea используется специально, чтобы избежать выполнения кода JS. Это проходит эти:

htmlDecode('&lt;&amp;&nbsp;&gt;'); // returns "<& >" with non-breaking space.
htmlDecode('  '); // returns "  "
htmlDecode('<img src="dummy" onerror="alert(\'xss\')">'); // Does not execute alert()
htmlDecode('\r\n') // returns "\r\n", doesn't lose the \r like other solutions.
...