Декодировать и обратно в JavaScript - PullRequest
205 голосов
/ 13 сентября 2010

У меня есть строки вроде

var str = 'One & two & three';

отображается в HTML веб-сервером. Мне нужно преобразовать эти строки в

'One & two & three'

В настоящее время это то, что я делаю (с помощью jQuery):

$(document.createElement('div')).html('{{ driver.person.name }}').text()

Однако у меня тревожное чувство, что я делаю это неправильно. Я пытался

unescape("&")

но, похоже, не работает, как и decodeURI / decodeURIComponent.

Существуют ли другие, более естественные и элегантные способы сделать это?

Ответы [ 12 ]

267 голосов
/ 13 сентября 2010

Вам нужно декодировать все закодированные сущности HTML или просто & само по себе?

Если вам нужно обработать только &, вы можете сделать это:

var decoded = encoded.replace(/&/g, '&');

Если вам нужно декодировать все сущности HTML, вы можете сделать это без jQuery:

var elem = document.createElement('textarea');
elem.innerHTML = encoded;
var decoded = elem.value;

Пожалуйста, обратите внимание на комментарии Марка, которые выделяют дыры в безопасности в более ранней версии этого ответа и рекомендуют использовать textarea вместо div для предотвращения потенциальных уязвимостей XSS. Эти уязвимости существуют независимо от того, используете ли вы jQuery или простой JavaScript.

76 голосов
/ 15 февраля 2017

Более современным вариантом для интерпретации HTML (текста и других) из JavaScript является поддержка HTML в DOMParser API ( см. Здесь в MDN ).Это позволяет использовать собственный анализатор HTML браузера для преобразования строки в документ HTML.Он поддерживается в новых версиях всех основных браузеров с конца 2014 года.

Если мы просто хотим декодировать некоторое текстовое содержимое, мы можем поместить его в качестве единственного содержимого в теле документа, проанализировать документ и извлечьиз его .body.textContent.

var encodedStr = 'hello & world';

var parser = new DOMParser;
var dom = parser.parseFromString(
    '<!doctype html><body>' + encodedStr,
    'text/html');
var decodedString = dom.body.textContent;

console.log(decodedString);

В проекте спецификации для DOMParser мы видим, что JavaScript не включен для проанализированного документа, поэтому мы можем выполнить это преобразование текста безпроблемы безопасности.

Метод parseFromString(str, type) должен запускать эти шаги в зависимости от type :

  • "text/html"

    Разобрать str с HTML parser и вернуть только что созданный Document.

    Флаг сценария должен быть установлен на «отключен».

    ПРИМЕЧАНИЕ

    script элементы помечаются как неисполняемые, а содержимое noscript анализируется как разметка.

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

38 голосов
/ 11 июля 2015

Matthias Bynens имеет библиотеку для этого: https://github.com/mathiasbynens/he

Пример:

console.log(
    he.decode("J&#246;rg &amp J&#xFC;rgen rocked to &amp; fro ")
);
// Logs "Jörg & Jürgen rocked to & fro"

Я предлагаю отдать предпочтение этому хаку, включающему установку HTML-содержимого элемента и последующее чтение его текстового содержимого,Такие подходы могут работать, но они обманчиво опасны и представляют возможности XSS, если они используются для ненадежного пользовательского ввода.

Если вы действительно не можете переносить загрузку в библиотеку, вы можете использовать хак textarea, описанный в этот ответ на почти повторяющийся вопрос, который, в отличие от предложенных различных похожих подходов, не имеет дыр в безопасности, о которых я знаю:

function decodeEntities(encodedString) {
    var textArea = document.createElement('textarea');
    textArea.innerHTML = encodedString;
    return textArea.value;
}

console.log(decodeEntities('1 &amp; 2')); // '1 & 2'

Но обратите внимание на проблемы безопасности, затрагивающие сходные подходы к этому, которые я перечислю в связанном ответе!Этот подход является взломом, и будущие изменения в допустимом содержимом textarea (или ошибок в определенных браузерах) могут привести к тому, что в коде однажды возникнет дыра в XSS.

23 голосов
/ 02 января 2014
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.

16 голосов
/ 19 ноября 2012

element.innerText также добивается цели.

8 голосов
/ 14 января 2018

В случае, если вы ищете его, как я - тем временем есть хороший и безопасный метод JQuery.

https://api.jquery.com/jquery.parsehtml/

Вы можете f.ex.введите это в вашей консоли:

var x = "test &amp;";
> undefined
$.parseHTML(x)[0].textContent
> "test &"

Так что $ .parseHTML (x) возвращает массив, и если у вас есть HTML-разметка в вашем тексте, array.length будет больше 1.

7 голосов
/ 23 февраля 2018

Вы можете использовать функцию Lodash unescape / escape https://lodash.com/docs/4.17.5#unescape

import unescape from 'lodash/unescape';

const str = unescape('fred, barney, &amp; pebbles');

ул станет 'fred, barney, & pebbles'

7 голосов
/ 28 сентября 2016

jQuery будет кодировать и декодировать для вас.Однако вам нужно использовать тег textarea, а не div.

var str1 = 'One & two & three';
var str2 = "One &amp; two &amp; three";
  
$(document).ready(function() {
   $("#encoded").text(htmlEncode(str1)); 
   $("#decoded").text(htmlDecode(str2));
});

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

function htmlEncode(value) {
  return $('<textarea/>').text(value).html();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

<div id="encoded"></div>
<div id="decoded"></div>
4 голосов
/ 09 января 2013

Сначала создайте <span id="decodeIt" style="display:none;"></span> где-нибудь в теле

Затем присвойте строку, которая будет декодирована как innerHTML:

document.getElementById("decodeIt").innerHTML=stringtodecode

Наконец,

stringtodecode=document.getElementById("decodeIt").innerText

Вот общий код:

var stringtodecode="<B>Hello</B> world<br>";
document.getElementById("decodeIt").innerHTML=stringtodecode;
stringtodecode=document.getElementById("decodeIt").innerText
2 голосов
/ 28 июля 2017

Для однопользовательских парней:

const htmlDecode = innerHTML => Object.assign(document.createElement('textarea'), {innerHTML}).value;

console.log(htmlDecode('Complicated - Dimitri Vegas &amp; Like Mike'));
...