PushState: примеры (JAVASCRIPT)
pushState(state: Object, title: String, [url]: String): undefinedОсновные сведения о pushState
Метод history.pushState() является частью History API в браузере. Он позволяет добавлять новую запись в историю сессии без перезагрузки страницы. Это ключевой инструмент для создания одностраничных приложений (SPA), где навигация и отображение контента происходят динамически.
Функция используется для обновления URL в адресной строке браузера и сохранения связанных с этим состояний. Это обеспечивает корректную работу кнопок "Назад" и "Вперед", а также возможность делиться ссылками на конкретные состояния интерфейса.
Аргументы метода:
- state (объект): Объект состояния, связанный с новой записью в истории. Он может содержать любые данные, сериализуемые в JSON. Размер объекта ограничен, и браузер может хранить его на диске. При перезагрузке страницы объект состояния восстанавливается.
- title (строка): В настоящее время большинство браузеров игнорирует этот параметр. Для совместимости обычно передают пустую строку.
- url (строка, необязательный): Новый URL для отображения в адресной строке. Он должен быть того же происхождения (same-origin), что и текущий URL. Может быть абсолютным или относительным путем. Если параметр опущен или равен
null, используется текущий URL.
Возвращаемое значение: undefined.
Примеры использования pushState
Базовое изменение URL
// Меняем URL без перезагрузки страницы
history.pushState(null, '', '/new-page.html');
console.log(location.pathname); // Проверяем новый путьВыведет: /new-page.html
С сохранением состояния
const stateObj = { page: 'catalog', filters: { category: 'books' } };
history.pushState(stateObj, '', '/catalog/books');
// Позже состояние можно получить из события popstate
console.log(history.state);Выведет: { page: "catalog", filters: { category: "books" } }Использование относительного пути
// При текущем URL https://example.com/dashboard/
history.pushState(null, '', 'reports?year=2023');
console.log(location.href);Выведет: https://example.com/dashboard/reports?year=2023
Похожие функции в JavaScript
history.replaceState() — заменяет текущую запись в истории, не создавая новую. Полезен для обновления состояния текущей страницы без добавления новой точки в навигации.
Изменение свойства location.hash — позволяет менять часть URL после символа '#'. Это вызывает событие hashchange и часто использовалось для роутинга до появления pushState. Основное отличие: hash не отправляется на сервер и имеет ограниченную емкость для данных состояния.
Прямое присваивание window.location — приводит к полной перезагрузке страницы, что не подходит для SPA.
Метод pushState предпочтительнее, когда требуется чистая навигация без хэшей и полный контроль над URL. ReplaceState используют, когда необходимо исправить или обновить состояние текущей страницы, не ломая навигацию пользователя.
Альтернативы в других языках
Концепция изменения URL без перезагрузки страницы специфична для клиентского JavaScript. В серверных языках аналогичные задачи решаются иначе — через перенаправления (редиректы).
PHP
Функция header() отправляет заголовок HTTP для перенаправления, что приводит к полной загрузке новой страницы.
<?php
header('Location: /new-page.php');
exit();
?>Браузер получает код ответа 302 и выполняет полную перезагрузку по указанному адресу.
Python (Flask)
Используется функция redirect() из модуля flask.
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/old')
def old_page():
return redirect('/new')Сервер возвращает код 302, браузер загружает новую страницу.
Ключевое отличие от pushState — серверные редиректы всегда требуют обращения к серверу и полной загрузки страницы, в то время как pushState работает полностью на клиенте, обновляя только часть интерфейса.
Типичные ошибки
Нарушение политики одинакового происхождения (Same-Origin)
// Текущий URL: https://my-site.com
// Попытка изменить URL на другой домен
history.pushState(null, '', 'https://other-site.com');Вызов не сработает. В консоли браузера появится ошибка безопасности (SecurityError).
Слишком большой объект состояния
const hugeState = { data: new Array(1000000).fill('text') };
history.pushState(hugeState, '', '/');Браузер может отказаться сохранять такой объем данных. На практике размер объекта должен быть минимальным.
Необработка события popstate
После использования pushState состояние интерфейса должно синхронизироваться с историей. Если не добавить обработчик события popstate, то при нажатии кнопок "Назад"/"Вперед" URL изменится, а содержимое страницы — нет.
history.pushState({ page: 2 }, '', '?page=2');
// Без этого обработчика навигация будет некорректной
window.addEventListener('popstate', (event) => {
console.log('Location changed to:', location.href);
});Изменения в истории функции
Метод pushState() был стандартизирован в спецификации HTML5. С момента своего появления в основных браузерах он претерпел незначительные изменения в деталях реализации.
Основные уточнения касались размера объекта состояния, который браузеры согласились сохранять на диске. В современных браузерах лимит достаточно велик для практических нужд, но точное значение не регламентировано и может различаться.
Параметр title изначально предназначался для обновления заголовка вкладки браузера, но эта функциональность так и не была реализована единообразно. Поэтому сейчас этот параметр игнорируется, и для обновления заголовка страницы используют document.title отдельно.
Расширенные примеры применения
Интеграция с системой роутинга SPA
const router = {
routes: {
'/': 'Home',
'/about': 'About',
'/contact': 'Contact'
},
navigate(path) {
// Изменяем URL и состояние
history.pushState({ view: this.routes[path] }, '', path);
// Обновляем контент на странице
this.renderView(this.routes[path]);
},
renderView(viewName) {
document.getElementById('app').innerHTML = `<h1>${viewName}</h1>`;
}
};
// Обработка кнопок браузера
window.addEventListener('popstate', (e) => {
if (e.state && e.state.view) {
router.renderView(e.state.view);
}
});
// Инициируем навигацию
router.navigate('/about');URL в браузере изменится на /about, а на странице отобразится заголовок "About".
Бесконечная прокрутка с сохранением позиции
let currentPage = 1;
function loadNextPage() {
currentPage++;
// Добавляем запись в историю при загрузке новой порции контента
history.pushState(
{ page: currentPage, scrollY: window.scrollY },
'',
`?page=${currentPage}`
);
// Симуляция загрузки данных
console.log(`Загружена страница ${currentPage}`);
}
// Восстановление позиции при навигации назад
window.addEventListener('popstate', (e) => {
if (e.state && e.state.scrollY !== undefined) {
window.scrollTo(0, e.state.scrollY);
currentPage = e.state.page;
console.log(`Восстановлена страница ${currentPage}`);
}
});Сложное состояние для формы фильтрации
function applyFilters(filters) {
const queryString = new URLSearchParams(filters).toString();
const newUrl = `/search?${queryString}`;
history.pushState(
{ filters: filters, timestamp: Date.now() },
'',
newUrl
);
// Здесь функция, которая обновляет результаты поиска на странице
updateSearchResults(filters);
}
// Пример вызова
applyFilters({ category: 'electronics', price: '100-500', sort: 'newest' });URL обновится на /search?category=electronics&price=100-500&sort=newest, а состояние истории будет содержать объект фильтров.