Как именно работает <script defer = «defer»>?

208

У меня есть несколько <script>элементов, и код в некоторых из них зависит от кода в других <script>элементах. Я видел, что deferатрибут может пригодиться здесь, поскольку он позволяет откладывать блоки кода в процессе выполнения.

Чтобы проверить это, я выполнил это на Chrome: http://jsfiddle.net/xXZMN/ .

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

Тем не менее, это предупреждает 2 - 1 - 3. Почему это не насторожило 1 - 2 - 3?

pimvdb
источник
2
Может быть, проверить эту статью . И, как всегда, IE по-своему понимает, что что-то значит, и решил сначала загрузить скрипт, но отложить выполнение до загрузки тела (как правило).
Брэд Кристи
Спасибо, однако тестовая страница имеет другой результат в Chrome: websiteoptimization.com/speed/tweak/defer/test . На скриншоте показано, как я этого ожидаю, а Chrome, похоже, сначала выполняет отложенное выполнение.
pimvdb
1
Я думаю, вы найдете в IE определение defer, совпадающее с намерением W3C по defer в спецификации DOM Level 1.
Марк на Ramp51
41
Как уже указывал Алохчи в своем ответе, согласно стандарту HTML defer действует только при указании src. Это может быть причиной того, что ваш пример не работает должным образом в большинстве браузеров.
Панкрат
2
@Pankrat Правдивая история! Попробуйте jsfiddle.net/xXZMN/50 Протестировано в Firefox24
m93a

Ответы:

51

ОБНОВЛЕНО: 19.02.2016

Считайте этот ответ устаревшим. Обратитесь к другим ответам на этот пост для получения информации, относящейся к более новой версии браузера.


По сути, defer указывает браузеру подождать «пока он не будет готов», прежде чем выполнять javascript в этом блоке сценария. Обычно это происходит после завершения загрузки DOM и document.readyState == 4

Атрибут defer относится только к Internet Explorer. В Internet Explorer 8 в Windows 7 результат, который я вижу на вашей тестовой странице JS Fiddle, 1 - 2 - 3.

Результаты могут отличаться от браузера к браузеру.

http://msdn.microsoft.com/en-us/library/ms533719(v=vs.85).aspx

Вопреки распространенному мнению, IE следует стандартам чаще, чем люди допускают, в действительности атрибут defer определен в спецификации DOM Level 1 http://www.w3.org/TR/REC-DOM-Level-1/level -он-html.html

Определение отсрочки в W3C: http://www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer :

«Когда этот атрибут установлен, этот логический атрибут подсказывает агенту пользователя, что сценарий не будет генерировать никакого содержимого документа (например, нет« document.write »в javascript), и, таким образом, агент пользователя может продолжить анализ и рендеринг».

Mark At Ramp51
источник
8
@ MarkAtRamp51 - Если ваш ответ устарел, вы должны отредактировать его, а не жаловаться на отрицательные отзывы в комментариях к другим ответам. Снижение голосов за ответы, которые "не полезны".
Кристиан Конкл,
10
@ChristianConkle Я ценю урок этикета, но другие ответы здесь актуальны. Я имел в виду тот факт, что неправильный ответ не был выбран в то время, когда был задан вопрос. Возможно, вам следует контролировать тех, кто распространяет ложные оценки сообщества, неправильно выбирающего ответы, вместо того, чтобы пытаться напомнить людям, что со временем все меняется, и контекст важен. Я не вижу смысла в удалении моего ответа, так как историческая информация также важна.
Марк на Ramp51
3
«Я не вижу смысла в удалении своего ответа, так как историческая информация также полезна». В таком случае, как насчет добавления заметки в начале, указывающей, что она применима только к pre-HTML5, а затем ссылки на «правильную» ( актуальный) ответ? Это должно избавить вас от многих неприятностей (если вы говорите как парень, у которого один раз был принят «неправильный» ответ, и на него «оказывалось давление со стороны сверстников», чтобы он в итоге изменил его).
mgibsonbr
3
@ Лео, разве это не должно быть помечено? Поиском "html5 defer script" это третий результат в Google. Этот ответ дает многим пользователям устаревшее и неправильное определение. (Текущее определение: «Указывает, что пользовательский агент может отложить обработку сценария. См. Определение атрибута defer в HTML 4.0.»).
Малавос
2
@ MarkAtRamp51 Я думаю, что вы должны обновить свой ответ. Любой, кто найдет этот вопрос и ваш ответ, не узнает его историческую информацию. Для них это будет выглядеть так, будто это правильный ответ сегодня. Вот так работает интернет. Поэтому вы должны отредактировать свой ответ, отметить, что он был когда-то правильным, и обратиться к правильному ответу.
Juuro
167

