Как определить, изменился ли URL после хэша в JavaScript


161

Как я могу проверить, изменился ли URL в JavaScript? Например, веб-сайты, такие как GitHub, которые используют AJAX, будут добавлять информацию о странице после символа #, чтобы создать уникальный URL-адрес без перезагрузки страницы. Как лучше всего определить, изменяется ли этот URL?

  • Является ли onloadназывается событие еще раз?
  • Есть ли обработчик событий для URL?
  • Или URL должен проверяться каждую секунду, чтобы обнаружить изменение?

1
Для будущих посетителей лучшим ответом будет новый ответ @aljgom от 2018 года: stackoverflow.com/a/52809105/151503
Redzarf,

Ответы:


106

В современных браузерах (IE8 +, FF3.6 +, Chrome) вы можете просто прослушать hashchangeсобытие window.

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


133
Это, как я понимаю, работает только для смены детали после знака # (отсюда и название события)? И не для полного изменения URL, как видно из заголовка вопроса.
NPC

13
@NPC Любой обработчик для полного изменения URL (без тега привязки)?
Неха Чоудхари

Вам редко нужны события тайм-аута: используйте мышь и keyboardevents для проверки.
Сеити

что если путь изменится, а не хеш?
SuperUberDuper

@SuperUberDuper Если путь изменяется, потому что пользователь инициировал навигацию / щелкнул ссылку и т. Д., То вы увидите только beforeunloadсобытие. Если ваш код инициировал изменение URL, он знает лучше.
phihag

92

Я хотел иметь возможность добавлять locationchangeслушателей событий. После изменения ниже мы сможем сделать это, как это

window.addEventListener('locationchange', function(){
    console.log('location changed!');
})

Напротив, window.addEventListener('hashchange',()=>{})будет срабатывать только в том случае, если часть после хэштега в URL-адресе изменяется и window.addEventListener('popstate',()=>{})не всегда работает.

Эта модификация, аналогичная ответу Кристиана , изменяет объект истории, чтобы добавить некоторую функциональность.

По умолчанию есть popstateсобытие, но нет событий для pushstateи replacestate.

Это изменяет эти три функции, так что все они запускают пользовательское locationchangeсобытие для вас, а также pushstateи replacestateсобытия, если вы хотите их использовать:

/* These are the modifications: */
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.pushState);

history.replaceState = ( f => function replaceState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('replacestate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.replaceState);

window.addEventListener('popstate',()=>{
    window.dispatchEvent(new Event('locationchange'))
});

2
Отлично, что мне было нужно Спасибо
eslamb

Спасибо, очень полезно!
Томас Ланг,

1
Есть ли способ сделать это в IE? Поскольку это не поддерживает =>
joshuascotton

1
@joshuacotton => является функцией стрелки, вы можете заменить f => функцию fname () {...} на функцию (f) {вернуть функцию fname () {...}}
aljgom

1
Это очень полезно и работает как шарм. Это должен быть принятый ответ.
Кунал Парех

77

используйте этот код

window.onhashchange = function() { 
     //code  
}

с помощью jQuery

$(window).bind('hashchange', function() {
     //code
});

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

1
Это, на мой взгляд, лучший ответ здесь. Нет плагина и минимальный код
agDev

15
Не работает с изменениями, не относящимися к хешу, которые, похоже, очень популярны, такие как те, что были реализованы Slack
NycCompSci

27
Как это лучший ответ, если он срабатывает только при наличии хеша в URL?
Аарон

1
Вы должны использовать addEventListener вместо прямой замены значения onhashchange на тот случай, если что-то еще захочет прослушать.
сломано-е

55

РЕДАКТИРОВАТЬ после небольшого исследования:

Почему-то кажется, что меня одурачила документация, представленная в документации по Mozilla. popstateСобытие (и его функции обратного вызова onpopstate) являются не срабатывает всякий раз , когда pushState()или replaceState()вызываются в коде. Поэтому оригинальный ответ не применяется во всех случаях.

Однако есть способ обойти это, исправляя функции в соответствии с @ alpha123 :

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function
};

Оригинальный ответ

Учитывая, что заголовок этого вопроса - « Как обнаружить изменение URL », ответ, когда вы хотите знать, когда меняется полный путь (а не только хэш-привязка), заключается в том, что вы можете прослушивать popstateсобытие:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Ссылка для popstate в Mozilla Docs

В настоящее время (январь 2017 г.) поддержка popstate поддерживается 92% браузеров по всему миру.


11
Невероятно, что мы все еще должны прибегать к таким взломам в 2018 году.
коза

1
Это сработало для моего варианта использования - но, как говорит @goat, - невероятно, что для него нет встроенной поддержки ...
wasddd_

1
какие аргументы? как бы настроить fireEvents?
SeanMC

2
Обратите внимание, что это также ненадежно во многих случаях. Например, он не обнаружит изменение URL-адреса, когда вы нажмете на разные варианты продукта Amazon (плитки под ценой).
thdoan

2
это не обнаружит изменения с localhost / foo на localhost / baa, если не используется location.back ()
SuperUberDuper

53

С JQuery (и плагин) вы можете сделать

$(window).bind('hashchange', function() {
 /* things */
});

http://benalman.com/projects/jquery-hashchange-plugin/

В противном случае да, вам придется использовать setInterval и проверить наличие изменений в хеш-событии (window.location.hash)

Обновить! Простой проект

function hashHandler(){
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function(){ detect() }, 100);
}

