I have a question for you regarding this code snippet:
window.location.hash=1;
$(window).on('hashchange', function() {
alert('hello');
});
The script above should do this:
- set the location hash to
1
- on any further change ->
alert('hello')
This is the problem: why is hashchange
called at the first time of the first execution? Shouldn't this script change only the hash without any alert?
How can I fix it so that it works as described?
I have a question for you regarding this code snippet:
window.location.hash=1;
$(window).on('hashchange', function() {
alert('hello');
});
The script above should do this:
- set the location hash to
1
- on any further change ->
alert('hello')
This is the problem: why is hashchange
called at the first time of the first execution? Shouldn't this script change only the hash without any alert?
How can I fix it so that it works as described?
Share Improve this question edited Oct 12, 2015 at 18:26 James Thorpe 32.2k6 gold badges75 silver badges94 bronze badges asked Oct 12, 2015 at 17:44 BillyBellyBillyBelly 5233 silver badges14 bronze badges 2- 7 The actual update of the hash might happen in the next tick of the event loop. – Felix Kling Commented Oct 12, 2015 at 17:47
-
4
Try putting the event handler in a
setTimeout()
– Rory McCrossan Commented Oct 12, 2015 at 17:50
3 Answers
Reset to default 7Firstly, you ask:
why is
hashchange
called at the first time of the first execution? Shouldn't this script change only the hash without any alert?
To answer this, we can delve into the specification. When navigating to a new fragment (ie setting document.location.hash
), the specification goes through a number of steps, one of which is:
- Traverse the history to the new entry, with the asynchronous events flag set. This will scroll to the fragment identifier given in what is now the document's address.
The specification for traversing the history goes on to say:
- If the asynchronous events flag is not set, then run the following steps synchronously. Otherwise, the asynchronous events flag is set; queue a task to run the following substeps.
- If state changed is true, fire a trusted event with the name popstate at the Window object of the Document, using the PopStateEvent interface, with the state attribute initialized to the value of state. This event must bubble but not be cancelable and has no default action.
- If hash changed is true, then fire a trusted event with the name hashchange at the browsing context's Window object, using the HashChangeEvent interface, with the oldURL attribute initialized to old URL and the newURL attribute initialized to new URL. This event must bubble but not be cancelable and has no default action.
So all of that taken together means that when you run your code, the event listener for hashchange
will be added before the code in the substeps of step 14 is run and will subsequently be fired when the hash is set.
How can I fix it so that it works as described?
To fix it, you can also queue the adding of your event listener using setTimeout(.., 0)
:
setTimeout(function() {
$(window).on('hashchange', function() {
alert('hello');
});
}, 0);
Since you add this to the queue after setting the hash, it will be added to the queue after the task queued in step 14 above, and so the event listener only gets added after the event has been fired.
use a counter inside your code:
var counter = 0;
$(window).on('hashchange', function() {
if (counter)
alert('hello');
counter++;
});
Another way to fix it without calling setTimeout would to register the 'hashchange' event and then set the value of hash.
$(window).on("hashchange",function()
{
alert('Invoked due to hash change' + window.location.hash);
});
alert('Hash Updated');
window.location.hash = "1";
Fiddle : http://jsfiddle/86829ryz/10/