События загрузки: DOMContentLoaded, load

DOMContentLoaded срабатывает когда HTML разобран и DOM построен (без ожидания картинок и стилей), а load — только после загрузки всех ресурсов страницы.

Зачем нужно

Правильный выбор момента инициализации скрипта критичен: обращение к DOM до его построения даёт null. DOMContentLoaded — стандартная точка входа для кода, работающего с DOM; load нужен только когда требуются размеры изображений или другие ресурсы.

Где используется

  • Инициализация JavaScript-кода, работающего с DOM
  • Запуск логики приложения (SPA bootstrap)
  • Измерение времени загрузки страницы
  • Lazy loading ресурсов после готовности DOM

DOMContentLoaded

Срабатывает на document, когда HTML полностью разобран и DOM построен. Стили, изображения, шрифты — ещё могут грузиться.

document.addEventListener('DOMContentLoaded', () => {
  // DOM готов — можно работать с элементами
  const app = document.getElementById('app');
  initApp(app);
  console.log('DOM готов');
});

Эквивалентно скрипту с атрибутом defer:

<script defer src="app.js"></script>
<!-- выполнится после DOMContentLoaded -->

load (window)

Срабатывает только после загрузки всех ресурсов: изображения, CSS, iframe, шрифты.

window.addEventListener('load', () => {
  // Всё загружено — можно получить реальные размеры изображений
  const img = document.querySelector('.hero-image');
  console.log('Размер изображения:', img.naturalWidth, '×', img.naturalHeight);

  // Замер времени загрузки
  const timing = performance.timing;
  const loadTime = timing.loadEventStart - timing.navigationStart;
  console.log('Страница загружена за:', loadTime, 'мс');
});

load на конкретном элементе

// Изображение
const img = document.createElement('img');
img.addEventListener('load', () => {
  console.log('Картинка загружена');
  document.body.appendChild(img);
});
img.addEventListener('error', () => {
  console.error('Ошибка загрузки картинки');
});
img.src = '/images/photo.jpg';

// Скрипт
const script = document.createElement('script');
script.addEventListener('load', () => console.log('Скрипт загружен'));
script.src = '/libs/chart.js';
document.head.appendChild(script);

beforeunload и unload

// Предупреждение при закрытии вкладки
window.addEventListener('beforeunload', (event) => {
  if (hasUnsavedChanges) {
    event.preventDefault();
    event.returnValue = ''; // Требуется для Chrome
    // Браузер покажет стандартный диалог
  }
});

// Финализация перед уходом
window.addEventListener('unload', () => {
  // Только синхронный, быстрый код!
  // fetch не сработает — страница уже закрывается
  navigator.sendBeacon('/analytics', JSON.stringify({ action: 'leave' }));
});

Временная шкала событий

Браузер получает HTML
        ↓
   Парсинг HTML → построение DOM
        ↓
   DOMContentLoaded  ← идеальный момент для работы с DOM
        ↓
   Загрузка ресурсов (img, css, fonts, iframes...)
        ↓
   load  ← если нужны реальные размеры ресурсов

Проверка состояния документа

// document.readyState:
// 'loading'     — HTML ещё парсится
// 'interactive' — DOM построен (аналог DOMContentLoaded)
// 'complete'    — всё загружено (аналог load)

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', init);
} else {
  // DOM уже готов (скрипт выполняется позже)
  init;
}

// Удобная обёртка
function onReady(fn) {
  if (document.readyState !== 'loading') {
    fn;
  } else {
    document.addEventListener('DOMContentLoaded', fn, { once: true });
  }
}

onReady(() => {
  console.log('Готов!');
});

Частые ошибки

1. Работа с DOM до его построения

// Скрипт в <head> без defer/async
const btn = document.getElementById('btn'); // null!
btn.addEventListener('click', ...);         // TypeError

// Решение 1: перенести скрипт перед </body>
// Решение 2: использовать DOMContentLoaded
// Решение 3: атрибут defer

2. Использование window.onload вместо addEventListener

// onload — только один обработчик, второй перезапишет первый
window.onload = initApp;
window.onload = initAnalytics; // initApp не выполнится!

// Правильно:
window.addEventListener('load', initApp);
window.addEventListener('load', initAnalytics); // оба выполнятся

Связанные темы

Ресурсы