var hashDetection = new hashHandler();

я могу обнаружить изменение (window.location) и обработать его? (без jquery)
BergP

7
Вы можете @BergP, используя простой слушатель JavaScript: window.addEventListener("hashchange", hashChanged);
Рон

1
Такой короткий промежуток времени хорош для приложения? То есть браузер не слишком занят выполнением detect()функции?
Хасиб Махмуд

4
@HasibMahmud, этот код выполняет 1 проверку на равенство каждые 100 мс. Я только что проверил в своем браузере, что могу сделать 500 проверок на равенство менее чем за 1 мс. Так что этот код использует 1/50000 моей вычислительной мощности. Я бы не слишком волновался.
z5h

24

Добавьте слушателя события изменения хэша!

window.addEventListener('hashchange', function(e){console.log('hash changed')});

Или, чтобы прослушать все изменения URL:

window.addEventListener('popstate', function(e){console.log('url changed')});

Это лучше, чем приведенный ниже код, потому что в window.onhashchange может существовать только одна вещь, и вы, возможно, будете перезаписывать чужой код.

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}

1
Всплывающее состояние срабатывает только при всплывающем состоянии, а не при нажатии на него
SeanMC

8

это решение работало для меня:

var oldURL = "";
var currentURL = window.location.href;
function checkURLchange(currentURL){
    if(currentURL != oldURL){
        alert("url changed!");
        oldURL = currentURL;
    }

    oldURL = window.location.href;
    setInterval(function() {
        checkURLchange(window.location.href);
    }, 1000);
}

checkURLchange();

19
Это довольно рудиментарный метод, я думаю, что мы можем стремиться выше.
Карлес

8
Хотя я согласен с @CarlesAlcolea в том, что это кажется старым, по моему опыту, это все еще единственный способ отловить 100% всех изменений URL.
Trev14

5
@ahofmann предлагает (в редактировании, которое должно было быть комментарием) изменить setIntervalна setTimeout: «использование setInterval () через некоторое время остановит браузер, потому что он будет создавать новый вызов checkURLchange () каждую секунду. setTimeout () правильное решение, потому что оно вызывается только один раз ".
Дивибизан

Это единственное решение, позволяющее отследить все изменения URL-адресов, но потребление ресурсов заставляет меня искать другие решения.
ReturnTable

3
Или вместо того, чтобы использовать, setTimeoutкак подсказывает @divibisan, переместите setIntervalвнешнюю часть функции. checkURLchange();также становится необязательным.
aristidesfl

4

Хотя старый вопрос, проект Location-bar очень полезен.

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);
});

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex
});

locationBar.start({
  pushState: true
});

// update the address bar and add a new entry in browsers history
locationBar.update("/some/url?param=123");

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});

Это для nodeJs, нам нужно использовать browserify, чтобы использовать его на стороне клиента. Не так ли?
hack4mer

