Способы подключения JavaScript к HTML-странице
При создании веб-страниц требуется подключать скрипты на 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() не поддерживается.
Решение: использовать полифилл или традиционный скрипт.