Вы открываете новый файл HTML язык разметки, определяющий структуру веб‑страницы и задаётесь вопросом: где писать код JavaScript в HTML? Ответ зависит от того, чего вы хотите достичь - быстрой загрузки, чистой структуры или лёгкой поддержки. В этой статье разберём все варианты, их плюсы и минусы, а также дадим практические рекомендации, чтобы ваш код работал быстро и без сюрпризов.
<body>
с атрибутом defer
.<head>
размещайте только скрипты, которые действительно нужны до отрисовки, например, Async асинхронный атрибут, позволяющий загрузить скрипт параллельно с HTML.<script>…</script>
внутри HTML) удобно использовать для небольших одноразовых задач, но они усложняют кеширование.async
и defer
влияют на порядок выполнения - выбирайте их в зависимости от необходимости доступа к DOM.onclick="…"
), это ухудшает читаемость и безопасность.Существует несколько официальных способов включить JavaScript в страницу:
src
тега script элемент HTML, предназначенный для выполнения JavaScript.
<script src="/js/app.js" defer></script>
<script>
без атрибута src
.
<script>
console.log('Привет, мир!');
</script>
<script src="analytics.js" async></script>
<script src="main.js" defer></script>
onclick="..."
.
<button onclick="alert('Клик!')">Нажми</button>
type="module"
для ES‑модулей.
<script type="module" src="/js/module.js"></script>
Выбор зависит от трёх ключевых факторов: производительность, поддерживаемость и требуемый доступ к DOM.
async
и defer
. Поэтому размещайте тяжёлые файлы в конце <body>
с defer
, а лёгкие - в <head>
с async
.defer
или запускайте код после события DOMContentLoaded событие, возникающее после полной загрузки HTML‑документа.
document.addEventListener('DOMContentLoaded', function() {
// безопасно работать с элементами
});
Метод | Плюсы | Минусы | Лучшее применение |
---|---|---|---|
Внешний файл + defer (в конце body) | Кеширование, не блокирует рендеринг, доступ к полной разметке | Требует отдельный HTTP‑запрос (можно объединять через bundler) | Большие проекты, интерактивные страницы |
Внешний файл + async (в head) | Загружается параллельно, быстрый старт аналитики | Выполняется сразу, может обращаться к несуществующим элементам | Трекинговые скрипты, рекламу |
Встроенный скрипт в head | Нет отдельного запроса, удобно для конфигураций | Блокирует парсинг, тяжёлый код ухудшит скорость | Настройки, небольшие скрипты до загрузки UI |
Инлайн‑обработчики | Простота в простых примерах | Трудно поддерживать, плохая безопасность (XSS) | Прототипы, учебные задачи |
type="module" | Поддержка ES‑модулей, импорт/экспорт, строгий режим | Требует современных браузеров, отдельный запрос | Современные приложения, разбитие кода на модули |
Ниже несколько реальных сценариев, которые помогут быстро внедрить нужный способ.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Пример страницы</title>
</head>
<body>
<h1>Hello World</h1>
<!-- Другой контент -->
<script src="/js/main.js" defer></script>
</body>
</html>
Файл main.js
будет выполнен после того, как HTML полностью построен, но до события DOMContentLoaded
, поэтому можно сразу работать с элементами.
<head>
<script src="https://analytics.example.com/track.js" async></script>
</head>
Скрипт загрузится параллельно и выполнится, как только будет готов, не мешая рендерингу.
<script>
window.APP_CONFIG = {apiUrl: '/api/v1'};
</script>
Такой подход удобен, когда нужно передать серверные данные в JS до загрузки основного кода.
<script type="module" src="/js/app.js"></script>
// В файле app.js
import {init} from './init.js';
init();
<!-- init.js -->
export function init() {
console.log('Модуль инициализирован');
}
Модули автоматически работают в строгом режиме и позволяют разбивать проект на части.
defer
.Cannot read property of null
. Добавляйте нужный атрибут.addEventListener
.app.js?v=2025.10
.type="module"
импортировать скрипт, который зависит от другого, нужно убедиться, что путь указан правильно и сервер отдаёт правильный MIME‑type (application/javascript
).Оптимальным считается конец <body>
с атрибутом defer
. Это позволяет браузеру сначала построить страницу, а затем выполнить скрипт, не блокируя рендеринг.
async
загружает скрипт параллельно с HTML и исполняет его сразу после загрузки, независимо от порядка. defer
тоже загружает параллельно, но откладывает выполнение до момента, когда весь DOM построен, сохраняя порядок скриптов.
Технически возможно, но такой подход считается плохой практикой. Он ухудшает читаемость, усложняет тестирование и повышает риск XSS‑уязвимостей. Лучше привязывать обработчики через addEventListener
в отдельном файле.
Для простых задач (например, установка глобальной переменной) можно использовать инлайн‑скрипт. Но если скрипт может вырасти или понадобится кешировать, лучше вынести в отдельный файл сразу.
Подписывайтесь на событие после того, как подключён ваш скрипт (обычно в конце <body>
с defer
). Пример:
document.addEventListener('DOMContentLoaded', function() {
// ваш код
});
Это гарантирует, что все элементы DOM уже существуют.