Search code examples
javascriptjqueryhashchange

Hashchange event listener listens before event handler is attached to event


I have the following code

console.info('checking if url contains hash');
if (!location.hash) {
  console.warn('if-block replacing hash to empty!');
  location.href = '#';

  // Running route() to trigger Router.otherwise()
  console.info('running route() from if-block(hash doesn\'t exist)');
  route(location.hash.slice(1));
} else {
  // URL contains a hash. Running Router.when() to load template
  console.info('running route() from else-block(hash already present in url)');
  route(location.hash.slice(1));
}

// Bind hashchange-event to window-object... duh
console.info('binding hashchange event');
$(window).on('hashchange', function () {
  console.info('running route() from hashchange');
  route(location.hash.slice(1));
});

Clearly, the event listener is attached after the else-if block. My console's output verifies this

2015-02-13 12:51:14.281 main.js:69 checking if url contains hash
2015-02-13 12:51:14.284 main.js:71 if-block replacing hash to empty!
2015-02-13 12:51:14.290 main.js:75 running route() from if-block(hash doesn\'t exist)
2015-02-13 12:51:16.677 main.js:84 binding hashchange event
2015-02-13 12:51:16.678 main.js:86 running route() from hashchange

It looks like the event listener somehow picks up the previously triggered hashchange event.
This only occurs when the url is example.com, not example.com/#. If / or /# is missing from the url when you enter the site this phenomenon triggers.

Edit: The problem I'm experiencing is that the hashchange event listener is triggering before it's even listening to hash changes. I'm wondering if that's normal.

Another edit: An even clearer example showing that when I first run getEventListeners(window), no listeners are attached to the hashchange event. Despite that, when I do add the event listener, it picks up a previously triggered hashchange console logs

Am I missing something here or what's going on?
Is there any way to bypass this?


Solution

  • Let's simplify your code to

    location.hash = 'foobar';
    window.onhashchange = function() {
      document.body.innerHTML = 'You should not see me. But you do :(';
    };

    To fix the problem, you can use setTimeout to delay the addition of the event handler:

    location.hash = 'foobar';
    setTimeout(function(){ // Delayed code
      window.onhashchange = function() {
        document.body.innerHTML = 'You should not see me. And you do not :)';
      };
    }, 0);