2

I want to add and event listener, I want the function the event listener calls to be bound to the calling scope and I want to be able to remove the listener at some arbitrary date in the future.

The obvious thing doesn't work:

function Thing(){
    this.thingINeed = "important!"
}

// the function that does the thing. 
Thing.prototype.handlerFunction = function(e){
    console.log(this.thingINeed)
    e.preventDefault;
}

// do the binding.
window.document.body.addEventListener('click', this.handlerFunction.bind());

// sometime later...this is my best guess. The event listener remains active. 
window.removeEventListener('click', this.handlerFunction.bind());

// this also doesn't work: 
window.removeEventListener('click', this.handlerFunction);

So I flogged together some code that does work:

function Thing(){
    this.thingINeed = "important!"
}

Thing.prototype.handlerFunction = function(e){
    console.log(this.thingINeed);
    e.preventDefault;
}

// Where the 'magic' happens...
this.boundHandlerFunction = this.handlerFunction.bind(this);

window.document.body.addEventListener('click', this.boundHandlerFunction);

// sometime later...
window.removeEventListener('click', this.boundHandlerFunction);

MDN goes into some detail on matching event listeners with removal, but it doesn't mention .bind() and I can't find any examples of other people doing it this way. The code isn't exactly obvious without extensive commenting.

Is there a better way?

jQuery event listeners can be named, which makes them really easy to remove, but that isn't possible with vanilla?

Thanks.

crowhill
  • 2,398
  • 3
  • 26
  • 55
  • I wouldn't recommend attempting to do it at **all** with jQuery's `.bind()`, considering it was [**deprecated**](http://api.jquery.com/category/deprecated/deprecated-3.0/) in jQuery 3.0. Use `.on()` instead :) – Obsidian Age Sep 27 '17 at 19:19
  • 1
    @ObsidianAge erm. uh, `.bind()` isn't jquery in this question... so... yeah.... no. – Kevin B Sep 27 '17 at 19:20
  • If you used .bind() when adding the event, you have to store the resulting function to be able to unbind it. `foo.bind('bar') !== foo`. there's no way around this. – Kevin B Sep 27 '17 at 19:21
  • The only alternative would be to define an arrow function and store it in a var, then use that to bind the event handler. however... technically that's identical to your current solution, just with a little sugar. – Kevin B Sep 27 '17 at 19:24

1 Answers1

3

The issue boils down to - Function.prototype.bind returns a new function. It works when you set the bound function to a variable and use it in both addEventListener and removeEventListener because both are referencing the same function. The first block of code does not work because they are referencing different functions. Here is a contrived example:

function foo () {}

// does not work because foo.bind() returns a new function each time
// these functions are not the same object
document.addEventListener('click', foo.bind())
document.removeEventListener('click', foo.bind())

//does work because both reference the same function
var boundFoo = foo.bind()

document.addEventListener('click', boundFoo)
document.removeEventListener('click', boundFoo)

I can't speak much to how jQuery handles events under the hood, but there is no getting around this behavior in vanilla JS.

Damon
  • 4,216
  • 2
  • 17
  • 27
  • I figured this was the case. Is there a better way to handle the situation entirely, or would you consider this code acceptable? – crowhill Sep 27 '17 at 20:14
  • There is no better way to handle this in vanilla JS (added that to the end of my answer). The only change I would make to your code is not storing the bound function as `this.boundHandlerFunction` since there is no benefit to doing so. You can simply do `var boundHandlerFunction = ...` (similar to my example), but that's getting into code style. Both work as you have already figured out :). – Damon Sep 27 '17 at 20:16
  • 1
    I'm doing `this.boundHandlerFunction` because the listener will be unset in a different `prototype` method, but point taken. Only mentioning to provide context to my question above. Thanks for the help. – crowhill Sep 27 '17 at 20:19