Avatar ·

Как изменить URL в адресной строке без перезагрузки страницы?

Всем привет! Я начинающий фронтенд-разработчик и столкнулся с такой задачей: нужно менять URL в адресной строке браузера, когда пользователь кликает по ссылкам внутри сайта, но при этом страница не должна перезагружаться. Например, у меня есть одностраничное приложение (SPA) со списком статей. Когда пользователь кликает по заголовку статьи, я хочу, чтобы URL менялся с `/` на `/article/123`, но контент подгружался через AJAX без полной перезагрузки. Сейчас я использую обычные ссылки с `href`, и браузер просто перезагружает страницу. Пробовал использовать `window.location.href = '/article/123'`, но это тоже вызывает перезагрузку. Вот пример моего текущего кода: ```javascript // Это не работает как надо document.querySelector('.article-link').addEventListener('click', function(e) { e.preventDefault(); window.location.href = '/article/123'; // Всё равно перезагружает страницу // Загрузка контента через fetch... }); ``` Как правильно изменить URL без перезагрузки? Нужно ли для этого использовать какие-то специальные методы? И как потом обрабатывать эти изменения, чтобы при нажатии "Назад" в браузере всё работало корректно? Буду благодарен за подробное объяснение с примерами! <<>>

Использование History API для изменения URL без перезагрузки

Для решения вашей задачи идеально подходит History API — встроенный механизм браузера, который позволяет манипулировать историей навигации и URL без перезагрузки страницы. Основные методы, которые вам понадобятся:

  • history.pushState() — добавляет новую запись в историю браузера
  • history.replaceState() — заменяет текущую запись в истории
  • window.popstate — событие, срабатывающее при навигации по истории

Как это работает на практике

Вот полный пример реализации для вашего случая со статьями:

// HTML-разметка
<div id="content">
    <a href="/article/123" class="article-link" data-id="123">Статья 1</a>
    <a href="/article/456" class="article-link" data-id="456">Статья 2</a>
</div>

// JavaScript
document.querySelectorAll('.article-link').forEach(link => {
    link.addEventListener('click', function(e) {
        e.preventDefault();
        
        const articleId = this.dataset.id;
        const newUrl = `/article/${articleId}`;
        
        // 1. Меняем URL без перезагрузки
        history.pushState({ articleId: articleId }, '', newUrl);
        
        // 2. Загружаем контент асинхронно
        loadArticleContent(articleId);
    });
});

function loadArticleContent(articleId) {
    fetch(`/api/articles/${articleId}`)
        .then(response => response.json())
        .then(data => {
            document.getElementById('content').innerHTML = data.html;
            // Обновляем title страницы
            document.title = data.title;
        });
}

// 3. Обработка кнопок "Назад" и "Вперёд"
window.addEventListener('popstate', function(e) {
    if (e.state && e.state.articleId) {
        loadArticleContent(e.state.articleId);
    } else {
        // Если нет state, загружаем главную страницу
        loadMainPage();
    }
});

Разница между pushState и replaceState

pushState создаёт новую запись в истории. Это значит, что пользователь сможет вернуться на предыдущий URL кнопкой "Назад". Используйте его, когда переход на новый URL — это значимое действие (например, открытие статьи).

replaceState заменяет текущую запись. Пользователь не сможет вернуться на предыдущий URL через "Назад". Это удобно для промежуточных состояний (например, фильтрация списка).

// Пример с replaceState
function applyFilter(filterType) {
    const newUrl = `/products?filter=${filterType}`;
    history.replaceState({ filter: filterType }, '', newUrl);
    updateProductList(filterType);
}

Важные моменты

  • Безопасность: Вы не можете изменить URL на другой домен — только путь и параметры в пределах текущего домена
  • Серверная поддержка: Если пользователь обновит страницу с новым URL, сервер должен корректно обработать этот запрос (например, через роутинг на сервере)
  • SEO: Для одностраничных приложений используйте серверный рендеринг или prerendering, чтобы поисковые системы могли индексировать контент
  • Совместимость: History API поддерживается всеми современными браузерами, включая IE10+

В итоге ваш код будет работать так: пользователь кликает по ссылке → URL меняется в адресной строке → контент подгружается через fetch → при нажатии "Назад" срабатывает popstate и загружается предыдущий контент. Всё без единой перезагрузки страницы!

<<>>

Альтернативный подход с использованием hash (#)

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

// Изменение URL через hash
document.querySelector('.article-link').addEventListener('click', function(e) {
    e.preventDefault();
    const articleId = this.dataset.id;
    window.location.hash = `article/${articleId}`;
    loadArticleContent(articleId);
});

// Обработка изменения hash
window.addEventListener('hashchange', function() {
    const hash = window.location.hash.slice(1); // убираем #
    if (hash.startsWith('article/')) {
        const articleId = hash.split('/')[1];
        loadArticleContent(articleId);
    }
});

Плюсы:

  • Простота реализации — не нужно передавать state
  • Работает во всех браузерах, включая очень старые
  • Не требует серверной поддержки (часть после # не отправляется на сервер)

Минусы:

  • URL выглядит менее эстетично: /#article/123 вместо /article/123
  • Меньше контроля над историей навигации
  • Сложнее с SEO — поисковики могут не индексировать hash-контент

Выбирайте этот способ, если вам нужна максимальная совместимость и простота, а эстетика URL не критична.