Измените URL в браузере, не загружая новую страницу с помощью JavaScript

297

Как получить действие JavaScript, которое может оказать некоторое влияние на текущую страницу, но при этом изменить URL-адрес в браузере, поэтому, если пользователь нажимает кнопку перезагрузки или закладки, используется новый URL-адрес?

Также было бы хорошо, если бы кнопка «Назад» перезагрузила бы исходный URL.

Я пытаюсь записать состояние JavaScript в URL.

Стивен Нобл
источник
1
Это было бы так мило. Конечно, это будет ограничено модификациями того же домена. Но некоторый контроль пути (и не только хеш) на стороне клиента теперь является логическим шагом, поскольку перезагрузки страниц являются своего рода «последним средством» для многих приложений.
Harpo
1
«В некотором роде» хорошее использование pushState:for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}
Дерек 朕 會 功夫
Пример этого эффекта в действии: dujour.com
Бен
2
Пример этого эффекта: facebook.com (при открытии изображений в лайтбоксе)
Это хороший вопрос. однако печальная ситуация заключается в том, что, если бы этот вопрос был задан таким образом сегодня, он был бы опущен и спровоцирован тем, что «это не тот сайт, на котором вы заставляете людей писать весь ваш код для вас».
роса

Ответы:

118

С HTML 5 используйте history.pushStateфункцию . Например:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

и href:

<a href="#" id='click'>Click to change url to bar.html</a>

Если вы хотите изменить URL-адрес без добавления записи в список кнопок «Назад», используйте history.replaceStateвместо этого.

