Skip to main content

Атрибут defer у script

defer, це атрибут теґу <script>, який каже браузеру: завантаж цей скрипт паралельно з HTML, але виконай його лише після того, як увесь HTML-документ буде завантажено і розібрано. Виконання відбувається перед подією DOMContentLoaded, тому DOM на цей момент уже повністю побудований.

Теорія

TL;DR

  • Без атрибутів <script> зупиняє парсинг HTML на час завантаження і виконання файлу.
  • defer завантажує скрипт паралельно з розбором HTML і не блокує його.
  • Виконання відкладається до моменту, коли DOM побудовано, і відбувається перед DOMContentLoaded.
  • Кілька defer-скриптів виконуються в порядку появи в розмітці; в async порядок довільний.
  • defer у <head>, це стандартний спосіб підключати основні скрипти застосунку.
  • defer діє тільки для зовнішніх скриптів з src, для інлайнового коду він ігнорується.

Швидкий приклад

html
<head> <!-- завантажується паралельно з HTML, виконується вже на готовому DOM --> <script src="main.js" defer></script> </head> <body> <p>Привіт!</p> </body>

Звичайна поведінка без defer

html
<script src="app.js"></script>

Що відбувається:

  1. Браузер починає завантажувати HTML.
  2. Зустрічає <script>.
  3. Зупиняє парсинг HTML, щоб:
    • скачати app.js,
    • виконати його.
  4. Тільки потім продовжує будувати сторінку.

У результаті сторінка завантажується повільніше, бо JS блокує HTML. Саме тому раніше скрипти радили класти в кінець <body>.

Що робить defer

html
<script src="app.js" defer></script>

Тепер браузер:

  1. Починає завантажувати HTML і JS паралельно.
  2. Не блокує парсинг HTML.
  3. Виконує app.js тільки після того, як DOM повністю побудовано, але до події DOMContentLoaded.

Тобто послідовність така:

  • HTML парситься,
  • скрипт завантажується у фоні,
  • DOM готовий,
  • скрипт виконується,
  • спрацьовує DOMContentLoaded.

Приклад різниці

html
<!-- Без defer --> <script src="slow.js"></script> <p>Привіт!</p> <!-- З defer --> <script src="slow.js" defer></script> <p>Привіт!</p>

З defer текст «Привіт!» з'явиться одразу, поки скрипт ще вантажиться. Без defer сторінка «зависне», доки не завантажиться slow.js.

Коли скриптів кілька

html
<script src="a.js" defer></script> <script src="b.js" defer></script>

Скрипти з defer:

  • завантажуються паралельно;
  • але виконуються в порядку появи в HTML, тобто спочатку a.js, потім b.js.

Це головна відмінність від async, де порядок не гарантується: першим виконається той файл, який першим доїхав по мережі. Тому для залежних одна від одної частин коду підходить лише defer.

Відмінність defer від async

АтрибутКоли завантажуєтьсяКоли виконуєтьсяГарантований порядокТиповий сценарій
(без атрибута)Одразу при зустрічіОдразу (блокує HTML)ТакСтарий JS у <head>
asyncПаралельно з HTMLОдразу після завантаження (не чекає DOM)НіАналітика, реклама, віджети
deferПаралельно з HTMLПісля побудови DOMТакОсновні скрипти застосунку

Загальна таблиця:

ВластивістьБез атрибутаasyncdefer
Завантажує скриптОдразу (блокує HTML)ПаралельноПаралельно
Виконує скриптОдразу після завантаженняОдразу після завантаженняПісля розбору HTML
Порядок виконанняПо порядкуДовільнийПо порядку
Блокує HTMLТакНіНі
Чекає на DOMНіНіТак

Сумісність і вплив на DOMContentLoaded

defer працює в усіх сучасних браузерах. Якщо ставити скрипт у <head>, це найправильніший спосіб підключати JS:

html
<head> <script src="main.js" defer></script> </head>

Браузер не блокує завантаження сторінки і гарантує, що ваш код виконається тоді, коли DOM уже готовий.

DOMContentLoaded спрацьовує після виконання всіх defer-скриптів. Тобто в обробнику цієї події можна бути впевненим, що ваші скрипти вже відпрацювали і зареєстрували все потрібне.

Окремо варто пам'ятати, що <script type="module"> поводиться як defer за замовчуванням, тому для модулів атрибут дописувати не потрібно.

Типові помилки

  • Ставити defer на інлайновий скрипт. Без src атрибут просто ігнорується, і код виконається негайно.
  • Очікувати від async порядку виконання. Якщо b.js залежить від a.js, async зламає його випадковим порядком; потрібен defer.
  • Думати, що defer відкладає завантаження. Файл качається одразу і паралельно, відкладається лише виконання.
  • Класти в defer код, який має відпрацювати якнайраніше. Наприклад, антимерехтливий скрипт теми краще лишити синхронним інлайном.
  • Змішувати defer зі скриптами, які роблять document.write(). У відкладеному скрипті цей виклик просто ігнорується.
  • Все ще тримати скрипти в кінці <body>, «бо так швидше». З defer у <head> браузер почне завантаження раніше і результат буде кращим.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.