W3docs

События жизненного цикла страницы в JavaScript

События жизненного цикла страницы: DOMContentLoaded, load, beforeunload, unload — когда срабатывают и как использовать.

Каждая страница, открываемая браузером, проходит через предсказуемую последовательность этапов: HTML разбирается, загружаются внешние ресурсы, пользователь взаимодействует со страницей, и в конечном счёте она закрывается. JavaScript предоставляет доступ к этой последовательности через четыре события жизненного цикла страницыDOMContentLoaded, load, beforeunload и unload. Понимание того, когда именно срабатывает каждое из них, позволяет запускать код инициализации в самый ранний безопасный момент, откладывать ресурсоёмкие операции до полной готовности страницы и защищать пользователей от потери несохранённых данных.

В этом руководстве рассматривается назначение каждого события, порядок их срабатывания, типичные ловушки и современные альтернативы для событий, которые браузеры теперь не рекомендуют использовать. Все примеры ниже можно запустить — вставьте их в HTML-файл или откройте во встроенном редакторе.

События жизненного цикла: обзор

События срабатывают в фиксированном порядке. Первые два происходят при загрузке страницы; последние два — когда пользователь уходит:

СобытиеЦельКогда срабатываетТипичное применение
DOMContentLoadeddocumentHTML полностью разобран; скрипты выполнены; изображения/стили могут ещё загружатьсяИнициализация UI, подключение обработчиков, работа с DOM
loadwindowСтраница и все подресурсы (изображения, стили, iframe) загруженыЧтение размеров изображений, код, требующий полного присутствия ресурсов
beforeunloadwindowПользователь собирается покинуть страницуПредупреждение о несохранённых изменениях
unloadwindowСтраница закрывается (устаревшее)Устаревшая очистка / аналитика

Главный вывод: DOMContentLoaded — это событие, которое нужно в большинстве случаев. Оно срабатывает значительно раньше load, поэтому привязка кода к нему делает страницу интерактивной быстрее.

Подробный разбор события DOMContentLoaded

Событие DOMContentLoaded срабатывает сразу после того, как HTML-документ полностью разобран, — обычно задолго до того, как загрузятся изображения, стили и другие внешние ресурсы. В этот момент дерево DOM уже построено, поэтому document.getElementById, querySelector и другие методы найдут все элементы, описанные в HTML.

Поскольку событие не ждёт загрузки изображений и CSS, используйте DOMContentLoaded всякий раз, когда скрипту нужна только структура DOM — а это подавляющее большинство случаев.

Как скрипты влияют на DOMContentLoaded

Браузер приостанавливает разбор HTML при встрече обычного тега <script>, выполняет скрипт и лишь затем продолжает. Это означает, что DOMContentLoaded ожидает синхронные скрипты. Есть два важных нюанса:

  • <script async> выполняется сразу после загрузки и не блокирует DOMContentLoaded.
  • <script defer> выполняется после разбора документа, но до срабатывания DOMContentLoaded, в порядке их следования в документе.

Если вы управляете порядком выполнения скриптов с помощью этих атрибутов, полная картина описана в разделе scripts: async, defer.

Проверка состояния через document.readyState

Если ваш код может выполниться после того, как DOM уже разобран (например, скрипт, добавленный динамически), обработчик события никогда не вызовется, поскольку событие уже произошло. Защититесь от этого с помощью document.readyState:

function onReady() {
  console.log('DOM is ready, current state:', document.readyState);
}

if (document.readyState === 'loading') {
  // Still parsing — wait for the event.
  document.addEventListener('DOMContentLoaded', onReady);
} else {
  // 'interactive' or 'complete' — DOM is already ready.
  onReady();
}

readyState проходит через три значения: "loading" во время разбора, "interactive" когда DOM готов (в этот момент срабатывает DOMContentLoaded), и "complete" когда всё, включая ресурсы, загружено (в этот момент срабатывает load).

Интерактивный пример: DOMContentLoaded в действии

Чтобы увидеть DOMContentLoaded в действии, следующий пример обновляет содержимое элемента div после полного разбора HTML-документа, но до завершения загрузки всей страницы.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>DOMContentLoaded Example</title>
</head>
<body>
    <div id="output">Waiting for DOM...</div>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            document.getElementById('output').innerHTML = 'DOM fully loaded and parsed!'
        });
    </script>
</body>
</html>

Этот пример можно вставить в любой HTML-файл и открыть в браузере, чтобы увидеть, насколько быстро срабатывает DOMContentLoaded по сравнению с полной загрузкой страницы.

Исследование события load

Событие load необходимо для операций, требующих полной загрузки веб-страницы, включая все зависимые ресурсы — изображения и стили. К моменту срабатывания load каждое изображение имеет реальные размеры, шрифты применены, а iframe загружены — поэтому именно здесь следует размещать код, измеряющий отрендеренную страницу.

Компромисс заключается в тайминге: одно большое изображение или медленный сторонний виджет задержит load для всей страницы. Именно поэтому следует использовать load только для работы, действительно зависящей от ресурсов, а обычную инициализацию оставлять на DOMContentLoaded. Для обработки загрузки и ошибок отдельных ресурсов (а не всей страницы) см. resource loading: onload and onerror.

Интерактивный пример: демонстрация события load

