Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding execution order of the hashchange event

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:

  1. set the location hash to 1
  2. 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?

like image 795
BillyBelly Avatar asked Oct 12 '15 17:10

BillyBelly


1 Answers

Firstly, 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:

  1. 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:

  1. 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.
    1. 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.
    2. 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.

like image 51
James Thorpe Avatar answered Oct 18 '22 14:10

James Thorpe