Несколько фрагментов из спецификации HTML5: http://w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

Атрибуты defer и async не должны указываться, если атрибут src отсутствует.


Есть три возможных режима, которые можно выбрать с помощью этих атрибутов [async и defer]. Если атрибут async присутствует, сценарий будет выполняться асинхронно, как только он станет доступен. Если атрибут async отсутствует, но присутствует атрибут defer, тогда скрипт выполняется после завершения анализа страницы. Если ни один из атрибутов не присутствует, сценарий извлекается и выполняется немедленно, прежде чем пользовательский агент продолжит анализ страницы.


Точные детали обработки для этих атрибутов, по большей части историческим причинам, несколько нетривиальны, включая ряд аспектов HTML. Поэтому требования к реализации по необходимости разбросаны по всей спецификации. Приведенные ниже алгоритмы (в этом разделе) описывают суть этой обработки, но на эти алгоритмы ссылаются и ссылаются правила синтаксического анализа для начальных и конечных тегов сценариев в HTML, во внешнем контенте и в XML - правилах для document.write. () метод, обработка сценариев и т. д.


Если элемент имеет атрибут src, а элемент имеет атрибут defer, и элемент помечен как «вставленный синтаксический анализатор», а элемент не имеет асинхронного атрибута:

Элемент должен быть добавлен в конец списка сценариев, которые будут выполняться после завершения анализа документа, связанного с документом синтаксического анализатора, создавшего элемент.

Alohci
источник
37
Возможно, мой ответ не позволит людям проголосовать за мой ответ из-за вашего бесполезного комментария. Принятый ответ не неправильный, ответ другой, потому что в начале 2011 года спецификация HTML5 была менее актуальна для основных веб-браузеров, чем в настоящее время. Этот ответ может быть лучше в будущем, но принятый ответ не является ошибочным ни по одному стандарту.
Марк на Ramp51
3
Хотя полезно знать, что говорится в спецификации, оказывается, что некоторые браузеры, такие как IE <9, deferплохо работают . Если вы используете defer, вы не можете полагаться на файлы сценариев, выполняемые по порядку в некоторых браузерах.
Flimm
2
@Flimm Не только IE, похоже, что порядок выполнения не гарантируется и в Firefox .
Франклин Ю
Первая цитата больше не действительна, верно? Теперь я могу прочитать это: «Атрибут не должен быть указан, если атрибут src отсутствует, или если скрипт не является классическим скриптом». И классический сценарий тоже один без src = "".
Феликс Санц,
158

Реальный ответ: потому что вы не можете доверять отсрочке.

Концептуально, defer и async отличаются следующим образом:

асинхронной позволяет загружать скрипт в фоновом режиме без блокировки. Затем, в момент завершения загрузки, рендеринг блокируется, и этот скрипт выполняется. Визуализация возобновляется после выполнения сценария.

defer делает то же самое, за исключением утверждений, гарантирующих, что сценарии выполняются в том порядке, в котором они были указаны на странице, и что они будут выполняться после завершения анализа документа. Таким образом, некоторые сценарии могут закончить загрузку, а затем сидеть и ждать сценариев, которые загружались позже, но появлялись перед ними.

К сожалению, из-за того, что на самом деле является стандартным сражением, определение defer варьируется от спецификации к спецификации, и даже в самых последних спецификациях не дает полезной гарантии. Как показывают ответы здесь и эта проблема , браузеры по-разному реализуют отсрочку:

  • В некоторых ситуациях в некоторых браузерах есть ошибка, из-за которой deferскрипты работают не по порядку.
  • Некоторые браузеры задерживают DOMContentLoadedсобытие до тех пор, пока deferскрипты не загрузятся, а некоторые - нет.
  • Некоторые браузеры подчиняются deferна <script>элементах с инлайн кодом и без srcатрибута, и некоторые его игнорировать.

К счастью, в спецификации, по крайней мере, указано, что асинхронные переопределения отложены. Таким образом, вы можете рассматривать все сценарии как асинхронные и получать широкую поддержку браузеров следующим образом:

<script defer async src="..."></script>

При таком подходе 98% браузеров, используемых по всему миру, и 99% в США избежат блокировки.

