$ sudo teach IT
РАЗДЕЛ 9 · УРОК 1

Как работают вместе HTML, CSS и JavaScript

Вы уже умеете писать код на JavaScript — но где он живёт? Как браузер загружает страницу, при чём тут DOM, и почему важно, куда вы ставите тег <script>. Разберём всё по порядку.

⏱ ~20 минут 🌐 Браузер и DOM 🎓 Для новичков

🏗️ Три технологии — три роли

Каждый сайт в интернете построен на трёх технологиях. Они работают вместе, но у каждой — своя чёткая роль. Попробуйте представить строительство дома:

🧱
HTML — структура

HTML — это скелет страницы. Он говорит браузеру: «здесь заголовок, здесь абзац, здесь картинка, здесь кнопка». Это просто разметка — что и где находится. Без HTML нет страницы вообще.

🎨
CSS — стиль

CSS — это внешний вид. Какой цвет у кнопки? Какой шрифт? Какие отступы? Как элементы расположены на странице? CSS превращает голую разметку в красивый интерфейс.

⚡
JavaScript — поведение

JS — это мозг страницы. Нажали кнопку — появилось меню. Ввели текст — проверилась форма. Страница обновилась без перезагрузки. Всё, что «делает» страница в ответ на действия пользователя — это JavaScript.

💬 Аналогия с домом: HTML — это стены и комнаты. CSS — это обои, мебель и освещение. JavaScript — это выключатели, дверные звонки и лифты. Без стен дом не существует. Без обоев — некрасив. Без лифта — неудобен.

Посмотрим на конкретный пример. Вот кнопка в HTML:

<!-- HTML: просто кнопка — она существует, но ничего не делает -->
<button>Нажми меня</button>

Добавляем CSS — кнопка стала красивой:

/* CSS: кнопка теперь выглядит красиво */
button {
  background: #3b82f6;
  color: white;
  padding: 10px 20px;
  border-radius: 8px;
  border: none;
  font-size: 1rem;
  cursor: pointer;
}

Добавляем JavaScript — кнопка ожила:

// JavaScript: кнопка теперь что-то ДЕЛАЕТ
document.querySelector('button').addEventListener('click', function() {
  alert('Привет! Кнопка работает!');
});

Без JavaScript кнопка просто существует и выглядит. С JavaScript — она реагирует. Именно это делает страницу интерактивной.

🔄 Как браузер загружает страницу — шаг за шагом

Когда вы вводите адрес сайта и жмёте Enter, браузер делает много работы прежде чем вы увидите страницу. Понять этот процесс — значит понять, почему важно правильно подключать JavaScript.

1
Получает HTML

Браузер скачивает HTML-файл с сервера. Это сырой текст — набор тегов и содержимого.

2
Строит DOM (Document Object Model)

Браузер разбирает (парсит) HTML и строит из него дерево объектов — DOM. Теперь каждый тег — это не просто текст, а живой объект в памяти, с которым может работать JavaScript.

3
Строит CSSOM (CSS Object Model)

Параллельно браузер скачивает и разбирает CSS-файлы. Получается CSSOM — дерево стилей, которое знает, как должен выглядеть каждый элемент.

4
Объединяет в Render Tree

DOM + CSSOM = Render Tree. Это финальный список всего, что видно на странице, с учётом стилей. Элементы с display: none сюда не попадают.

5
Layout → Paint

Layout (раскладка): браузер вычисляет, где именно на экране находится каждый элемент — координаты, размеры. Paint (отрисовка): пиксели наконец появляются на экране. Страница отображена!

💡 Ключевой момент: JavaScript может прервать этот процесс. Когда браузер встречает тег <script> без специальных атрибутов, он останавливается, скачивает и выполняет скрипт, и только потом продолжает строить DOM. Это называется «блокирующий» скрипт.

📎 Как подключить JavaScript к HTML

Есть несколько способов добавить JavaScript на страницу. Разберём каждый.

Способ 1: Встроенный скрипт

Прямо в HTML, внутри тега <script>:

<!DOCTYPE html>
<html>
  <head>
    <title>Мой сайт</title>
  </head>
  <body>
    <h1>Привет!</h1>

    <!-- Встроенный JS прямо в HTML -->
    <script>
      console.log('Страница загружена!');
    </script>
  </body>
</html>

Подходит для маленьких скриптов, но неудобен — весь код смешивается с HTML. Для реальных проектов так не делают.

Способ 2: Внешний файл

JavaScript пишется в отдельном файле script.js, а в HTML добавляется ссылка:

<!-- Подключение внешнего JS-файла -->
<script src="script.js"></script>

Это правильный подход. Код отдельно — удобно редактировать, кешируется браузером, можно подключить к нескольким страницам.

Куда ставить тег <script>?

Вот где начинается важный выбор. У вас есть три варианта:

❌ В <head> без атрибутов — плохо
<head>
  <script src="script.js"></script> <!-- блокирует загрузку! -->
</head>

Браузер встречает <script>, останавливает парсинг HTML, скачивает и выполняет скрипт. DOM ещё не построен — JavaScript не может найти элементы страницы. Пользователь видит пустую страницу во время загрузки скрипта.

🟡 Перед </body> — работает, но устарело
<body>
  <h1>Контент</h1>
  <!-- ... весь HTML ... -->

  <script src="script.js"></script> <!-- в конце тела -->
</body>