clu3
источник
Вы правы, в прошлый раз, когда я экспериментировал с Chrome. Я только что попробовал с Firefox 3.6 на моем Ubuntu, это не сработало. Я думаю, нам придется подождать, пока HTML5 полностью не будет поддерживаться в FF
clu3
15
Пример кода вырезан и вставлен из developer.mozilla.org/en/DOM/Manipulation_the_browser_history . Что на самом деле мешает объяснить, что означают foo и bar в этом случае.
mikemaccana
2
Foo и bar ничего не значат, это произвольно определенный объект состояния, который вы можете получить позже.
Пол Диксон
3
@Paul: да, статья выше предоставляет эту информацию.
mikemaccana
2
Начиная с публикации этого комментария, он работает на Firefox Chrome и Safari. Не повезло с мобильным сафари на ipad или iphone, хотя :(
Сид
187

Если вы хотите работать в браузерах , которые не поддерживают history.pushStateи history.popStateтем не менее, «старый» способ установить идентификатор фрагмента, который не вызовет перезагрузку страницы.

Основная идея состоит в том, чтобы установить для window.location.hashсвойства значение, которое содержит любую необходимую информацию о состоянии, затем либо использовать событие window.onhashchange , либо для старых браузеров, которые не поддерживают onhashchange(IE <8, Firefox <3.6), периодически проверять, чтобы посмотрите, изменился ли хеш (используя, setIntervalнапример), и обновите страницу. Вам также нужно будет проверить значение хеша при загрузке страницы, чтобы настроить исходный контент.

Если вы используете jQuery, есть плагин hashchange, который будет использовать тот метод, который поддерживает браузер. Я уверен, что есть плагины для других библиотек.

Остерегайтесь столкновения с идентификаторами на странице, потому что браузер прокручивает до любого элемента с совпадающим идентификатором.

Мэтью Крамли
источник
2
Разве поисковые системы не игнорируют эти внутренние ссылки (хэши)? В моем сценарии я хочу, чтобы пауки тоже брали эти ссылки.
Дрю Ноакс
3
@ Насколько я знаю, поисковые системы никак не могут рассматривать часть страницы как отдельный результат.
Мэтью Крамли
8
В настоящее время есть предложение, чтобы поисковые системы видели хэши: googlewebmastercentral.blogspot.com/2009/10/…
LKM
5
Вместо использования setIntervalдля проверки document.location.hashможно использовать hashchangeсобытие, так как оно гораздо более приемлемо. Проверьте, какие браузеры поддерживают каждый стандарт . Мой текущий подход заключается в использовании push / popState в браузерах, которые его поддерживают. На остальных я установил хеш и смотрю только hashchangeв поддерживающих браузерах. Это оставляет IE <= 7 без поддержки истории, но с полезной постоянной ссылкой. Но кого волнует история IE <= 7?
Ира Карвалью,
2
@gabeDel Да, хотя я не думаю, что Analytics записывает хэш, поэтому он будет иметь тот же URL. Лучшим способом было бы вручную вызвать _trackPageview«реальный» URL новой страницы.
Мэтью Крамли
37

window.location.href содержит текущий URL. Вы можете прочитать из него, вы можете добавить к нему, и вы можете заменить его, что может привести к перезагрузке страницы.

Если, как это звучит, вы хотите записать состояние javascript в URL, чтобы его можно было добавить в закладки, не перезагружая страницу, добавьте его к текущему URL после # и получите фрагмент JavaScript, вызванный событием onload, проанализируйте текущий URL-адрес, чтобы увидеть, если он содержит сохраненное состояние.

Если вы используете? вместо # вы будете принудительно перезагружать страницу, но так как вы будете анализировать сохраненное состояние при загрузке, это может на самом деле не быть проблемой; и это заставит кнопки вперед и назад работать правильно.

Лунная тень
источник
2
запись состояния javascript в URL - это именно то, что я пытаюсь сделать
Стивен Нобл
21

Я сильно подозреваю, что это невозможно, потому что это было бы невероятной проблемой безопасности. Например, я мог бы сделать страницу, похожую на страницу входа в банк, и сделать URL-адрес в адресной строке похожим на настоящий банк !

Возможно, если вы объясните, почему вы хотите это сделать, люди могут предложить альтернативные подходы ...

[Редактировать в 2011 году: с тех пор, как я написал этот ответ в 2008 году, стало известно больше информации о технике HTML5, которая позволяет изменять URL-адрес, если он принадлежит одному и тому же источнику]

Пол Диксон
источник
6
Не так уж много проблем, если изменения без перезагрузки были ограничены путем, строкой запроса и фрагментом, то есть не полномочиями (доменом и т. Д.).
Олли Сондерс
2
youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg - пример того, что это возможно, попробуйте переключить видео :)
Spidfire
Вы можете изменить только location.hash (все после #), как отмечает Мэтью Чамли в принятом ответе.
Пол Диксон
2
Вы используете Фейсбук? Facebook меняет URL без перезагрузки, используя history.pushState().
Дерек 朕 會 功夫
Пол Диксон, вы должны заметить входящие в Facebook, когда вы нажимаете на имена с левой стороны, только URL изменяется, и новый чат загружается в окно чата, страница не обновляется. также есть много других сайтов на WebGL, они делают то же самое
Dheeraj Thedijje
8

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

Единственный надежный способ изменить URL без смены страниц - использовать внутреннюю ссылку или хэш. например: http://site.com/page.html становится http://site.com/page.html#item1 . Эта техника часто используется в Hijax (AJAX + сохранить историю).

При этом я часто просто использую ссылки для действий с хешем в качестве href, а затем добавляю события щелчка с помощью jquery, которые используют запрошенный хеш для определения и делегирования действия.

Я надеюсь, что это ставит вас на правильный путь.

Джетро Ларсон
источник
Извините, но ваш ответ не соответствует действительности. Да, вы можете изменить URL.
Дерек 朕 會 功夫
1
Да, вы можете использовать API html5 history, но это не кросс-браузер, хотя вы можете использовать poly-fill, которая будет возвращаться к хеш-URL.
Джетро Ларсон
8

jQuery имеет отличный плагин для изменения URL браузера, называемый jQuery-pusher .

JavaScript pushStateи jQuery можно использовать вместе, например:

history.pushState(null, null, $(this).attr('href'));

Пример:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});

Использование только JavaScript history.pushState() , который изменяет реферер, который используется в заголовке HTTP для объектов XMLHttpRequest, созданных после изменения состояния.

Пример:

window.history.pushState("object", "Your New Title", "/new-url");

Метод pushState ():

