PushState: примеры (JAVASCRIPT)

Полное руководство по методу pushState в JavaScript
Раздел: BOM, История
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

Пример javascript
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".

Бесконечная прокрутка с сохранением позиции

Пример javascript
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}`);
    }
});

Сложное состояние для формы фильтрации

Пример javascript
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, а состояние истории будет содержать объект фильтров.

JS pushState function comments

En
PushState Adds an entry to the browser's session history stack