Событие popstate является частью API History и возникает
при изменении текущей записи истории браузера. Оно срабатывает, когда
пользователь перемещается по истории с помощью кнопок «назад» и
«вперёд», а также при программном вызове методов
history.back(), history.forward() и
history.go().
Ключевая особенность: событие не инициируется при вызове
history.pushState() или
history.replaceState(). Оно возникает только при
переходе между уже существующими записями истории.
window.addEventListener('popstate', (event) => {
console.log('Сработало событие popstate');
console.log(event.state);
});
Объект события содержит свойство state, которое
представляет собой данные, ранее переданные в pushState или
replaceState.
Методы History API позволяют управлять историей браузера без перезагрузки страницы:
history.pushState(state, title, url) — добавляет новую
записьhistory.replaceState(state, title, url) — заменяет
текущую записьhistory.back() — переход назадhistory.forward() — переход вперёдПример добавления состояния:
history.pushState({ page: 'about' }, '', '/about');
При возврате к этой записи через кнопку «назад» произойдёт событие
popstate, и в event.state будет:
{ page: 'about' }
popstateВ некоторых браузерах (особенно старых версиях) событие
popstate может срабатывать при загрузке страницы. В
современных реализациях оно не вызывается при первом открытии
страницы.
Изменение URL и состояние истории происходят синхронно, но обработка события может быть воспринята как асинхронная, особенно при сложной логике маршрутизации.
pushStatehistory.pushState({ page: 'home' }, '', '/home');
// popstate НЕ вызовется
Это важно учитывать при разработке маршрутизаторов.
popstate в
Page.jsБиблиотека Page.js реализует клиентскую маршрутизацию, используя
History API. Событие popstate является основным механизмом
отслеживания навигации пользователя.
Page.js подписывается на popstate и при его
срабатывании:
Упрощённая схема:
window.addEventListener('popstate', function () {
page.dispatch(location.pathname);
});
При использовании Page.js пользовательская навигация через браузер автоматически обрабатывается:
page('/home', () => {
console.log('Главная страница');
});
page('/about', () => {
console.log('О нас');
});
page();
Последовательность:
/home/aboutpopstate/homeevent.stateХотя Page.js в основном ориентируется на URL, доступ к
event.state остаётся важным для хранения дополнительной
информации.
window.addEventListener('popstate', (event) => {
if (event.state) {
console.log('Данные состояния:', event.state);
}
});
Использование:
history.pushState({ scrollY: window.scrollY }, '', '/page');
При возврате можно восстановить позицию прокрутки:
window.addEventListener('popstate', (event) => {
if (event.state && event.state.scrollY) {
window.scrollTo(0, event.state.scrollY);
}
});
Одной из задач popstate является восстановление
состояния интерфейса при навигации.
Пример:
page('/products/:id', (ctx) => {
loadProduct(ctx.params.id);
});
При возврате назад:
popstate срабатываетpopstateОжидание, что pushState вызовет
popstate:
history.pushState({}, '', '/new');
// обработчик popstate НЕ выполнится
Иногда обработка маршрута выполняется и при pushState, и
при popstate, что приводит к дублированию:
function navigate(path) {
history.pushState({}, '', path);
render(path); // вручную
}
И отдельно:
window.addEventListener('popstate', () => {
render(location.pathname);
});
Page.js решает эту проблему централизованной маршрутизацией.
Браузеры по умолчанию могут сохранять позицию прокрутки, но при SPA-навигации это поведение часто нужно контролировать вручную.
window.addEventListener('popstate', () => {
window.scrollTo(0, 0);
});
Или с использованием сохранённого состояния:
history.pushState({ scroll: window.scrollY }, '');
Page.js поддерживает middleware, которые также участвуют в обработке
popstate.
page((ctx, next) => {
console.log('Переход:', ctx.path);
next();
});
При срабатывании popstate:
Если пользователь вручную изменяет URL:
popstate не участвуетЕсли изменение происходит через историю:
popstate срабатываетpopstate не реагирует на изменения hash,
если не используется History API.
Для hash:
window.addEventListener('hashchange', () => {
console.log(location.hash);
});
Page.js может работать в режиме hash, но тогда используется другой механизм.
popstateПолезный шаблон:
window.addEventListener('popstate', (event) => {
console.log({
pathname: location.pathname,
state: event.state
});
});
Позволяет отслеживать:
При использовании History API сервер должен корректно обрабатывать маршруты:
Иначе при обновлении страницы возникнет ошибка 404.
popstatepushStatepopstatepopstateconst routes = {
'/': () => console.log('Главная'),
'/about': () => console.log('О нас')
};
function render(path) {
if (routes[path]) {
routes[path]();
}
}
window.addEventListener('popstate', () => {
render(location.pathname);
});
function navigate(path) {
history.pushState({}, '', path);
render(path);
}
Этот пример иллюстрирует ту же концепцию, которую Page.js реализует более полноценно.
Можно ограничивать поведение пользователя:
window.addEventListener('popstate', (event) => {
if (!event.state) {
history.pushState({ blocked: true }, '', location.href);
}
});
Однако такие подходы требуют осторожности и могут ухудшать пользовательский опыт.
Событие popstate само по себе лёгкое, но обработчики
могут быть тяжёлыми:
Оптимизация включает:
pushStatestate не должны содержать чувствительную
информациюpopstate — один из ключевых элементов SPA:
Page.js использует это событие как основу своей архитектуры маршрутизации, обеспечивая предсказуемое и управляемое поведение навигации.