pushState()принимает три параметра: объект состояния, заголовок (который в настоящее время игнорируется) и (необязательно) URL-адрес. Давайте рассмотрим каждый из этих трех параметров более подробно:

  1. объект состояния - объект состояния - это объект JavaScript, связанный с новой записью истории, созданной pushState(). Всякий раз, когда пользователь переходит в новое состояние, запускается событие popstate, а свойство состояния события содержит копию объекта состояния записи истории.

    Объект состояния может быть любым, что может быть сериализовано. Поскольку Firefox сохраняет объекты состояния на диск пользователя, чтобы их можно было восстановить после того, как пользователь перезапустил свой браузер, мы накладываем ограничение размера в 640 тыс. Символов на сериализованное представление объекта состояния. Если вы передадите объект состояния, сериализованное представление которого больше этого pushState(), метод сгенерирует исключение. Если вам нужно больше места, чем это, вам рекомендуется использовать sessionStorage и / или localStorage.

  2. title - Firefox в настоящее время игнорирует этот параметр, хотя может использовать его в будущем. Передача пустой строки здесь должна быть безопасной от будущих изменений метода. В качестве альтернативы вы можете передать короткий заголовок для штата, в который вы переходите.

  3. URL - этот параметр задает URL новой записи истории. Обратите внимание, что браузер не будет пытаться загрузить этот URL после вызова pushState(), но он может попытаться загрузить URL позже, например, после того, как пользователь перезапустит свой браузер. Новый URL не обязательно должен быть абсолютным; если он относительный, он разрешается относительно текущего URL. Новый URL должен иметь то же происхождение, что и текущий URL; в противном случае, pushState()скинут исключение. Этот параметр является необязательным; если он не указан, ему присваивается текущий URL документа.

Илья Ростовцев
источник
3

Фотогалерея Facebook делает это с помощью #hash в URL. Вот несколько примеров URL:

Прежде чем нажать «Далее»:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

После нажатия «Далее»:

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Обратите внимание на хэш-удар (#!), За которым сразу следует новый URL.

Джонатон Хилл
источник
2

Что работает для меня это - history.replaceState()функция, которая заключается в следующем -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

Это не перезагрузит страницу, вы можете использовать ее с событием JavaScript

Вешрадж Джоши
источник
1

Мне было интересно, будет ли это возможно, пока родительский путь на странице один и тот же, к нему добавляется только что-то новое.

Так, например, скажем, пользователь на странице: http://domain.com/site/page.html тогда браузер может разрешить мне сделать, location.append = new.html и страница становится: http://domain.com/site/page.htmlnew.htmlи браузер не меняет ее.

Или просто позволить человеку изменить параметр get, так что давайте location.get = me=1&page=1.

Так что оригинальная страница становится http://domain.com/site/page.html?me=1&page=1и не обновляется.

Проблема с # заключается в том, что данные не кэшируются (по крайней мере, я так не думаю) при изменении хэша. Таким образом, это похоже на каждый раз, когда загружается новая страница, тогда как кнопки «назад» и «вперед» на не- Ajax- странице способны кешировать данные и не тратить время на их повторную загрузку.

Из того, что я увидел, история Yahoo уже загружает все данные одновременно. Кажется, он не выполняет никаких запросов Ajax. Таким образом, когда a divиспользуется для обработки другого метода сверхурочно, эти данные не сохраняются для каждого состояния истории.

user58670
источник
1

мой код:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

и действие:

setLocation('http://example.com/your-url-here');

и пример

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

Вот и все :)

Туско Труш
источник
1

Более простой ответ, который я представляю,

window.history.pushState(null, null, "/abc")

это добавится /abcпосле имени домена в URL браузера. Просто скопируйте этот код и вставьте его в консоль браузера и увидите, что URL-адрес меняется на « https://stackoverflow.com/abc »

Сэм
источник
0

Я имел успех с:

location.hash="myValue";

Это просто добавляет #myValueк текущему URL. Если вам нужно вызвать событие на странице «Загрузка», вы можете использовать его, location.hashчтобы проверить соответствующее значение. Просто не забудьте удалить #значение, возвращаемое, location.hashнапример,

var articleId = window.location.hash.replace("#","");
Адам Эй
источник