(см дальше вниз для ответа на вопрос, как актуализация комментарии из ниже OP)
Может ли это быть обработаны с HTML DOM и JavaScript?
Нет, как только текст находится в DOM, концепция «экранирования» не применяется. HTML Исходный текст должен быть экранирован так, чтобы он правильно разбирался в DOM; как только он находится в DOM, он не сбежит.
Это может быть немного сложно понять, поэтому давайте использовать пример. Вот некоторые HTML исходный текст (например, в HTML-файл, который будет просматривать с помощью браузера):
<div>This & That</div>
Как только это будет проанализирован в DOM с помощью браузера, текст внутри DIV является This & That
, потому что &
был истолкован в этом пункте.
Итак, вам нужно поймать это раньше, прежде чем текст будет разобран в DOM браузером. После этого вы не сможете справиться, уже слишком поздно.
Отдельно, строка, с которой вы начинаете, недействительна, если в ней есть такие вещи, как <div>This & That</div>
. Предварительная обработка этой недопустимой строки будет сложной. Вы не можете просто использовать встроенные функции вашей среды (PHP или что-то еще, что вы используете на стороне сервера), потому что они также избегают этих тегов. Вам нужно будет выполнить обработку текста, извлекая только те части, которые вы хотите обработать, а затем выполните их с помощью процесса экранирования. Этот процесс будет сложным. &
, за которым следуют пробелы, достаточно просто, но если в исходном тексте есть неэквивалентные объекты, как вы знаете, следует ли их избегать или нет? Вы предполагаете, что если строка содержит &
, вы оставите ее в покое? Или превратить его в &amp;
? (Что совершенно верно, так вы показываете фактическую строку &
на странице HTML.)
Что вам действительно нужно сделать, это исправить основную проблему: вещь, создающая эти недопустимые, полукодированные строки.
Редактировать: Из нашего комментария потока ниже, вопрос совершенно иначе, чем это казалось из вашего примера (это не означает, критически).Чтобы повторить комментарии для тех, кто пришел к этому, вы сказали, что получаете эти строки из innerHTML
от WebKit, и я сказал, что это было нечетно, innerHTML
должен правильно закодировать &
(и указал вам на из test pages, что и предполагалось). Ваш ответ был:
Это работает для &. Но одна и та же тестовая страница не работает для таких объектов, как ©, ®, « и многие другие.
Это изменяет характер вопроса. Вы хотите сделать сущности из символов, которые, будучи совершенно правильными, когда они используются буквально (при условии, что у вас есть правильное кодирование текста), могут быть выражены как сущности вместо этого и, следовательно, более устойчивы к изменениям текстового кодирования.
Мы можем это сделать. Согласно the spec значения символов в строке JavaScript составляют UTF-16 (с использованием Unicode Normalized Form C), и любое преобразование из кодировки исходного кода (ISO 8859-1, Windows-1252, UTF-8, независимо) выполняется до того, как среда выполнения JavaScript увидит это. (Если вы не на 100% уверены, что знаете, что я имею в виду под кодировкой символов, стоит остановиться сейчас, уйдя и прочитав от Joel Spolsky, затем вернувшись.) Итак, это входная сторона. На стороне вывода объекты HTML идентифицируют точки кода Unicode. Таким образом, мы можем надежно преобразовать из строк JavaScript в объекты HTML.
Дьявол в деталях, хотя, как всегда. JavaScript явно предполагает, что каждое 16-битовое значение является символом (см. Раздел 8.4 в спецификации), хотя на самом деле это неверно для UTF-16 —. Одно 16-битное значение может быть «суррогатным» (например, 0xD800), которое имеет смысл только в сочетании со следующим значением, что означает, что два «символа» в строке JavaScript на самом деле являются одним символом. Это не редкость для дальневосточных языков.
Так надежное преобразование, которое начинается со строки JavaScript и приводит к тому, что объект HTML не может считать, что символ «JavaScript» фактически равен символу в тексте, он должен обрабатывать суррогаты. К счастью, делать это легко, потому что умные люди, определяющие Unicode, сделали его мертвым: первое суррогатное значение всегда находится в диапазоне 0xD800-0xDBFF (включительно), а второй суррогат всегда находится в диапазоне 0xDC00-0xDFFF (включительно). Поэтому в любое время, когда вы видите пару «символов» в строке JavaScript, которые соответствуют этим диапазонам, вы имеете дело с одним символом, определенным суррогатной парой. Формулы для преобразования из пары суррогатных значений в значение кодовой точки приведены в приведенных выше ссылках, хотя и довольно тупо; Я нахожу this page гораздо более доступным.
Вооружившись всей этой информацией, мы можем написать функцию, которая возьмет строку JavaScript и будет искать символы (реальные символы, которые могут быть длиной один или два символа), вы можете захотеть превратиться в сущности, заменив их именованные объектные сущности из карты или числовых лиц, если мы не имеем их в нашем имени карты:
// A map of the entities we want to handle.
// The numbers on the left are the Unicode code point values; their
// matching named entity strings are on the right.
var entityMap = {
"160": " ",
"161": "¡",
"162": "&#cent;",
"163": "&#pound;",
"164": "&#curren;",
"165": "&#yen;",
"166": "&#brvbar;",
"167": "&#sect;",
"168": "&#uml;",
"169": "©",
// ...and lots and lots more, see http://www.w3.org/TR/REC-html40/sgml/entities.html
"8364": "€" // Last one must not have a comma after it, IE doesn't like trailing commas
};
// The function to do the work.
// Accepts a string, returns a string with replacements made.
function prepEntities(str) {
// The regular expression below uses an alternation to look for a surrogate pair _or_
// a single character that we might want to make an entity out of. The first part of the
// alternation (the [\uD800-\uDBFF][\uDC00-\uDFFF] before the |), you want to leave
// alone, it searches for the surrogates. The second part of the alternation you can
// adjust as you see fit, depending on how conservative you want to be. The example
// below uses [\u0000-\u001f\u0080-\uFFFF], meaning that it will match and convert any
// character with a value from 0 to 31 ("control characters") or above 127 -- e.g., if
// it's not "printable ASCII" (in the old parlance), convert it. That's probably
// overkill, but you said you wanted to make entities out of things, so... :-)
return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0000-\u001f\u0080-\uFFFF]/g, function(match) {
var high, low, charValue, rep
// Get the character value, handling surrogate pairs
if (match.length == 2) {
// It's a surrogate pair, calculate the Unicode code point
high = match.charCodeAt(0) - 0xD800;
low = match.charCodeAt(1) - 0xDC00;
charValue = (high * 0x400) + low + 0x10000;
}
else {
// Not a surrogate pair, the value *is* the Unicode code point
charValue = match.charCodeAt(0);
}
// See if we have a mapping for it
rep = entityMap[charValue];
if (!rep) {
// No, use a numeric entity. Here we brazenly (and possibly mistakenly)
rep = "&#" + charValue + ";";
}
// Return replacement
return rep;
});
}
Вы должны быть хорошо передавая все HTML через него, так как, если эти символы появляются в значениях атрибутов, вы почти наверняка хотите их закодировать там.
У меня не используется выше в производстве (я на самом деле написал для этого ответа, потому что проблема заинтриговала меня), и это полностью поставляется без гарантийного любого вида.Я пытался обеспечить, чтобы он обрабатывал суррогатные пары, потому что это необходимо для дальневосточных языков, и поддержка их - это то, что мы все должны делать сейчас, когда мир стал меньше.
Полный пример страницы:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
#log p {
margin: 0;
padding: 0;
}
</style>
<script type='text/javascript'>
// Make the function available as a global, but define it within a scoping
// function so we can have data (the `entityMap`) that only it has access to
var prepEntities = (function() {
// A map of the entities we want to handle.
// The numbers on the left are the Unicode code point values; their
// matching named entity strings are on the right.
var entityMap = {
"160": " ",
"161": "¡",
"162": "&#cent;",
"163": "&#pound;",
"164": "&#curren;",
"165": "&#yen;",
"166": "&#brvbar;",
"167": "&#sect;",
"168": "&#uml;",
"169": "©",
// ...and lots and lots more, see http://www.w3.org/TR/REC-html40/sgml/entities.html
"8364": "€" // Last one must not have a comma after it, IE doesn't like trailing commas
};
// The function to do the work.
// Accepts a string, returns a string with replacements made.
function prepEntities(str) {
// The regular expression below uses an alternation to look for a surrogate pair _or_
// a single character that we might want to make an entity out of. The first part of the
// alternation (the [\uD800-\uDBFF][\uDC00-\uDFFF] before the |), you want to leave
// alone, it searches for the surrogates. The second part of the alternation you can
// adjust as you see fit, depending on how conservative you want to be. The example
// below uses [\u0000-\u001f\u0080-\uFFFF], meaning that it will match and convert any
// character with a value from 0 to 31 ("control characters") or above 127 -- e.g., if
// it's not "printable ASCII" (in the old parlance), convert it. That's probably
// overkill, but you said you wanted to make entities out of things, so... :-)
return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0000-\u001f\u0080-\uFFFF]/g, function(match) {
var high, low, charValue, rep
// Get the character value, handling surrogate pairs
if (match.length == 2) {
// It's a surrogate pair, calculate the Unicode code point
high = match.charCodeAt(0) - 0xD800;
low = match.charCodeAt(1) - 0xDC00;
charValue = (high * 0x400) + low + 0x10000;
}
else {
// Not a surrogate pair, the value *is* the Unicode code point
charValue = match.charCodeAt(0);
}
// See if we have a mapping for it
rep = entityMap[charValue];
if (!rep) {
// No, use a numeric entity. Here we brazenly (and possibly mistakenly)
rep = "&#" + charValue + ";";
}
// Return replacement
return rep;
});
}
// Return the function reference out of the scoping function to publish it
return prepEntities;
})();
function go() {
var d = document.getElementById('d1');
var s = d.innerHTML;
alert("Before: " + s);
s = prepEntities(s);
alert("After: " + s);
}
</script>
</head>
<body>
<div id='d1'>Copyright: © Yen: ¥ Cedilla: ¸ Surrogate pair: 𐀀</div>
<input type='button' id='btnGo' value='Go' onclick="return go();">
</body>
</html>
Там я включил седиль как пример преобразования в цифровой сущности, а не по имени одного (так как я покинул cedil
из моей очень небольшой пример карты). И обратите внимание, что суррогатная пара в конце появляется в первом предупреждении как два «символа» из-за того, как JavaScript обрабатывает UTF-16.
каким образом строки генерируются? – matpol
Можете ли вы установить '.innerText' ваших якорей - я думаю, что непослушные символы исчезают. – James