(Если вам нужно подождать, пока документ не завершит синтаксический анализ, прослушайте событие DOMContentLoadedсобытия или воспользуйтесь удобной .ready()функцией jQuery. В любом случае вы захотите сделать это, чтобы элегантно использовать браузеры, которые вообще не реализуются defer.)

Крис Москини
источник
13
Спасибо, ваш ответ был самым полезным для меня!
Маркус
5
Я считаю, что это неправильно. Преимущество defer заключается в том, что он не выполняется до тех пор, пока не завершится разбор страницы. На этой странице есть хорошее изображение, объясняющее разницу между асинхронностью и отсрочкой
tinkerr
1
@tinkerr В концепции вы правы; на практике это не подтверждается. Поскольку она не реализована последовательно, гарантия последовательности не является универсальной, и поэтому не является гарантией. При реализации чего-то вы заботитесь о выполнении. Намерение дизайна симпатично, но не особенно полезно.
Крис Москини
Просто хочу отметить, что Opera поддерживает deferатрибут с версии 15 , которая была выпущена 2 июня 2013 года .
1
@VikasBansal Для старых браузеров, которые не поддерживают асинхронность, а именно для старых версий IE.
Крис Москини
13

deferможет использоваться только в <script>теге для включения внешнего скрипта . Следовательно, рекомендуется использовать в <script>-tags в <head>-section.

Раджеш Пол
источник
8

Атрибут defer работает только с тегами scripts с src. Нашел способ подражать defer для встроенных скриптов. Используйте событие DOMContentLoaded.

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

Это связано с тем, что событие DOMContentLoaded возникает после полной загрузки отложенных сценариев.

Биджой Анупам
источник
6

Атрибут defer предназначен только для внешних сценариев (должен использоваться только при наличии атрибута src).

Soumitra
источник
4

Взгляните на эту прекрасную статью « Глубокое погружение в мутные воды загрузки скриптов », разработанную разработчиком Google Джейком Арчибальдом, написанную в 2013 году.

Цитирую соответствующий раздел из этой статьи:

Перенести

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

Spec говорит : скачать вместе, выполнить по порядку перед DOMContentLoaded. Игнорируйте «defer» в сценариях без «src».

IE <10 говорит : я мог бы выполнить 2.js в середине выполнения 1.js. Разве это не весело?

Браузеры в красном говорят : я понятия не имею, что это за «отсрочка», я собираюсь загрузить скрипты, как будто их там не было.

Другие браузеры говорят : хорошо, но я не могу игнорировать «defer» в сценариях без «src».

(Я добавлю, что ранние версии Firefox вызывают DOMContentLoaded до завершения работы deferсценариев, согласно этому комментарию .)

Современные браузеры, кажется, поддерживают asyncдолжным образом, но вы должны быть в порядке со сценариями, работающими не по порядку и, возможно, до DOMContentLoaded.

Флимм
источник
1

Этот логический атрибут устанавливается для указания браузеру, что сценарий должен выполняться после анализа документа. Поскольку эта функция еще не была реализована во всех других основных браузерах, авторы не должны предполагать, что выполнение сценария будет фактически отложено. Никогда не вызывайте document.write () из отложенного скрипта (начиная с Gecko 1.9.2, это уничтожит документ). Атрибут defer не должен использоваться в сценариях, которые не имеют атрибута src. Начиная с Gecko 1.9.2, атрибут defer игнорируется в сценариях, которые не имеют атрибута src. Однако в Gecko 1.9.1 даже встроенные скрипты откладываются, если установлен атрибут defer.

defer работает с Chrome, Firefox, т.е.> 7 и Safari

ссылка: https://developer.mozilla.org/en-US/docs/HTML/Element/script

S-Sharma
источник
0

Атрибут defer является логическим атрибутом.

Когда он присутствует, он указывает, что скрипт выполняется, когда страница закончила анализ.

Примечание. Атрибут defer предназначен только для внешних сценариев (должен использоваться только при наличии атрибута src).

Примечание. Существует несколько способов выполнения внешнего скрипта:

Если присутствует асинхронность: сценарий выполняется асинхронно с остальной частью страницы (сценарий будет выполняться, пока страница продолжит синтаксический анализ) Если асинхронность отсутствует и присутствует отсрочка: сценарий выполняется после завершения анализа страницы. нет ни асинхронного, ни отложенного: скрипт извлекается и выполняется немедленно, прежде чем браузер продолжит анализ страницы

srikanth_k
источник