1
Нет, это не так. Работает в браузере
Рэй Бойзен,

3

Чтобы прослушать изменения URL, см. Ниже:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Используйте этот стиль, если вы собираетесь остановить / удалить слушателя после определенного условия.

window.addEventListener('popstate', function(e) {
   console.log('url changed')
});

2

Делая небольшое расширение Chrome, я столкнулся с той же проблемой с дополнительной проблемой: иногда меняются страницы, но не URL-адреса.

Например, просто перейдите на домашнюю страницу Facebook и нажмите кнопку «Домой». Вы перезагрузите страницу, но URL не изменится (стиль одностраничного приложения).

В 99% случаев мы занимаемся разработкой веб-сайтов, чтобы мы могли получать такие события от таких структур, как Angular, React, Vue и т. Д.

НО , в моем случае с расширением Chrome (в Vanilla JS) мне приходилось прослушивать событие, которое будет срабатывать при каждом «изменении страницы», которое обычно можно отследить по измененному URL-адресу, но иногда это не так.

Моим домашним решением было следующее:

listen(window.history.length);
var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here
  }

  oldLength = window.history.length;
  setTimeout(function () {
    listen(window.history.length);
  }, 1000);
}

Таким образом, в основном решение leoneckert, примененное к истории окон, будет меняться при изменении страницы в одностраничном приложении.

Не ракетостроение, а самое чистое решение, которое я нашел, учитывая, что мы здесь проверяем только целочисленное равенство, а не большие объекты или весь DOM.


Ваше решение простое и очень хорошо работает для расширений Chrome. Я хотел бы предложить использовать идентификатор видео YouTube вместо длины. stackoverflow.com/a/3452617/808901
Род Лима

2
Вы не должны использовать setInterval, потому что каждый раз, когда вы вызываете listen (xy), создается новый Interval, и вы получаете тысячи интервалов.
Stef Chäser

1
Вы правы, я заметил, что после и не изменил свой пост, я буду редактировать это. В то время я даже столкнулся с падением Google Chrome из-за утечек памяти. Спасибо за комментарий
Alburkerk

window.historyимеет максимальную длину 50 (по крайней мере, для Chrome 80). После этой точки window.history.lengthвсегда возвращается 50. Когда это произойдет, этот метод не сможет распознать любые изменения.
Коллин Кроулл

1

Посмотрите на функцию выгрузки jQuery. Он обрабатывает все вещи.

https://api.jquery.com/unload/

Событие unload отправляется элементу окна, когда пользователь уходит со страницы. Это может означать одно из многих. Пользователь мог щелкнуть ссылку, чтобы покинуть страницу, или ввести новый URL-адрес в адресной строке. Кнопки «Вперед» и «Назад» активируют событие. Закрытие окна браузера вызовет событие. Даже перезагрузка страницы сначала создаст событие выгрузки.

$(window).unload(
    function(event) {
        alert("navigating");
    }
);

2
Где содержимое Ajaxed, URL может измениться без выгрузки окна. Этот сценарий не обнаруживает изменение URL-адреса, хотя он все еще может быть полезен для некоторых пользователей, у которых есть окно выгрузки при каждом изменении URL-адреса.
Дебора

как и вопрос @ranbuch, это относится только к страницам, которые не являются одностраничными приложениями, и это событие только наблюдает за событием окна выгрузки, но не за изменением URL.
ncubica

0
window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

11
это не будет работать в контексте одностраничных приложений, так как событие unload никогда не
сработает

0

Ниже приводится ответ (со старым синтаксисом JavaScript (без функции стрелки, поддержка IE 10+)): https://stackoverflow.com/a/52809105/9168962

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  }
  window.CustomEvent = CustomEvent;
})();

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.pushState);
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.replaceState);
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));
  });
})();

0

Еще один простой способ сделать это - добавить событие click через имя класса к тегам привязки на странице, чтобы определить, когда на него щелкнули, затем вы можете использовать window.location.href, чтобы получить данные URL, которые Вы можете использовать для запуска вашего запроса AJAX на сервер. Просто и легко.


-1

Вы начинаете новый setIntervalпри каждом вызове, не отменяя предыдущий - вероятно, вы только хотели иметьsetTimeout

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.