it-swarm.com.ru

Unescape HTML сущности в Javascript?

У меня есть некоторый код 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/

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

118
Joseph Turian

Я использую следующий метод:

function htmlDecode(input){
  var e = document.createElement('div');
  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-символов .

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

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

155
CMS

Большинство ответов, приведенных здесь, имеют огромный недостаток: если строка, которую вы пытаетесь преобразовать, не является доверенной, то вы получите межсайтовый скриптинг (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 (обычно их все еще недостаточно, чтобы беспокоиться).

235
Wladimir Palant

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

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

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

39
Chris Fulstow

Хитрость заключается в том, чтобы использовать возможности браузера для декодирования специальных символов 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
    });
}
6
Ben White

Ответ 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

5
KajMagnus

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

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}
4
nerijus

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

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

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

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

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

3
kender

Это лучше:

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

использовать:

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

от: HTML Entity Decode

1
Sergio Belevskij

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

Методы 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.
0
Dwayne

Я использую это в своем проекте: вдохновленный другими ответами но с дополнительным безопасным параметром, может быть полезен, когда вы имеете дело с украшенными персонажами

var decodeEntities=(function(){

    var el=document.createElement('div');
    return function(str, safeEscape){

        if(str && typeof str === 'string'){

            str=str.replace(/\</g, '&lt;');

            el.innerHTML=str;
            if(el.innerText){

                str=el.innerText;
                el.innerText='';
            }
            else if(el.textContent){

                str=el.textContent;
                el.textContent='';
            }

            if(safeEscape)
                str=str.replace(/\</g, '&lt;');
        }
        return str;
    }
})();

И это можно использовать как:

var label='safe <b> character &eacute;ntity</b>';
var safehtml='<div title="'+decodeEntities(label)+'">'+decodeEntities(label, true)+'</div>';
0
tmx976