0

I have this object:

var eventDisplay = {
  eventTable: $('.table-event > table'),
  archiveButton: $('table-event__archive-button'),
  deleteButton: $('table-event__delete-button'),
  checkedCheckboxes: $('.table-event input[type=checkbox]'),
  countChecked: function() {
    return this.checkboxes.filter(':checked').length;
  },
  selectionChanged: function() {
    if (this.countChecked() > 0) {
      deleteButton.removeClass("disabled");
      archiveButton.removeClass("disabled");
    } else {
      deleteButton.addClass("disabled");
      archiveButton.addClass("disabled");
    }
  },
  attachCheckboxHandler: function() {
    this.checkboxes.on('click', this.selectionChanged);
  }
}

When I call eventDisplay.attachCheckboxHandler(), the this in attachCheckboxHandler is refering to the object as I expected. However, as it calls selectionChanged, this refers to the checkbox instead. But I want this within the selectionChanged to refer to eventDisplay. Is it possible to do that? If so, how?

davidchoo12
  • 1,261
  • 15
  • 26

3 Answers3

0

This is because the this keyword in an event function relates to the caller of the function, not necessarily the overall instance (as you discovered).

For example, the event might be triggered like the following:

// checkbox has been selected
checkbox.selectionChanged.call();

So then in your callback, this will be the checkbox instance.

Since you are assigning the main object to a variable, you should just be able to reference that in your callback:

var eventDisplay = {
  selectionChanged: function() {
    if (eventDisplay.countChecked() > 0) { <-- note here
      deleteButton.removeClass("disabled");
      archiveButton.removeClass("disabled");
    } else {
      deleteButton.addClass("disabled");
      archiveButton.addClass("disabled");
    }
  }
}
Steve
  • 9,335
  • 10
  • 49
  • 81
0

The context (this) is changed under very specific circumstances; normally, when you invoke a method (function that is set as an object property) using the dot notation. For example, foo.bar() will set this to foo.

If you don't call a function using the dot notation, you typically do not change the context. For example, baz = foo.bar; baz() looks equivalent to the above, but will not set this to foo (it will leave it unchanged).

The exact same thing is happening in your code, but instead of a local variable assignment, you have a parameter passing: function quux(baz) { baz(); }; quux(foo.bar) assigns foo.bar to baz, just like above.

What you can do is bind a new context to a function: baz = foo.bar.bind(foo); baz() will set the context to foo every time baz is invoked. Alternately, and equivalently, you can bind baz to a wrapper function that will invoke foo.bar() properly: baz = function() { foo.bar(); }; baz();.

How this translates to your example:

this.checkboxes.on('click', this.selectionChanged.bind(this));

(For completeness' sake, besides method calling and bound function calling, the context is also set when new operator is used, and with call and apply functions)

Amadan
  • 191,408
  • 23
  • 240
  • 301
0

A common practice is to declare your object as a function and create an alias for it (I use thiz), which you use on your methods. Finally you return the actual object where you expat only the functions you want. Now all the thiz refer to the same object.

var myObj = function() {
    var thiz = this;
    var method1 = function (args) {
        // use thiz here
    }
    // define more methods of you want, such as method2...

    return {
        method : method1,
        anotherMethod : method2
    };
}