Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Атрибут defer у script». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`defer` каже браузеру завантажити скрипт паралельно з HTML, але виконати його лише після того, як увесь документ буде розібрано, тобто безпосередньо перед `DOMContentLoaded`.** Парсинг HTML не блокується, тому сторінка показується швидше. Кілька `defer`-скриптів завантажуються паралельно, але виконуються строго в порядку появи в розмітці, на відміну від `async`, де порядок не гарантований. Це найправильніший спосіб підключати основні скрипти застосунку прямо в `<head>`. ```html <head> <script src="main.js" defer></script> </head> ``` **Ключове:** `defer`, це «завантаж одразу, виконай потім, по порядку і після готового DOM».Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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 | Так | Основні скрипти застосунку | Загальна таблиця: | Властивість | **Без атрибута** | `async` | `defer` | | --- | --- | --- | --- | | Завантажує скрипт | Одразу (блокує 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>` браузер почне завантаження раніше і результат буде кращим.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.