baseprime / grapnel

The smallest JavaScript router with named parameters, HTML5 pushState, and middleware support
http://grapnel.js.org
468 stars 40 forks source link

Destroying the router, in order to stop listening to its registered routes #21

Closed PanzerKunst closed 9 years ago

PanzerKunst commented 9 years ago

Hi,

I have a modular website which instantiates several Grapnel routers, one per module.

I noticed that when one of those modules is re-created, although I re-instantiate the router via this.router = new Grapnel(), the callback for a given route is executed as many times as that module has been created.

So, basically, I wonder if there is a way to destroy a Grapnel router, so that it stops listening to its registered routes.

Regards, Christophe

baseprime commented 9 years ago

Grapnel uses window.onhashchange or window.onpopstate to trigger its route event handlers. So any subsequent routes are effectively destroyed by overwriting the events property.

this.router = new Grapnel();

this.router.destroy = function(){
    this.events = {};
}
PanzerKunst commented 9 years ago

Thanks for the answer. And doing this.events = {}; wouldn't have any side effects on code outside Grapnel?

baseprime commented 9 years ago

Yes, it would. Just creating an instance of Grapnel overwrites any listener on window.onhashchange or window.onpopstate already, but before it does it inherits any other listener already there, including any other Grapnel routers. Therefore it is the responsibility of the last-created router to delegate those window.onhashchange and window.onpopstate events. So if the last-created router is effectively destroyed, any previous routers won't get their events fired (even though their route event handlers are not destroyed). This is only the case when there are more than 1 routers on a page and the last router that is instantiated is destroyed.

There is a work-around for this, and that is to reassign the window.onhashchange and window.onpopstate events:

var router = new Grapnel(),
    routerToDestroy = new Grapnel();

routerToDestroy.destroy();

window.onhashchange = function(){
  router.trigger('onhashchange');
}

window.onpopstate = function(){
  router.trigger('navigate');
}
PanzerKunst commented 9 years ago

Thanks for the explanation. I'm still confused about something else from your first comment.

In my initial example, this refers to the instance of a JavaScript controller, so it's my own JS object. I guess that in that case, this.events from your code doesn't contain anything, and so this.events = {} will reset nothing. Or what am I missing?

baseprime commented 9 years ago

If you have create an instance of this.router = new Grapnel() then this.router.events is called with a context of this.router.

baseprime commented 9 years ago

Heads up though, I think I am going to add support for a router.destroy() method within Grapnel itself, which should solve both of your issues (destroying, and maintaining multiple active routers).

PanzerKunst commented 9 years ago

Sounds awesome :) Thank you!

baseprime commented 9 years ago

@PanzerKunst Please note that in v0.5.3 you can now just use the following code to destroy a router, even if there are more than 1 routers in an app:

this.router = new Grapnel();

this.router.destroy = function(){
    this.events = {};
}
PanzerKunst commented 9 years ago

It works great, I tested it here: http://jsfiddle.net/mp51d3pw