How to detect if URL has changed after hash in JavaScript
Solution 1
In modern browsers (IE8+, FF3.6+, Chrome), you can just listen to the hashchange
event on window
.
In some old browsers, you need a timer that continually checks location.hash
. If you're using jQuery, there is a plugin that does exactly that.
Example
Below I undo any URL change, to keep just the scrolling:
<script type="text/javascript">
if (window.history) {
var myOldUrl = window.location.href;
window.addEventListener('hashchange', function(){
window.history.pushState({}, null, myOldUrl);
});
}
</script>
Note that above used
history
-API is available in Chrome, Safari, Firefox 4+, and Internet Explorer 10pp4+
Solution 2
I wanted to be able to add locationchange
event listeners. After the modification below, we'll be able to do it, like this
window.addEventListener('locationchange', function () {
console.log('location changed!');
});
In contrast, window.addEventListener('hashchange',() => {})
would only fire if the part after a hashtag in a url changes, and window.addEventListener('popstate',() => {})
doesn't always work.
This modification, similar to Christian's answer, modifies the history object to add some functionality.
By default, before these modifications, there's a popstate
event, but there are no events for pushstate
, and replacestate
.
This modifies these three functions so that all fire a custom locationchange
event for you to use, and also pushstate
and replacestate
events if you want to use those.
These are the modifications:
(() => {
let oldPushState = history.pushState;
history.pushState = function pushState() {
let ret = oldPushState.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
let oldReplaceState = history.replaceState;
history.replaceState = function replaceState() {
let ret = oldReplaceState.apply(this, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
window.addEventListener('popstate', () => {
window.dispatchEvent(new Event('locationchange'));
});
})();
Note, we're creating a closure, to save the old function as part of the new one, so that it gets called whenever the new one is called.
Solution 3
window.onhashchange = function() {
//code
}
window.onpopstate = function() {
//code
}
or
window.addEventListener('hashchange', function() {
//code
});
window.addEventListener('popstate', function() {
//code
});
with jQuery
$(window).bind('hashchange', function() {
//code
});
$(window).bind('popstate', function() {
//code
});
Solution 4
EDIT after a bit of researching:
It somehow seems that I have been fooled by the documentation present on Mozilla docs. The popstate
event (and its callback function onpopstate
) are not triggered whenever the pushState()
or replaceState()
are called in code. Therefore the original answer does not apply in all cases.
However there is a way to circumvent this by monkey-patching the functions according to @alpha123:
var pushState = history.pushState;
history.pushState = function () {
pushState.apply(history, arguments);
fireEvents('pushState', arguments); // Some event-handling function
};
Original answer
Given that the title of this question is "How to detect URL change" the answer, when you want to know when the full path changes (and not just the hash anchor), is that you can listen for the popstate
event:
window.onpopstate = function(event) {
console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};
Reference for popstate in Mozilla Docs
Currently (Jan 2017) there is support for popstate from 92% of browsers worldwide.
Solution 5
With jquery (and a plug-in) you can do
$(window).bind('hashchange', function() {
/* things */
});
http://benalman.com/projects/jquery-hashchange-plugin/
Otherwise yes, you would have to use setInterval and check for a change in the hash event (window.location.hash)
Update! A simple draft
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();
Related videos on Youtube
Comments
-
AJ00200 about 2 years
How can I check if a URL has changed in JavaScript? For example, websites like GitHub, which use AJAX, will append page information after a # symbol to create a unique URL without reloading the page. What is the best way to detect if this URL changes?
- Is the
onload
event called again? - Is there an event handler for the URL?
- Or must the URL be checked every second to detect a change?
-
Redzarf about 5 yearsFor future visitors, a new answer by @aljgom from 2018 is the best solution: stackoverflow.com/a/52809105/151503
- Is the
-
BergP almost 11 yearscan I detect change of (window.location) and handle it? (without jquery)
-
NPC about 10 yearsThis, as I understand, works only for the change of the part after the # sign (hence the event name)? And not for full URL change, as seems to be implied by the question's title.
-
Ron almost 10 yearsYou can @BergP, Using the plain javascript listener:
window.addEventListener("hashchange", hashChanged);
-
Hasib Mahmud almost 10 yearsIs such short time interval good for the app? That is, doesn't it keep the browser too busy in executing
detect()
function? -
Neha Choudhary over 9 years@NPC Any handler for full URL change(without anchor tag)?
-
Mark Bolusmjak over 9 years@HasibMahmud, that code is doing 1 equality check every 100ms. I just benchmarked in my browser that I can do 500 equality checks in under 1ms. So that code is using 1/50000th of my processing power. I wouldn't worry too much.
-
Deborah about 8 yearsWhere content is Ajaxed in, the url may change without the window being unloaded. This script does not detect a url change, although it may still be helpful for some users who do have a window unload on every url change.
-
ncubica almost 8 yearsthis will not work in the context of single page applications since the unload event will never trigger
-
ncubica almost 8 yearssame as @ranbuch question, this is specific only for pages that are not single page application, and this event is only watching the unload window event, not the url change.
-
Nick Mitchell over 7 yearsThis must be marked the answer. It's one line, uses browser event model and doesn't rely on endless resource consuming timeouts
-
Neithan Max over 7 yearsThis is a rather rudimentary method, I think we can aim higher.
-
Trev14 almost 7 yearsAlthough I agree with @CarlesAlcolea that this feels old, in my experience it is still the only way to catch 100% of all url changes.
-
NycCompSci over 6 yearsDoesn't work on non-hash url changes which seems to be very popular such as the one implemented by Slack
-
Anand Singh about 6 yearsIt's for nodeJs, we need to use browserify to use it client-side. Don't we?
-
Ray Booysen about 6 yearsNo it isn't. Works in the browser
-
divibisan about 6 years@ahofmann suggests (in an edit that should have been a comment) changing
setInterval
tosetTimeout
: "using setInterval() will bring the Browser to a halt after a while, because it will create a new call to checkURLchange() every second. setTimeout() is the correct solition, because it is called only once." -
ReturnTable about 6 yearsThis has been the only solution to catch all the URL changes but the resource consuming is forcing me to seek some other solutions.
-
Aaron almost 6 yearsHow is this the best answer if this is only triggered when there is a hash in the url?
-
goat over 5 yearsUnbelievable that we must still resort to such hacks in 2018.
-
wasddd_ over 5 yearsThis worked for my use case - but just like @goat says - it's unbelievable that there's no native support for this...
-
Rod Lima over 5 yearsYou solution is simple and works very well for chrome extensions. I would like to suggest to use the YouTube video id instead of length. stackoverflow.com/a/3452617/808901
-
SeanMC over 5 yearswhat arguments? how would I set up fireEvents?
-
SeanMC over 5 yearspop state only triggers when you pop a state, not push one
-
eslamb over 5 yearsGreat, what I needed Thanks
-
Stef Chäser over 5 yearsyou shouldn't use setInterval because each time you call listen(xy) a new Interval is created and you end up with thousands of intervals.
-
Alburkerk over 5 yearsYou are right I noticed that after and didn't change my post, I will edit that. Back in the time I even encountered a crash of Google Chrome because of RAM leaks. Thank you for the comment
-
thdoan about 5 yearsNote that this is also unreliable in many cases. For example, it won't detect the URL change when you click on different Amazon product variations (the tiles underneath the price).
-
Sjeiti almost 5 yearsYou rarely need timeout events: use mouse- and keyboardevents for checking.
-
joshuascotton almost 5 yearsIs there a way to do this in IE? As it doesn't support =>
-
Thomas Lang almost 5 years@joshuascotton yes there is! I'll try and add it in the answer here
-
aljgom almost 5 years@joshuacotton => is an arrow function, you can replace f => function fname(){...} with function(f){ return function fname(){...} }
-
aristidesfl almost 5 yearsOr instead of using
setTimeout
like @divibisan suggests, move thesetInterval
outside of the function.checkURLchange();
also becomes optional. -
Kunal Parekh over 4 yearsThis is very helpful and works like a charm. This should be the accepted answer.
-
SuperUberDuper over 4 yearswhat if the path changes, not the hash?
-
SuperUberDuper over 4 yearsthis wont detect a change from localhost/foo to localhost/baa if not using location.back()
-
phihag over 4 years@SuperUberDuper If the path changes because the user initiated a navigation / clicked a link etc., then you will only see a
beforeunload
event. If your code initiated the URL change, it knows best. -
Diego Fortes over 4 yearsI agree that from all the answers this was the only way I was able to catch all of the URL changes.
-
Haritsinh Gohil over 4 yearsYour example works perfectly, all others on internet suggesting to use
hashchange
, but i don't use hash in my url, i just want to add listener for url change, thanks for sharing. -
Collin Krawll over 4 years
window.history
has a max length of 50 (at least as of Chrome 80). After that point,window.history.length
always returns 50. When that happens, this method will fail to recognize any changes. -
broken-e over 4 yearsYou should be using addEventListener instead of replacing the onhashchange value directly, in case something else wants to listen as well.
-
Alberto S. over 4 yearswhat about if you want to monitor any new or update on the url GET params? thanks
-
JulienD over 4 yearsDoesn't it send
'locationchange'
twice then? Once when we fire it here, and once when the URL changes? -
aljgom about 4 years@JulienD 'locationchange' is a custom event, it doesn't exist without this code. It doesn't get fired by default with a url change without this modification
-
Operator almost 4 yearsThis only works when navigating with the browsers back and forward buttons, ie completely useless in many cases.
-
Seph Reed over 3 yearsThis didn't work for my project. It comes pre-bundled and makes a lot of assumptions about what bundler you are using.
-
Gel over 3 yearswhats
fireEvents
? -
ajax333221 almost 3 yearsyou can get rid of globals
function checkURLchange(old_url){ var temp; temp=window.location.href; if(temp!=old_url){ fireChangeToBoard(); } old_url=temp; setTimeout(function(){ checkURLchange(old_url); }, 1000); }
-
aljgom over 2 yearsThe code you wrote for the function callbacks is essentially what
addEventListener
anddispatchEvent
do. You save callbacks usingaddEventListener(eventType, function)
, and when you dispatch an event, all the functions get called -
Luis Lobo over 2 yearsIf I was going to use something today for routes I would use crossroads.js
-
Monday Fatigue over 2 years
pageshow
for user activated history navigation -
Kevin Farrugia over 2 yearsUnbelievable that we must resort to such hacks in 2022.
-
Isaac Weingarten about 2 yearsI have a spa svelte with html5 routing this answer was the only answer that worked for me, because html5 routing is not hash-based routing so the hash event didn't work in my case, thank you
-
szaman about 2 yearsis this seriously the 2022 answer? ugh
-
Maciej Krawczyk about 2 yearsIf you have full control of the app it would be better to add a custom pushState function that does it and call it instead, rather than modifying the native function.