document ready js без jQuery и события жизненного цикла страницы
Для запуска кода после готовности DOM (document ready js) используйте DOMContentLoaded и проверку document.readyState. Для размеров изображений нужен load, а при уходе пригодятся pagehide и navigator.sendBeacon. Вы узнаете, как заменить $(document).ready, выбрать async, defer или type="module" и предупредить о несохраненной форме.
- Жизненный цикл HTML-страницы
- document ready js: запуск кода после готовности DOM
- DOMContentLoaded и порядок выполнения скриптов
- async, defer и type="module": когда выполняются скрипты
- DOMContentLoaded и таблицы стилей
- Ошибки при инициализации скриптов
- window onload js: как дождаться загрузки изображений
- window.onbeforeunload: предупреждение о несохраненных изменениях
- pagehide, visibilitychange и отправка данных
- Локальная проверка sendBeacon на PHP
- pageshow, pagehide и возврат через bfcache
- readyState и последовательность событий
- События страницы: что выбрать для задачи
Жизненный цикл HTML-страницы
Браузер проходит несколько состояний от чтения HTML до скрытия страницы. У каждого события своя задача, поэтому один обработчик не стоит использовать для всего.
- DOMContentLoaded. Объект document сообщает, что DOM построен и отложенные скрипты выполнены.
- load. Объект window сообщает, что загрузились ресурсы, которые задерживают событие.
- beforeunload. Объект window позволяет запросить предупреждение о несохраненных изменениях.
- pagehide. Объект window сообщает, что страница покидает экран, в том числе перед сохранением в bfcache.
- visibilitychange. Объект document сообщает об изменении видимости вкладки.
Для инициализации формы выбирайте DOMContentLoaded. Для размеров картинок ждите load у изображения или window, а небольшие данные отправляйте через pagehide и sendBeacon.
document ready js: запуск кода после готовности DOM
Запрос document ready js обычно означает запуск функции после построения DOM. В jQuery эту задачу решал $(document).ready, но чистый JavaScript закрывает ее без библиотеки.
Поместите кнопку и подключение файла в HTML:
<button id="save" type="button">Сохранить</button>
<script src="app.js" defer></script>В app.js добавьте проверку readyState и обработчик кнопки:
function init() {
const button = document.getElementById('save');
button.addEventListener('click', () => {
console.log('Форма готова к отправке');
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, { once: true });
} else {
init();
}При состоянии loading код ждет DOMContentLoaded. При состоянии interactive или complete функция запускается сразу, поэтому шаблон подходит и для поздно загруженного файла.
Так работает и javascript document ready без jQuery. После нажатия кнопки консоль покажет сообщение «Форма готова к отправке».
DOMContentLoaded и порядок выполнения скриптов
Обычный внешний script останавливает разбор HTML в месте подключения. Браузер загружает файл, выполняет его и продолжает строить DOM.
<script src="app.js"></script>DOMContentLoaded ждет обычные скрипты и скрипты с defer. Независимый async-файл событие не задерживает: браузер запускает его сразу после загрузки, не соблюдая общий порядок.
Скрипт в head может обратиться к элементу, которого браузер еще не встретил. Вызов document.getElementById тогда вернет null. Поставьте defer или перенесите подключение обычного файла перед закрывающим body.
Скрипт может подписаться на DOMContentLoaded слишком поздно. Если событие уже прошло, обработчик не сработает. Проверка document.readyState из предыдущего примера закрывает оба сценария.
async, defer и type="module": когда выполняются скрипты
Эти варианты позволяют загружать внешний файл параллельно с разбором HTML. Отличия появляются в моменте запуска, порядке файлов и связи с DOMContentLoaded.
| Вариант | Порядок выполнения | Связь с DOMContentLoaded | Подходит для |
|---|---|---|---|
| Обычный script | В месте подключения после загрузки файла | Событие ждет выполнение | Кода, которому нужен текущий фрагмент HTML |
| async | Сразу после загрузки файла, порядок между файлами не гарантирован | Событие не ждет такой скрипт | Независимой аналитики |
| defer | После разбора HTML, в порядке подключения | Событие ждет выполнение defer-файлов | Основного кода интерфейса |
| type="module" | Отложенно после разбора HTML, без async | Событие ждет выполнение модуля | Кода с import и export |
Берите async для полностью самостоятельного файла. Используйте defer, если скрипты должны сохранить порядок. Модуль подключайте так:
<script type="module" src="app.js"></script>Модуль без async по умолчанию ведет себя похоже на defer. Он запускается после разбора документа, а DOMContentLoaded ждет завершения модуля.
Правила атрибутов описывает документация MDN по элементу script.
DOMContentLoaded и таблицы стилей
DOMContentLoaded не ждет загрузку CSS напрямую. Однако блокирующая таблица стилей может задержать выполнение скрипта после link.
<link rel="stylesheet" href="style.css">
<script>
// Скрипт ждет загрузки таблицы стилей.
const margin = getComputedStyle(document.body).marginTop;
console.log(margin);
</script>Браузер выполняет такой скрипт после загрузки блокирующей таблицы стилей. Поэтому CSS может косвенно отложить DOMContentLoaded через ожидающий скрипт.
Ошибки при инициализации скриптов
Проблема возникает, когда код запускается раньше нужного ресурса или позже события. Три случая встречаются чаще остальных.
Скрипт в head получает null
Обычный script в head выполняется до чтения body. Вызов document.getElementById('menu') вернет null, если элемент menu еще не попал в DOM.
Добавьте defer внешнему файлу или перенесите обычный script перед закрывающим body. После исправления обработчик найдет элемент.
Обработчик добавлен после события
Async-файл может выполниться после DOMContentLoaded. Новый обработчик уже не сработает, если код подписался на прошедшее событие без проверки readyState.
При loading дождитесь DOMContentLoaded, а при interactive или complete вызовите init сразу. Так один шаблон работает при разных скоростях загрузки.
Картинка имеет нулевой размер
DOMContentLoaded не обязан ждать изображения. В этот момент offsetWidth и offsetHeight могут быть равны нулю, если картинка еще не загрузилась.
Для естественных размеров используйте load у изображения или window load. Задайте ширину и высоту в HTML либо CSS, чтобы верстка не ждала расчета размеров.
window onload js: как дождаться загрузки изображений
Событие load срабатывает на window после загрузки страницы и ресурсов, которые задерживают событие. Поэтому запрос window onload js или window onload javascript относится к обработчику window.onload.
Событие загрузки всей страницы приходит на window. document.onload для этой задачи не сработает.
Сохраните рядом с HTML изображение под именем preview.jpg. Создайте HTML с картинкой и подпишитесь на load:
<img id="preview" src="preview.jpg" alt="Изображение для проверки размеров">
<script>
window.addEventListener('load', () => {
const image = document.getElementById('preview');
console.log(`${image.naturalWidth}x${image.naturalHeight}`);
});
</script>После загрузки консоль покажет исходную ширину и высоту изображения. Если значения равны нулю, проверьте путь и сам файл: window load не гарантирует успешную загрузку каждой картинки.
Если нужна одна картинка, подпишитесь на ее собственное событие:
const image = document.createElement('img');
image.alt = 'Изображение для проверки размеров';
image.addEventListener('load', () => {
console.log(image.naturalWidth, image.naturalHeight);
});
image.addEventListener('error', () => {
console.log('Изображение не загрузилось');
});
image.src = 'preview.jpg';
document.body.append(image);Ставьте обработчик до присваивания src. Тогда код поймает событие и для изображения, которое браузер быстро берет из кэша.
Запрос js document onload часто означает ожидание всей страницы. Для этого добавьте обработчик load на window через addEventListener.
window.onbeforeunload: предупреждение о несохраненных изменениях
Используйте beforeunload только при реальных несохраненных данных. Современные браузеры игнорируют ваш текст и показывают стандартный диалог.
Сохраните пример как form.html и откройте файл в браузере. Скрипт расположен после формы, поэтому ее элементы уже доступны:
<form id="profile-form">
<label>
О себе
<textarea name="about"></textarea>
</label>
</form>
<script>
let hasUnsavedChanges = false;
const form = document.getElementById('profile-form');
form.addEventListener('input', () => {
hasUnsavedChanges = true;
});
window.addEventListener('beforeunload', (event) => {
if (!hasUnsavedChanges) {
return;
}
event.preventDefault();
event.returnValue = true;
});
</script>Введите текст в поле и попробуйте перезагрузить страницу. Браузер покажет предупреждение; отмените уход, чтобы сохранить введенный текст.
event.preventDefault() запрашивает подтверждение ухода. Свойство event.returnValue сохраняет совместимость со старыми браузерами.
Все современные браузеры игнорируют собственный текст сообщения. Диалог появляется только после взаимодействия пользователя со страницей.
После успешного сохранения присвойте hasUnsavedChanges значение false, чтобы предупреждение не мешало обычному переходу.
MDN описывает ограничения события beforeunload, включая условие пользовательского взаимодействия.
pagehide, visibilitychange и отправка данных
Не используйте unload для важной логики ухода. Браузер может не вызвать обработчик при завершении процесса, а сам обработчик мешает восстановлению страницы из bfcache.
Для перехода со страницы подходит pagehide. Для сохранения черновика при скрытии вкладки подходит visibilitychange со значением hidden. Скрытие вкладки само по себе не означает завершение посещения.
Небольшие данные отправляйте через navigator.sendBeacon. Метод ставит POST-запрос в очередь и не заставляет страницу ждать ответ.
function sendVisitEnd() {
const data = JSON.stringify({
page: location.pathname,
endedAt: Date.now()
});
navigator.sendBeacon(
'/visit-end.php',
new Blob([data], { type: 'application/json' })
);
}
window.addEventListener('pagehide', sendVisitEnd);
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
saveDraft();
}
});Возвращаемое значение sendBeacon true означает постановку запроса в очередь, но не подтверждает получение сервером. Критичные данные сохраняйте заранее: завершение процесса браузера может прервать любой обработчик ухода.
Локальная проверка sendBeacon на PHP
Для локального теста нужны страница index.html и принимающий файл visit-end.php. Сохраните их в одной папке.
Страница и обработчик
В index.html добавьте ссылку и код отправки:
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Проверка sendBeacon</title>
</head>
<body>
<a href="/?next=1">Перейти на другую страницу</a>
<script>
function sendVisitEnd() {
const data = JSON.stringify({
page: location.pathname,
endedAt: Date.now()
});
navigator.sendBeacon(
'/visit-end.php',
new Blob([data], { type: 'application/json' })
);
}
window.addEventListener('pagehide', sendVisitEnd);
</script>
</body>
</html>В visit-end.php примите POST-запрос и запишите данные в журнал:
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
header('Allow: POST');
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
if (!is_array($data)
|| !isset($data['page'], $data['endedAt'])
|| !is_string($data['page'])
|| !is_numeric($data['endedAt'])) {
http_response_code(400);
exit;
}
error_log('visit-end: ' . json_encode([
'page' => $data['page'],
'endedAt' => $data['endedAt']
]));
http_response_code(204);Адрес /visit-end.php в sendBeacon соответствует файлу в корне сервера.
Запуск встроенного сервера
Откройте терминал в папке с файлами и выполните php -v. Если команда выводит сведения об интерпретаторе, запустите сервер:
php -d error_log= -S 127.0.0.1:8000Введите в браузере http://127.0.0.1:8000/. Страница покажет ссылку «Перейти на другую страницу».
- Нажмите ссылку на странице. Адрес изменится на
http://127.0.0.1:8000/?next=1. - Посмотрите терминал. В нем появится строка
visit-end:с полями page и endedAt.
Если строки нет, проверьте наличие обоих файлов в папке сервера. Ошибка 404 для visit-end.php означает неверное расположение файла; ответ 400 означает, что обработчик отверг тело запроса.
Не открывайте index.html двойным щелчком: адрес file:// не запускает PHP. Для остановки локального сервера нажмите Ctrl+C в его терминале. Встроенный сервер PHP используйте только для разработки.
pageshow, pagehide и возврат через bfcache
Кнопка «Назад» может вернуть страницу из bfcache без нового разбора HTML. При таком возврате DOMContentLoaded и load повторно не срабатывают.
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
console.log('Страница восстановлена из bfcache');
}
});
window.addEventListener('pagehide', (event) => {
console.log('Страница покидает экран:', event.persisted);
});Используйте pageshow с event.persisted, когда данные нужно обновить после возврата. Значение true у pagehide означает, что браузер помещает страницу в bfcache.
readyState и последовательность событий
Свойство document.readyState дает 3 состояния. Они помогают выбрать момент запуска, когда способ подключения файла заранее неизвестен.
- loading. Браузер еще читает HTML.
- interactive. DOM построен, но ресурсы могут загружаться.
- complete. Документ и ресурсы, задерживающие load, загрузились.
function log(message) {
console.log(`${performance.now().toFixed(1)} ms: ${message}`);
}
log(`initial: ${document.readyState}`);
document.addEventListener('readystatechange', () => {
log(`readyState: ${document.readyState}`);
});
document.addEventListener('DOMContentLoaded', () => {
log('DOMContentLoaded');
});
window.addEventListener('load', () => {
log('window load');
});Подключите этот код обычным скриптом в head. Консоль покажет состояния и время с начала загрузки страницы.
Браузер переходит из loading в interactive после разбора HTML. Затем он выполняет отложенные скрипты и отправляет DOMContentLoaded. После ресурсов состояние меняется на complete, затем window получает load.
События страницы: что выбрать для задачи
Выбирайте событие по объекту и моменту запуска. Таблица помогает сопоставить задачу с подходящим обработчиком.
| Событие | Объект | Когда срабатывает | Для чего использовать |
|---|---|---|---|
| DOMContentLoaded | document | DOM построен, обычные отложенные скрипты выполнены | Инициализации элементов |
| load | window | Загрузились страница и ресурсы, задерживающие событие | Измерения загруженных изображений |
| beforeunload | window | Перед уходом со страницы | Предупреждения о несохраненных данных |
| pagehide | window | Страница покидает экран | Сохранения состояния и sendBeacon |
| pageshow | window | Страница открывается или восстанавливается | Обновления после bfcache |
| visibilitychange | document | Меняется видимость вкладки | Сохранения черновика при скрытии |
Для javascript document ready выбирайте DOMContentLoaded или проверку readyState. Для изображения используйте его load, для всей страницы – window load. Перед уходом предупреждайте только о реальных изменениях, а данные отправляйте через pagehide и navigator.sendBeacon.
Совет от нашей редакции: начинайте инициализацию с DOMContentLoaded, а load оставляйте для задач, которым нужны размеры и другие загруженные ресурсы.
Популярное за неделю
- Как создать сайт самому в 2026: 4 способа + пошаговая инструкция 34 мин чтения
- Лучшие конструкторы для создания сайтов — плюсы, минусы и сравнения 22 мин чтения
- Скрипт сокращения URL на PHP с INI 3 мин чтения
- jquery select onchange в jQuery: как получить значение select 6 мин чтения
- Как создать фотогалерею для сайта 5 мин чтения
- Валидация формы js для регистрации с проверкой полей 5 мин чтения
Комментарии
Ошибка:
"Скрипты с атрибутом defer выполняются после того, как документ будет полностью загружен и обработан (если необходимо они ожидают завершения процесса), сразу после события DOMContentLoaded."
Скрипты вызываются перед DOMContentLoaded.