Здесь мы создаём пример, в котором сообщение отображается только после полной загрузки всего содержимого страницы. Как видите, событие DOMContentLoaded всегда происходит раньше события load.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Load Event Example</title>
</head>
<body>
  <div>You see first triggered event on top of the other.</div>
  <script>
    window.addEventListener('load', function() {
      const newDiv = document.createElement("div");
      newDiv.innerHTML = 'loaded event happened!'
      document.body.append(newDiv);
    });
    window.addEventListener('DOMContentLoaded', function() {
      const newDiv = document.createElement("div");
      newDiv.innerHTML = 'DOMContentLoaded event happened!'
      document.body.append(newDiv);
    });
  </script>
</body>
</html>

Скопируйте и проверьте этот код в своём HTML-окружении, чтобы увидеть разницу в тайминге между DOMContentLoaded и load.

Работа с событием beforeunload

Событие beforeunload чрезвычайно полезно для предотвращения потери данных — оно предупреждает пользователей перед тем, как они покинут страницу с несохранёнными изменениями. Когда обработчик устанавливает event.returnValue, браузер показывает стандартный диалог подтверждения («Покинуть сайт? Изменения могут не сохраниться»).

Несколько правил, которые браузеры теперь применяют принудительно, — стоит знать их перед использованием:

  • Пользовательское сообщение игнорируется. Для борьбы со спамом браузеры отображают собственный текст вне зависимости от строки, которую вы назначаете. Достаточно установить returnValue в любое непустое значение (или вызвать event.preventDefault()), чтобы вызвать диалог.
  • Диалог появляется только если пользователь взаимодействовал со страницей (кликал, печатал и т. д.). Страница, с которой пользователь не взаимодействовал, не может блокировать навигацию.
  • Регистрируйте обработчик только при наличии реально несохранённых изменений и удаляйте его после сохранения данных. Постоянный обработчик beforeunload раздражает пользователей и может ухудшить производительность кеша назад/вперёд.

Более надёжный паттерн добавляет и удаляет обработчик на основе флага изменений:

let isDirty = false;

function beforeUnloadHandler(event) {
  event.preventDefault();
  // Required for some browsers to show the dialog.
  event.returnValue = '';
}

function markDirty() {
  if (!isDirty) {
    isDirty = true;
    window.addEventListener('beforeunload', beforeUnloadHandler);
  }
}

function markSaved() {
  isDirty = false;
  window.removeEventListener('beforeunload', beforeUnloadHandler);
}

Интерактивный пример: реализация подтверждения через beforeunload

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

<!DOCTYPE html>
<html lang="en">
<head>
    <title>beforeunload Example</title>
</head>
<body>
    <p>Now if you want to exit this page, a confirmation alert will be shown as a result of the <code>beforeunload</code> event.</p>
    <script>
        window.addEventListener('beforeunload', function(event) {
            event.returnValue = '';
        });
    </script>
</body>
</html>

Проверьте этот код, открыв страницу «попробуй сам» и попытавшись перейти на другую страницу.

Использование события unload

Внимание

The unload event is deprecated and you won't be able to use it in most modern browsers. It is also considered a bad practice. Therefore this part is only for better information about legacy codes.

Хотя событие unload сейчас редко используется из-за ограничений современных браузеров и распространения одностраничных приложений, исторически оно срабатывало при закрытии страницы — это было полезно для освобождения ресурсов или отправки последнего аналитического запроса. Проблема в том, что unloadbeforeunload) не позволяют браузеру сохранять страницу в кеше назад/вперёд (bfcache), что замедляет навигацию по кнопке «Назад» для всех пользователей.

Современные альтернативы: visibilitychange и pagehide

Вместо unload следует слушать visibilitychange и обрабатывать переход в состояние "hidden" как последний надёжный момент для сохранения состояния. Это событие стабильно срабатывает на десктопе и мобильных устройствах, в том числе когда пользователь переключает вкладки или закрывает приложение:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    // Last reliable point to save state.
    console.log('Page hidden — flush analytics / save draft here');
  }
});

Для отправки данных при выходе используйте navigator.sendBeacon(), который ставит запрос в очередь и гарантирует его отправку даже после закрытия страницы:

function sendStats(data) {
  navigator.sendBeacon('/log', JSON.stringify(data));
}

Интерактивный пример: использование события unload

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

<!DOCTYPE html>
<html lang="en">
<head>
    <title>unload Example</title>
</head>
<body>
    <script>
        window.addEventListener('unload', function(event) {
            alert('Page is unloading...');
            // Perform cleanup tasks or analytics here
        });
    </script>
</body>
</html>

Заключение

Каждое из этих событий соответствует определённой фазе жизненного цикла веб-страницы — от начальной загрузки до окончательного закрытия. Практические рекомендации кратки:

  • Выполняйте большую часть инициализации в DOMContentLoaded — оно срабатывает раньше всего, и DOM уже готов.
  • Используйте load только когда вам необходимо дождаться полной загрузки изображений, шрифтов или других подресурсов.
  • Используйте beforeunload для защиты несохранённых изменений, добавляя обработчик только при наличии несохранённых данных.
  • Избегайте unload; вместо него используйте visibilitychange вместе с navigator.sendBeacon().

Для более глубокого изучения обратитесь к связанным разделам:

Практика

Практика
Что делает событие 'beforeunload' в приведённом HTML-примере?
Что делает событие 'beforeunload' в приведённом HTML-примере?
Was this page helpful?