Способы подключения JavaScript к HTML-странице

Раздел: Веб-разработка -> HTML/CSS/JS связь

При создании веб-страниц требуется подключать скрипты на JavaScript. Существует несколько способов, каждый со своими особенностями. Наиболее действенный метод и распространённые варианты описаны ниже.

Основной способ подключения внешнего JavaScript

Самый надёжный подход - использовать тег <script> с атрибутом src, располагая его перед закрывающим тегом </body> либо с атрибутом defer внутри <head>. Это предотвращает блокировку рендеринга страницы и гарантирует готовность DOM к моменту выполнения.

<!DOCTYPE html>
<html>
<head>
  <title>Пример</title>
</head>
<body>
  <!-- содержимое страницы -->
  <script src="script.js" defer></script>
</body>
</html>

Link href js html (подключение javascript файлов в html)

Пояснение: атрибут defer заставляет браузер загружать скрипт параллельно с разбором HTML, но выполнять его только после полного построения DOM. Это улучшает восприятие скорости загрузки.

Типичные ошибки:

  • Неверный путь к файлу - проверьте относительные адреса и папки.
  • Случайное применение async вместо defer, когда важен порядок выполнения скриптов.
  • Размещение тяжёлого скрипта в <head> без атрибутов задержки - это замедляет отрисовку страницы.

Решение: для большинства случаев стоит использовать defer в <head> или помещать script перед </body>.

Как загрузить скрипт, который должен выполниться до начала построения DOM?

Размещение скрипта в <head> без атрибутов defer или async заставляет браузер приостановить парсинг HTML до полной загрузки и выполнения скрипта. Используется крайне редко - обычно для полифиллов или кода аналитики, который обязан работать до рендеринга.

<head>
  <script src="polyfill.js"></script>
</head>

Js теги (теги script в html)

Проблема: страница не отображается до загрузки скрипта.

Решение: применять только для маленьких и критически важных скриптов.

Как гарантировать, что DOM уже построен, при простом подключении скрипта?

Размещение тега script перед закрывающим </body> без дополнительных атрибутов - классический метод. Весь HTML уже распарсен, поэтому скрипт имеет доступ ко всем элементам.

<body>
  <div id="app"></div>
  <script src="app.js"></script>
</body>

Ошибка: скрипт по-прежнему блокирует загрузку, если он расположен не в конце - например, в середине <body>.

Решение: всегда помещать в самый конец body.

Как загрузить независимый скрипт, который не зависит от DOM и других скриптов?

Атрибут async даёт команду браузеру загружать скрипт параллельно и выполнять его сразу после загрузки, не дожидаясь завершения парсинга HTML. Порядок выполнения таких скриптов не гарантируется.

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

Проблема: если один async-скрипт зависит от другого, может возникнуть ошибка.

Решение: использовать async только для полностью автономных скриптов (счётчики, трекеры).

Как загрузить скрипт после определённого действия пользователя, например клика?

Динамическое создание элемента script через JavaScript позволяет подгружать код в любой момент. Это часто применяется для ленивой загрузки виджетов или дополнительных библиотек.

document.getElementById('loadBtn').addEventListener('click', function() {
  var script = document.createElement('script');
  script.src = 'widget.js';
  document.body.appendChild(script);
});

Ошибка: скрипт может загрузиться несколько раз, если не проверять его наличие.

Решение: добавить проверку или использовать флаг.

Как использовать современные модули JavaScript с импортом внутри?

Тег script с атрибутом type="module" включает режим ES-модуля. Такой скрипт по умолчанию ведёт себя как defer, поддерживает import и export.

<script type="module" src="main.mjs"></script>

Проблема: модули требуют CORS для загрузки с других доменов, а старые браузеры не поддерживаются.

Решение: использовать транспиляцию (Babel) для обратной совместимости.

Как загрузить скрипт заранее, до его указания в теге script?

Элемент <link rel="preload" as="script"> сообщает браузеру о необходимости скачать скрипт ещё на этапе предзагрузки. После парсинга тег script с тем же src уже использует кеш.

<head>
  <link rel="preload" href="critical.js" as="script">
</head>
<body>
  <script src="critical.js" defer></script>
</body>

Ошибка: если предзагружен не тот ресурс или забыт тег script, скрипт не выполнится.

Решение: всегда дублировать тегом script.

Как предзагрузить модуль?

Специальный <link rel="modulepreload"> работает аналогично preload, но для модулей. Браузер загружает и парсит модуль заранее.

<link rel="modulepreload" href="module.mjs">
<script type="module" src="module.mjs"></script>

Проблема: поддержка в старых браузерах отсутствует.

Решение: применять только для современных браузеров или использовать полифиллы.

Расширенные примеры использования

Динамическая загрузка с обработкой ошибок и колбэком

Пример загрузки скрипта по требованию с проверкой успеха и вызовом функции после загрузки.

Пример
function loadScript(src, callback) {
  var script = document.createElement('script');
  script.src = src;
  script.onload = function() {
    if (callback) callback(null, 'Скрипт загружен');
  };
  script.onerror = function() {
    if (callback) callback(new Error('Ошибка загрузки ' + src));
  };
  document.head.appendChild(script);
}

loadScript('https://example.com/widget.js', function(err, msg) {
  if (err) console.error(err);
  else console.log(msg);
});
В консоль будет выведено: "Скрипт загружен" (если файл доступен) или сообщение об ошибке.

Возможная ошибка: двойная загрузка одного и того же скрипта.

Добавьте проверку: if (document.querySelector('script[src="' + src + '"]')) return;

Динамический импорт модуля через import()

Современный способ загрузить модуль только при необходимости, используя Promise.

Пример
document.getElementById('showChart').addEventListener('click', async () => {
  try {
    const chartModule = await import('./chart.mjs');
    chartModule.drawChart();
  } catch (err) {
    console.error('Модуль не загружен:', err);
  }
});
После клика в консоль может выводиться результат работы drawChart или сообщение об ошибке.

Проблема: модуль должен поддерживать CORS, если расположен на другом домене.

Решение: размещать модули на том же сервере или настраивать CORS.

Предзагрузка скрипта через link preload с отложенным выполнением

Комбинация preload и defer позволяет начать загрузку критического скрипта раньше, чем парсер встретит тег script.

Пример
<head>
  <link rel="preload" href="app.js" as="script">
</head>
<body>
  <script src="app.js" defer></script>
</body>
Браузер загрузит app.js параллельно с HTML, но выполнит только после построения DOM.

Ошибка: если preload не совпадает по src с script, ресурс будет загружен дважды.

Решение: всегда указывать одинаковые атрибуты src и href.

Использование модуля с внешнего CDN и fallback на локальную копию

Подключение библиотеки через модуль с проверкой загрузки и подменой при ошибке.

Пример
<script type="module">
  let lib;
  try {
    lib = await import('https://cdn.example.com/utils.mjs');
  } catch {
    lib = await import('./local-utils.mjs');
  }
  lib.init();
</script>
Если CDN недоступен, загрузится локальный модуль. Функция init() выполнится в любом случае.

Проблема: в некоторых старых браузерах import() не поддерживается.

Решение: использовать полифилл или традиционный скрипт.

Подключение JavaScript файлов в HTML - comments

En
Link href js html (javascript)