DOM уже построен к моменту выполнения скрипта, элементы доступны. Но браузер начинает скачивать скрипт только после разбора всего HTML — это медленнее чем defer.

✅ В <head> с атрибутом defer — лучший вариант
<head>
  <script src="script.js" defer></script> <!-- правильно! -->
</head>

defer говорит браузеру: «Скачивай файл параллельно с разбором HTML, но выполни его только после того, как DOM полностью построен». Идеальный вариант: скрипт скачивается рано, DOM не блокируется, порядок выполнения сохраняется.

⚖️ defer vs async — в чём разница

У тега <script> есть два «ускоряющих» атрибута. Новички часто путают их.

defer
  • Скрипт скачивается параллельно с HTML
  • Выполняется после полного построения DOM
  • Несколько defer-скриптов выполняются по порядку
  • Подходит для большинства скриптов
<script src="app.js" defer></script>
async
  • Скрипт скачивается параллельно с HTML
  • Выполняется сразу как только скачан (может прервать парсинг)
  • Несколько async-скриптов выполняются в случайном порядке
  • Подходит для независимых скриптов (аналитика, реклама)
<script src="analytics.js" async></script>

✅ Простое правило: для ваших собственных скриптов используйте defer в <head>. Он гарантирует, что DOM готов, и сохраняет порядок выполнения. async — для сторонних независимых скриптов, которые не взаимодействуют с вашим кодом.

Вот итоговая шпаргалка по загрузке скриптов:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title>Мой проект</title>

  <!-- Стили — всегда в head, они не блокируют парсинг HTML -->
  <link rel="stylesheet" href="styles.css">

  <!-- Свой JS — defer в head. Скачается параллельно, выполнится после DOM -->
  <script src="app.js" defer></script>

  <!-- Сторонний независимый скрипт (Google Analytics и т.п.) — async -->
  <script src="analytics.js" async></script>
</head>
<body>
  <h1>Привет, мир!</h1>
  <button id="myBtn">Нажми меня</button>
</body>
</html>

🛠️ Всё вместе: реальный пример

Представим маленький проект: страница с кнопкой, которая меняет заголовок при клике. Вот как всё три технологии работают вместе:

index.html — структура страницы:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="UTF-8">
  <title>Моя страница</title>
  <link rel="stylesheet" href="style.css">
  <script src="app.js" defer></script>
</head>
<body>
  <h1 id="title">Добро пожаловать!</h1>
  <button id="changeBtn">Изменить заголовок</button>
</body>
</html>

style.css — внешний вид:

body {
  font-family: sans-serif;
  padding: 40px;
}

h1 {
  color: #1e40af;
}

button {
  padding: 10px 24px;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 1rem;
}

app.js — поведение:

// Этот код выполнится ПОСЛЕ построения DOM (благодаря defer)
const title = document.getElementById('title');
const btn = document.getElementById('changeBtn');

btn.addEventListener('click', function() {
  title.textContent = 'Заголовок изменился! 🎉';
});

🔑 Важно: если бы мы не использовали defer, а поставили <script> в <head> без атрибутов — JavaScript попытался бы найти #title и #changeBtn до того, как браузер разобрал тело страницы. Элементов ещё нет — код падает с ошибкой. defer решает именно эту проблему.

⚠️ Типичные ошибки новичков

❌ Забыть defer и искать элемент до того, как он есть в DOM
// В head без defer — элемент ещё не создан!
const btn = document.getElementById('myBtn'); // null
btn.addEventListener('click', ...); // TypeError: Cannot read properties of null

Решение: добавьте defer к тегу <script>.

❌ Перепутать имя файла скрипта
<script src="App.js" defer></script> <!-- файл называется app.js -->
<!-- На Linux/Mac — регистр важен! Скрипт не загрузится -->

Решение: проверяйте точное имя файла, особенно регистр букв.

❌ Писать JavaScript прямо в атрибутах HTML
<!-- Устаревший антипаттерн — смешивать HTML и JS -->
<button onclick="alert('клик')">Кнопка</button>

Решение: используйте addEventListener в отдельном JS-файле. Это чище, безопаснее и проще в поддержке.

✅ Итоги урока

  • HTML — структура (что есть на странице), CSS — стиль (как выглядит), JS — поведение (что делает)
  • Браузер строит DOM из HTML, CSSOM из CSS, объединяет в Render Tree и рисует пиксели
  • Тег <script> без атрибутов блокирует построение DOM — это медленно и опасно
  • defer: скрипт скачивается параллельно, выполняется после DOM — лучший вариант для своих скриптов
  • async: выполняется сразу после скачивания, порядок не гарантирован — для независимых сторонних скриптов
  • Правильная структура: CSS в <head>, свои скрипты в <head defer>

В следующем уроке мы разберём DOM изнутри — как JavaScript «видит» страницу и как искать нужные элементы. 🌳

HTML, CSS и JavaScript

7 вопросов

Три технологии веба

// Опишите тремя строками роли трёх технологий веба. // Заполните три переменные строками ответов: // htmlRole = '...' — за что отвечает HTML // cssRole = '...' — за что отвечает CSS // jsRole = '...' — за что отвечает JavaScript let htmlRole = ''; let cssRole = ''; let jsRole = ''; // Ваш код здесь

defer vs async

// Впишите в строки ниже правильные атрибуты: // deferResult = 'defer' или 'async' — для своего главного скрипта // asyncResult = 'defer' или 'async' — для сторонней аналитики let deferResult = ''; let asyncResult = ''; // Ваш код здесь