Почему некоторые сайты (или рекламодатели, которые предоставляют клиентам код javascript) используют метод разделения тегов <script> и / или </script> в рамках вызовов document.write()?
Я заметил, что Amazon тоже делает то же самое, например:
<script type='text/javascript'>
if (typeof window['jQuery'] == 'undefined') document.write('<scr'+'ipt type = "text/javascript" src = "http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></sc'+'ript>');
</script>



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я думаю, это для предотвращения интерпретации HTML-парсером браузера <script> и, в основном, </script> как закрывающий тег фактического скрипта, однако я не думаю, что использование document.write - отличная идея для оценки скрипта. блоки, почему бы не использовать DOM ...
var newScript = document.createElement("script");
...
</script> должен быть разбит, потому что в противном случае он преждевременно завершит включающий блок <script></script>. На самом деле он должен быть разделен между < и /, потому что блок сценария предполагается (согласно SGML) как оканчивается любой последовательностью открытого концевого тега (ETAGO) (например, </):
Although the STYLE and SCRIPT elements use CDATA for their data model, for these elements, CDATA must be handled differently by user agents. Markup and entities must be treated as raw text and passed to the application as is. The first occurrence of the character sequence "
</" (end-tag open delimiter) is treated as terminating the end of the element's content. In valid documents, this would be the end tag for the element.
Однако на практике браузеры заканчивают синтаксический анализ блока сценария CDATA только по фактическому закрывающему тегу </script>.
В XHTML нет такой специальной обработки для блоков сценария, поэтому любой символ < (или &) внутри них должен быть &escaped;, как и в любом другом элементе. Однако тогда браузеры, которые разбирают XHTML как HTML старой школы, запутаются. Есть обходные пути, связанные с блоками CDATA, но проще всего избегать использования этих символов без экранирования. Лучшим способом написать элемент сценария из сценария, который работает с любым типом синтаксического анализатора, был бы:
<script type = "text/javascript">
document.write('\x3Cscript type = "text/javascript" src = "foo.js">\x3C/script>');
</script>
/ - допустимая escape-последовательность для /, так почему бы просто не использовать ее вместо тех строковых буквальных escape-последовательностей для <? Например. document.write('<script src=foo.js></script>');. Кроме того, </script> - не единственная последовательность символов, которая может закрывать элемент <script>. Дополнительная информация здесь: mathiasbynens.be/notes/etago@Mathias: </script> в этом случае подойдет, но он будет работать только в HTML; в XHTML без дополнительной упаковки разделов CDATA это все равно ошибка правильности. Также вы можете использовать \x3C во встроенных атрибутах обработчика событий, где < также будет недопустимым как в HTML, так и в XHTML, поэтому он имеет более широкое применение: если бы я выбрал один, легко автоматизированный способ экранирования чувствительных символов в строковых литералах JS для всех контекстов , это тот, который я бы выбрал.
В HTML < можно использовать во встроенных атрибутах обработчика событий. html5.validator.nu/… И вы правы насчет XHTML-совместимости \x3C и так далее, но поскольку XHTML не поддерживает document.write (или innerHTML) в любом случае, я не понимаю, насколько это актуально.
@ MathiasBynens - document.write не имеет значения, это просто пример. OP мог бы использовать innerHTML, это касается сокрытия последовательности символов </ от синтаксического анализатора разметки, где бы это ни происходило. Просто большинство парсеров допускают это внутри элемента скрипта, хотя строго не должны (но парсеры HTML очень терпимы). Вы правы, хотя </ подходит для HTML во всех случаях.
Кто пишет спецификации веб-технологий? Кажется, это не люди, которые на самом деле используют эти технологии.
Я не думаю, что нужно избегать открытия <.... document.write('<script src = "foo.js">\x3C/script>') кажется достаточным для всех браузеров, начиная с IE6. (Я пропустил атрибут type, потому что он не требуется ни в HTML5, ни в каком-либо браузере).
@MattBrowne Вам нужно беспокоиться не только о браузерах. Я получил немного от этого, используя PHP DOMDocument для обработки некоторого HTML, содержащего JS, который построил и вставил <iframe>. Синтаксический анализатор увидел открывающий тег и создал узел DOM, но тег не был завершен, и синтаксический анализатор начал экранировать пробелы в остальной части JS-кода с помощью %20!
@MattBrowne - просто для полноты картины, если метка javascript: требуется во встроенных скриптах в (более старом) IE, если первый скрипт на странице не является JavaScript (например, vbscript), то, возможно, это также требуется для тегов скрипта, следующих после не-js скрипт
Вот еще один вариант, который я использовал, когда хотел сгенерировать встроенный тег скрипта (чтобы он выполнялся немедленно) без необходимости в какой-либо форме экранирования:
<script>
var script = document.createElement('script');
script.src = '/path/to/script.js';
document.write(script.outerHTML);
</script>
(Примечание: вопреки большинству примеров в сети, я не устанавливаю type = "text/javascript" ни на включающий тег, ни на сгенерированный: нет браузера, не имеющего этого по умолчанию, и поэтому он избыточен, но не повредит , если вы не согласны).
Хороший момент re: type. По умолчанию в HTML5 определено как «текст / javascript», так что это бесполезный атрибут. w3.org/html/wg/drafts/html/master/…
Этот лучше, чем принятый ответ, потому что этот вариант можно фактически минимизировать. После минификации этот 'x3C / script>' станет '</script>'.
Решение, опубликованное Бобинсом, отлично подходит для меня. Я хотел предложить и альтернативный метод для будущих посетителей:
if (typeof(jQuery) == 'undefined') {
(function() {
var sct = document.createElement('script');
sct.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js';
sct.type = 'text/javascript';
sct.async = 'true';
var domel = document.getElementsByTagName('script')[0];
domel.parentNode.insertBefore(sct, domel);
})();
}
В этом примере я включил условную загрузку для jQuery, чтобы продемонстрировать вариант использования. Надеюсь, это кому-то пригодится!
нет необходимости определять протокол - URI без протокола работает нормально (//foo.com/bar.js и т. д.)
также нет необходимости устанавливать асинхронный режим. он установлен для всех динамически создаваемых тегов скрипта.
Спас меня! При использовании document.write (<script>) мой сайт показывал пустой экран. Это сработало.
@dmp, кроме случаев, когда запускается из файловой системы, где протоколом будет file: //
</script> внутри строки Javascript интерпретируется анализатором HTML как закрывающий тег, вызывая неожиданное поведение (см. пример на JSFiddle).
Чтобы избежать этого, вы можете поместить свой javascript между комментариями (этот стиль кодирования был обычной практикой, когда Javascript плохо поддерживался браузерами). Это будет работать (см. пример в JSFiddle):
<script type = "text/javascript">
<!--
if (jQuery === undefined) {
document.write('<script type = "text/javascript" src = "http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js"></script>');
}
// -->
</script>
... но, честно говоря, использование document.write - не лучшая практика. Почему бы не манипулировать DOM напрямую?
<script type = "text/javascript">
<!--
if (jQuery === undefined) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://z-ecx.images-amazon.com/images/G/01/javascripts/lib/jquery/jquery-1.2.6.pack._V265113567_.js');
document.body.appendChild(script);
}
// -->
</script>
Если вы хотите использовать чистый JS, вы все равно захотите использовать document.write;)
Извините, я хотел написать - для этого нужна библиотека jQuery, верно? Когда я использовал document.body.append, он выдавал ошибку, что document.body.append не является функцией.
Извините, моя ошибка: я написал append вместо appendChild. Подправил ответ. Спасибо, что обратили внимание!
Это выглядит гораздо более общим, чем ручное разделение строки. Например. разделение невозможно, если строка вставлена механизмом шаблонов.
Это необходимо для того, чтобы синтаксический анализатор не закрывал скрипт-блок преждевременно ...