Далее на странице...
Здесь объяснялось, почему строку вызова js-скрипта следует располагать в конце документа.
А здесь речь шла о том, что также важно, чтобыjs-скрипт выполнялся после загрузки DOM-дерева или даже после загрузки всей веб-страницы. Тем не менее, в случае с большими проектами и длинными HTML-страницами это может создать заметную задержку (скрипт будет ждать построения DOM-дерева и выполнится не сразу). Скорее всего, при высокой скорости Интернета, мы этого и не заметим. Но что если у кого-то медленный Интернет: мобильный или домашний?
Поэтому в JavaScript существует два атрибута тега <script>, которые помогают решить эту проблему: атрибуты defer и async.
Атрибут defer - Загрузка скрипта в фоне
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт - когда DOM дерево будет полностью построено.
1. Скрипты с атрибутом defer никогда не блокируют страницу - она продолжает загружаться, пока скрипт обрабатывается в фоне.
2. Окончательно скрипт выполняется только после того, как DOM-дерево готово. Но до события DOMContentLoaded.
HTML-код
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>defer</title>
</head>
<body>
<script defer src="script.js"></script>
<p>Hello world</p>
<p>Second message</p>
</body>
</html>
JavaScript-код
'use strict';
const p = document.querySelectorAll('p');
console.log(p);
Результат в консоли
NodeList(2) [p, p]
0: p
1: p
length: 2
[[Prototype]]: NodeList
Результат в Браузере
При помощи метода querySelectorAll мы получаем и видим в консоли псевдомассив из абзацев - тегов p. Скрипт выполнился после загрузки html-страницы.
Если же убрать атрибут defer, то в консоли мы увидим пустой псевдомассив.
Результат в консоли
NodeList []
length: 0
[[Prototype]]: NodeList
Скрипт запустился до того, как параграфы сформировались.
Атрибут defer - Порядок выполнения скриптов
Как и обычные, скрипты с атрибутом defer сохраняют порядок выполнения друг относительно друга: то есть последовательно - сначала выполняется первый скрипт, потом 2-ой и т.д.
HTML-код
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>defer - 2 скрипта</title>
</head>
<body>
<script defer src="script.js"></script>
<script defer src="test.js"></script>
<p>Hello world</p>
<p>Second message</p>
</body>
</html>
Результат в Браузере
Как с атрибутом defer, так и без него - сначала выполняется 1-ый скрипт script.js, затем 2-ой - test.js.
Атрибут async - Асинхронные скрипты
Но существует атрибут async, который указывает на абсолютную независимость скрипта.
Если взять код из предыдущего примера и вместо атрибута defer указать атрибут async, то вовсе не обязательно, что сначала будет выполняться 1-ый скрипт.
HTML-код
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>defer - 2 скрипта</title>
</head>
<body>
<script async src="script.js"></script>
<script async src="test.js"></script>
<p>Hello world</p>
<p>Second message</p>
</body>
</html>
Результат в Браузере
Для чего нужен и что дает атрибут async?
1. Скрипты с атрибутом async (также как и с defer) не блокируют страницу - она продолжает загружаться, пока скрипт обрабатывается в фоне.
2. Но, окончательно скрипт может выполниться, как до, так и после того, как DOM-дерево готово. Как до, так и после события DOMContentLoaded.
3. Скрипты с атрибутом async - это асинхронные скрипты - они не ждут друг друга. И, например, скрипт "идущий вторым" - если он меньше, то может выполниться раньше.
Когда используется атрибут async?
При подключении сторонних скриптов, не зависящих от выполнения других скриптов, не зависящих от загрузки DOM-дерева (от формирования DOM-структуры) или веб-страницы в целом.
Разместить скрипт на странице - Создать тег <script>
Еще один способ - как можно поместить скрипт на страницу?
В js-файле можно создать тег <script> и поместить его на страницу. И этот способ иногда используется.
HTML-код
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>тег script</title>
</head>
<body>
<p>Hello world</p>
<p>Second message</p>
<script src="script.js"></script>
</body>
</html>
JavaScript-код
'use strict';
const p = document.querySelectorAll('p');
console.log(p);
/* 1. Создаем тег script
2. Указываем путь к js-файлу
3. Помещаем тег script в конец тега body */
const script = document.createElement('script');
script.src = 'test.js';
document.body.append(script);
Результат в Браузере
Созданные таким способом скрипты ведут себя как async - асинхронные скрипты. Это не всегда нужно/удобно.
Но таких случаях есть возможность отменить асинхронность выполнения скрипта. Для этого, перед тем как поместить тег <script> на страницу - нужно для атрибута async установить значение false: script.async = false;.
JavaScript-код
/* 1. Создаем тег script
2. Указываем путь к js-файлу
3. Отменяем асинхронность выполнения скрипта
4. Помещаем тег script в конец тега body */
const script = document.createElement('script');
script.src = 'test.js';
script.async = false;
document.body.append(script);
Здесь используется метод append - он позволяет вставить элемент в конец какого-либо другого (родительского) элемента.