Вы пишете первый сайт. Добавили кнопку, заголовок, картинку - всё выглядит нормально. Но когда пытаетесь сделать кнопку рабочей - ничего не происходит. Почему? Потому что не знаете, где писать код JavaScript в HTML. Это базовый вопрос, который ставит в тупик даже тех, кто уже умеет писать простые скрипты. Ответ не такой простой, как кажется. Место, где вы вставляете JavaScript, влияет на скорость загрузки, поведение страницы и даже на то, будет ли ваш код работать вообще.
Три основных способа добавить JavaScript в HTML
Всё просто: есть три места, куда можно вставить JavaScript - и каждое из них работает по-своему. Выбирайте не наугад, а понимая, что происходит под капотом.
- Внутри
<head>- до содержимого страницы - Внутри
<body>- перед закрывающим тегом</body> - В отдельном файле с расширением
.js- подключаемый через<script src="...">
Первые два способа - это встраивание кода прямо в HTML-файл. Третий - внешний файл. Каждый имеет свои плюсы и минусы.
Почему <head> - не лучшее место для JavaScript
Многие учатся писать JavaScript в <head>, потому что так делали в старых учебниках. Но это устаревшая практика. Когда браузер встречает тег <script> внутри <head>, он останавливает загрузку HTML. Он ждёт, пока скрипт загрузится, выполнится и закончит работу. Даже если ваш скрипт всего в пять строк - браузер не покажет пользователю ни одного элемента страницы, пока не дождётся его завершения.
Представьте: пользователь заходит на сайт. Видит белый экран. Ждёт три секунды. Потом - внезапно - появляется заголовок, кнопка, изображение. Он уходит. Потому что сайт кажется медленным. А вы думаете: «Но код-то работает!»
Такой подход оправдан только если:
- Ваш скрипт критически важен для отображения страницы (например, загружает шрифты или настраивает CSS до рендера)
- Вы используете атрибут
deferилиasync
Без этих атрибутов - избегайте JavaScript в <head>.
Лучшее место: перед закрывающим </body>
Это стандарт, который используют 87% современных сайтов по данным Web Almanac 2024. Почему? Потому что браузер сначала загружает и отображает весь HTML-контент. Пользователь видит страницу. А уже потом - когда всё визуально готово - браузер начинает выполнять JavaScript.
Пример правильной структуры:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Мой сайт</title>
</head>
<body>
<button id="clickMe">Нажми меня</button>
<p>Это текст перед скриптом.</p>
<script>
document.getElementById('clickMe').addEventListener('click', function() {
alert('Вы нажали кнопку!');
});
</script>
</body>
</html>
Здесь скрипт выполняется после того, как кнопка уже существует в DOM. Нет ошибок вроде «Cannot read property 'addEventListener' of null». Нет задержек в отображении. Пользователь не ждёт.
Это особенно важно, если вы используете библиотеки вроде jQuery или React - они работают только когда DOM готов. Помещая скрипт в конец body, вы избегаете большинства проблем с инициализацией.
Когда использовать отдельный файл .js
Если вы пишете больше 10-15 строк кода - вы уже должны выносить его в отдельный файл. Почему?
- Чистота кода: HTML - это структура, JS - это поведение. Разделяйте их.
- Кэширование: браузер закэширует файл script.js. При переходе на другую страницу сайта он не будет загружать JavaScript заново.
- Поддержка: легче искать ошибки, тестировать, обновлять.
Подключите файл так:
<script src="script.js"></script>
И поместите его тоже перед закрывающим </body>. Даже если файл внешний - он всё равно блокирует рендеринг, если стоит в <head>.
Если вы используете современные инструменты (Vite, Webpack, Parcel), они автоматически вставляют скрипты в конец body. Но если вы пишете вручную - не забывайте про это правило.
Что делать, если скрипт должен запуститься сразу?
Допустим, вы пишете скрипт, который меняет цвет фона при загрузке страницы. Или проверяет, поддерживает ли браузер WebP. Вы хотите, чтобы он выполнился до того, как пользователь что-то увидит.
В таком случае используйте атрибут defer:
<script src="init.js" defer></script>
Он загружает скрипт параллельно с HTML, но выполняет его после полной загрузки DOM, но до события DOMContentLoaded. Это как «выполнить сразу после того, как страница готова, но до того, как пользователь начнёт с ней взаимодействовать».
Атрибут async - для скриптов, которые не зависят от DOM: аналитика, реклама, внешние библиотеки вроде Google Analytics. Они загружаются и выполняются независимо, без ожидания других ресурсов.
Что не работает: JavaScript в тегах элементов
Вы когда-нибудь видели такое?
<button onclick="alert('Привет!')">Кнопка</button>
Такой код работает. Но он - плохая практика. Почему?
- Смешивается структура и поведение - нарушает принцип разделения ответственности.
- Сложно поддерживать: если у вас 20 кнопок с одинаковым поведением - вы копируете код 20 раз.
- Нельзя отладить: в консоли браузера вы не увидите, где именно ошибка.
- Не работает с CSP (Content Security Policy) - современный стандарт безопасности, который блокирует inline-скрипты.
Вместо этого используйте addEventListener:
<button id="myButton">Кнопка</button>
<script>
document.getElementById('myButton').addEventListener('click', function() {
alert('Привет!');
});
</script>
Это чище, масштабируемо и безопасно.
Проверьте, работает ли ваш скрипт
Если скрипт не работает - не вините браузер. Проверьте три вещи:
- Правильно ли вы разместили тег
<script>? - Он должен быть после элементов, которые он использует. - Есть ли ошибки в консоли? - Откройте DevTools (F12), перейдите во вкладку Console. Там будет красное сообщение - прочитайте его.
- Существует ли элемент в DOM? - В консоли введите:
document.getElementById('ваш-id'). Если вернётnull- элемент ещё не загружен.
Если вы видите ошибку Cannot read property 'addEventListener' of null - это почти всегда значит, что скрипт запустился до того, как элемент появился на странице.
Что делать, если вы используете фреймворки?
React, Vue, Angular - они сами управляют DOM. Вы не пишете скрипты в HTML вручную. Но даже в них есть правила:
- В React - код пишется в JS-файлах, а не в JSX как inline-скрипты.
- В Vue - скрипты в теге
<script>внутри .vue-файлов. - В Angular - всё в компонентах TypeScript.
Но базовый принцип остаётся: не смешивайте логику с разметкой. Даже если фреймворк скрывает HTML - вы всё равно должны следовать правилу: «Сначала DOM, потом скрипт».
Итог: где писать JavaScript в HTML
- Для простых скриптов (до 20 строк): размещайте перед закрывающим
</body>внутри<script>. - Для сложных проектов: выносите код в отдельный файл
.jsи подключайте перед</body>. - Для скриптов, которые должны запуститься до рендера: используйте
deferв<head>. - Никогда не пишите JavaScript в атрибутах HTML -
onclick,onloadи т.д. - это устаревший подход. - Всегда проверяйте консоль браузера - там вы найдёте 90% ошибок.
Правильное место для JavaScript - не вопрос вкуса. Это вопрос производительности, надёжности и профессионализма. Правильно разместите скрипт - и вы избежите кучи проблем, с которыми сталкиваются новички. А когда вы будете писать сложные сайты - это станет второй натурой.
Можно ли писать JavaScript в теге <head>?
Можно, но только с атрибутами defer или async. Без них скрипт блокирует загрузку страницы, и пользователь видит белый экран до тех пор, пока скрипт не выполнится. Для большинства случаев лучше ставить скрипты перед закрывающим тегом </body>.
Почему мой скрипт не работает, если он в <head>?
Потому что когда браузер доходит до вашего скрипта, он ещё не успел создать элементы DOM. Если вы пытаетесь найти кнопку по ID, а её ещё нет - JavaScript возвращает null. Решение: переместите скрипт в конец body или используйте событие DOMContentLoaded.
Что такое defer и async?
defer - загружает скрипт параллельно с HTML, но выполняет его после полной загрузки DOM. async - загружает и выполняет скрипт сразу, как только он скачается, без ожидания других ресурсов. defer - для скриптов, которые зависят от DOM. async - для аналитики, рекламы, внешних библиотек.
Нужно ли выносить JavaScript в отдельный файл?
Если код больше 10-15 строк - да. Это упрощает поддержку, позволяет кэшировать файл, делает код чище и легче тестировать. Даже если сайт один - разделяйте структуру (HTML), стили (CSS) и поведение (JS).
Почему не стоит использовать onclick в HTML?
Потому что это смешивает разметку и логику, усложняет поддержку, не работает с современными политиками безопасности (CSP) и не масштабируется. Лучше использовать addEventListener в отдельном скрипте - так код чище, безопаснее и проще отлаживать.