События загрузки: 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); // оба выполнятся