На сучасних вебсайтах скрипти часто “важчі” за HTML: їх розмір завантаження більший, і час обробки теж довший.
Коли браузер завантажує HTML і зустрічає тег <script>...</script>, він не може продовжувати будівництво DOM. Він повинен виконати скрипт прямо зараз. Те ж саме відбувається для зовнішніх скриптів <script src="..."></script>: браузер повинен зачекати, щоб скрипт завантажився, виконати завантажений скрипт, і тільки тоді він може обробити решту сторінки.
Це призводить до двох важливих проблем:
- Скрипти не можуть бачити елементи DOM під ними, тому вони не можуть додавати обробники тощо.
- Якщо у верхній частині сторінки є громіздкий скрипт, він “блокує сторінку”. Користувачі не можуть побачити вміст сторінки, поки він не завантажиться та не запуститься:
<p>...вміст перед скриптом...</p>
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- Це не видно, поки скрипт завантажується -->
<p>...вміст після скрипту...</p>
Для цього є деякі обхідні шляхи. Наприклад, ми можемо розмістити скрипт внизу сторінки. Тоді він зможе бачити елементи над ним і не блокуватиме показ вмісту сторінки:
<body>
...весь вміст знаходиться над скриптом...
<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>
Але це рішення далеко не ідеальне. Наприклад, браузер помічає скрипт (і може почати його завантаження) лише після того, як повністю завантажить HTML-документ. Для великих документів HTML це може спричинити помітну затримку.
Такі речі невидимі для людей, які використовують дуже швидкі з’єднання, але багато людей у світі все ще мають низьку швидкість Інтернету та використовують далеко не ідеальне мобільне інтернет-з’єднання.
На щастя, є два атрибути <script>, які вирішують нашу проблему: defer і async.
defer
Атрибут defer повідомляє браузеру, що йому не треба чекати на скрипт. Замість цього браузер продовжить обробляти HTML, будувати DOM. Скрипт завантажується “у фоновому режимі”, а потім запускається, коли DOM повністю побудовано.
Ось той самий приклад, що й вище, але з defer:
<p>...вміст перед скриптом...</p>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<!-- видно відразу -->
<p>...вміст після скрипту...</p>
Іншими словами:
- Скрипти з
deferніколи не блокують сторінку. - Скрипти з
deferзавжди виконуються, коли DOM готово (але перед подієюDOMContentLoaded).
Наступний приклад демонструє другу частину:
<p>...вміст перед скриптами...</p>
<script>
document.addEventListener('DOMContentLoaded', () => alert("DOM готово після defer!"));
</script>
<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
<p>...вміст після скриптів...</p>