Выбрать главу

Ниже приведен очень простой пример со множеством элементов сценария:

<!DOCTYPE html>

<html>

<body>

<h1>Example</h1>

<script>

console.log("inline 1");

</script>

<script src="external1.js"></script>

<script>

console.log("inline 2");

</script>

<script src="external2.js"></script>

<script>

console.log("inline 3");

</script>

</body>

</html>

Не важно, содержит ли сценарий встроенный код или ссылается на внешний источник, — все сценарии рассматриваются одинаково и запускаются в том порядке, в котором расположены в документе. В верхнем примере порядок выполнения сценариев будет следующим: inline 1, external 1, inline 2, external 2 и в конце inline 3.

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

Предположим, есть элементы сценария, расположенные внизу страницы чуть выше закрывающего тело элемента:

<!DOCTYPE html>

<html>

<body>

<h1>Example</h1>

<p>

Quisque faucibus, quam sollicitudin pulvinar dignissim, nunc

velit sodales leo, vel vehicula odio lectus vitae mauris. Sed

sed magna augue. Vestibulum tristique cursus orci, accumsan

posuere nunc congue sed. Ut pretium sit amet eros non consectetur.

Quisque tincidunt eleifend justo, quis molestie tellus venenatis

non. Vivamus interdum urna ut augue rhoncus, eu scelerisque

orci dignissim. In commodo purus id purus tempus commodo.

</p>

<button>Click Me</button>

<script src="something.js"></script>

</body>

</html>

Когда выполняется something.js, он может обратиться ко всем элементам DOM, находящимся над ним, вроде h1, p и button. Если ваш элемент сценария расположен в верхней части документа, он не будет знать о других элементах DOM, расположенных ниже него:

<!DOCTYPE html>

<html>

<body>

<script src="something.js"></script>

<h1>Example</h1>

<p>

Quisque faucibus, quam sollicitudin pulvinar dignissim, nunc

velit sodales leo, vel vehicula odio lectus vitae mauris. Sed

sed magna augue. Vestibulum tristique cursus orci, accumsan

posuere nunc congue sed. Ut pretium sit amet eros nonconsectetur.

Quisque tincidunt eleifend justo, quis molestie tellus venenatis

non. Vivamus interdum urna ut augue rhoncus, eu scelerisque

orci dignissim. In commodo purus id purus tempus commodo.

</p>

<button>Click Me</button>

</body>

</html>

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

В этом вся суть. Я большой поклонник размещения элементов сценария в нижней части DOM. Есть и еще одна причина кроме упрощенного доступа к DOM, почему я рекомендую располагать сценарии внизу страницы. Когда элемент сценария считывается, браузер приостанавливает выполнение всего остального на странице на время выполнения его кода. Если речь идет о длительно выполняемом сценарии или внешнем сценарии, требующем время на загрузку, HTML-страница будет попросту заморожена. Если же на этот момент ваша DOM будет считана лишь частично, то страница помимо остановки еще и будет выглядеть незавершенной. А это вряд ли кому-то понравится.

Элементы сценария async и defer

В предыдущем разделе я объяснил, как расположение элементов сценария в DOM определяет время их запуска. Все это относится только к тем элементам, которые я называю простыми. Чтобы стать частью непростого мира, элементы сценария, указывающие на внешние сценарии, могут содержать атрибуты defer и async:

<script async src="myScript.js"></script>

<script defer src="somethingSomethingDarkSide.js"></script>

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

async

Атрибут async позволяет сценарию выполняться асинхронно:

<script async src="someRandomScript.js"></script>

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