Навіщо розділяти тег <script> під час написання його документом document.write ()?


268

Чому деякі сайти (або рекламодавці, які надають клієнтам 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>

Відповіді:


373

</script>його потрібно розбити, тому що в іншому випадку це <script></script>занадто рано закінчиться блоку. Дійсно, його слід розділити між <та /, тому що блок сценарію (згідно SGML) повинен бути припинений будь-якою послідовністю відкритого кінцевого тега (ETAGO) (тобто </) :

Хоча елементи STYLE та SCRIPT використовують CDATA для своєї моделі даних, для цих елементів користувацькі агенти повинні керувати CDATA по-різному. Розмітка та об'єкти повинні розглядатися як неочищений текст і передаватися до програми як є. Перше виникнення символьної послідовності " </" (кінцевий тег відкритого роздільника) трактується як закінчення кінця вмісту елемента. У дійсних документах це буде кінцевим тегом для елемента.

Однак на практиці браузери лише завершують аналіз блоку скриптів CDATA на фактичному </script>тезі.

У XHTML немає такої спеціальної обробки блоків сценаріїв, тому будь-який <(або &) символ всередині них повинен бути &escaped;як у будь-якого іншого елемента. Однак тоді веб-переглядачі, які аналізують XHTML як HTML старої школи, заплутуються. Існують способи вирішення проблем із блоками CDATA, але найпростіше просто уникнути використання цих символів без нагляду. Кращим способом написання елемента сценарію з сценарію, який працює на будь-якому типі аналізатора, буде:

<script type="text/javascript">
    document.write('\x3Cscript type="text/javascript" src="foo.js">\x3C/script>');
</script>

30
\/є дійсною послідовністю евакуації для /, так чому б не просто використовувати це замість цих рядкових буквальних ухилень для <? Напр document.write('<script src=foo.js><\/script>');. Також </script>це не єдина послідовність символів, яка може закрити <script>елемент. Ще трохи інформації тут: mathiasbynens.be/notes/etago
Mathias Bynens

11
@Mathias: <\/script>в цьому випадку добре, але він працюватиме лише в HTML; у XHTML без додаткового обгортання розділу CDATA, це все-таки помилка сформованості. Крім того, ви можете використовувати \x3Cатрибути обробника вбудованих подій, де <також недійсні як у HTML, так і в XHTML, тому він має більш широку застосовність: якби я вибирав один, легко автоматизований спосіб уникнути чутливих символів у літературних рядках JS для всіх контекстів, це той, на який я б пішов.
bobince

3
У HTML <може використовуватися атрибути обробника вбудованих подій. html5.validator.nu/… І ви маєте рацію щодо сумісності XHTML \x3Cсічі, але оскільки XHTML не підтримує document.write(або innerHTML) все одно, я не бачу, наскільки це актуально.
Mathias Bynens

2
@ MathiasBynens document.write- не має ніякого значення, це, як правило, є прикладом. ОП міг би використовувати внутрішній HTML, це про приховування приховування </послідовності символів від аналізатора розмітки, де б це не відбулося. Просто більшість парсерів терпить це всередині елемента скрипту, коли строго вони не повинні (але HTML-парсери дуже толерантні). Ви прав, хоча цього <\/в усіх випадках вистачає для HTML.
RobG

3
Я не думаю, що уникнути відкриття <не потрібно ...., document.write('<script src="foo.js">\x3C/script>')здається, достатньо для всіх браузерів назад до IE6. (Я не залишив атрибут типу, тому що він не потрібен в HTML5, а також не застосовується так, як вимагає будь-який браузер.)
Метт Браун

34

Ось ще один варіант, який я використав, коли хотів генерувати тег сценарію вбудований (щоб він виконувався негайно), не потребуючи жодної форми ескадрів:

<script>
    var script = document.createElement('script');
    script.src = '/path/to/script.js';
    document.write(script.outerHTML);
</script>

(Примітка. На відміну від більшості прикладів в мережі, я не встановлюю type="text/javascript"ні тега, що додається, ні згенерованого: жоден браузер не має цього за замовчуванням, і тому він є зайвим, але не зашкодить, якщо ви не згодні).


Хороший момент повтор: введіть. За замовчуванням HTML5 визначається як "text / javascript", тому це марний атрибут. w3.org/html/wg/drafts/html/master/…
Лука,

8
Цей варіант кращий, ніж прийнятий відповідь, оскільки ця варіація може бути фактично спрощена. Цей "x3C / script>" стане "</script>" після мінімізації.
Золтан Кочан

20

Я думаю, що це заважає HTML-аналізатору браузера інтерпретувати <script> і головним чином </script> як завершальний тег фактичного сценарію, проте я не думаю, що використання document.write є чудовою ідеєю для оцінки сценарію блоки, чому б не використовувати DOM ...

var newScript = document.createElement("script");
...

4
Потрібно не допустити, щоб аналізатор передчасно
закривав

9

Рішення, яке Бобінс розмістив, працює для мене ідеально. Я хотів запропонувати альтернативний метод і майбутнім відвідувачам:

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 для демонстрації випадку використання. Сподіваюся, що комусь корисно!


3
не потрібно виявляти протокол - URI без протоколу працює чудово ('//foo.com/bar.js' тощо)
dmp

3
також не потрібно встановлювати асинхронізацію. він встановлений для всіх динамічно створених тегів сценарію.
Noishe

Врятували мене! Під час використання document.write (<script>) мій сайт показував порожній екран. Це спрацювало.
Томаш Гонсалес

@dmp, за винятком запуску з файлової системи, де буде протокол файлу: //
mplungjan

9

</script>Усередині рядка litteral 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>

1
Якщо ви хочете використовувати чистий JS, ви все ще хочете використовувати document.write;)
Nirav Zaveri

Вибачте, я мав намір написати - для цього потрібна бібліотека jQuery, правда? Коли я використовував, document.body.append, він видав помилку, що document.body.append не є функцією.
Нірав Завери

1
Вибачте, моя помилка: я написав appendзамість appendChild. Виправлено відповідь. Дякую, що помітили!
Матьє Родіч

1
Це виглядає набагато загальніше, ніж розділення рядка вручну. Розбиття напр., Неможливо, якщо рядок вставляється двигуном шаблону.
w1th0utnam3
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.