Save access to this scope

Explanation of the problem

this changed

The value of thisMDN has changed and is no longer referencing the expected element or value. Often this is because the scope has changed, and as a result so has the this reference.

this is contained in an execution context

The scope refers to the current Execution ContextECMA. In order to understand why this has changed, it is important to understand the way these execution contexts operate in JavaScript.

execution contexts bind this

When control enters an execution context (code is being executed in that scope) the environment for variables are setup (Lexical and Variable Environments – essentially this sets up an area for variables to enter which were already accessible, and an area for local variables to be stored), and the binding of this occurs.

the this binding was changed for the execution context

These contexts form a logical stack. The result is that contexts deeper in the stack have access to previous variables, but their bindings may have been altered. Every time jQuery calls a callback function, it alters the this binding by using applyMDN.

callback.apply( obj[ i ] )//where obj[i] is the current element

The result of calling apply is that inside of jQuery callback functions, this refers to the current element being used by the callback function.

For example, in .each, the callback function commonly used allows for .each(function(index,element){/*scope*/}). In that scope, this == element is true. The consequence of that, is if you expected a previous this to be available, it will be bound to a different element.

Brief explanation of this in jQuery callbacks

As shown above, jQuery callbacks use the apply function to bind the function being called with the current element. This element comes from the jQuery object’s element array. Each jQuery object constructed contains an array of elements which match the selectorjQuery API that was used to instantiate the jQuery object.

$(selector) calls the jQuery function (remember that $ is a reference to jQuery, code: window.jQuery = window.$ = jQuery;). Internally, the jQuery function instantiates a function object. So while it may not be immediately obvious, using $() internally uses new jQuery(). Part of the construction of this jQuery object is to find all matches of the selector. The jQuery object then contains an array-like structure of the DOM elements matching the selector.

When a jQuery api function is called, it will internally iterate over this array-like structure. For each item in the array, it calls the callback function for the api, binding the callback’s this to the current element. This call can be seen in the code snippet above where obj is the array-like structure, and i is the iterator used for the position in the array of the current element.

Finding a solution

It can be hard to determine what happened since jQuery tends to fail silently. .data()jQuery API is still a valid call here, it just returns undefined. As a result, the above code produces a class name of “Activate”+undefined, “Activateundefined”.

The important part to recognize here is that jQuery has changed the this binding. In order to use a previous this binding, the value must be stored in a variable. A common name for storing this is that, self, me, or in best practice, an actual description of what this represents.

The reason saving the binding works is that the callback’s execution context will be deeper in the execution context stack relative to where the binding value was saved, thus having access to that stored value.

jsFiddle Demo

$('#toggleButton').click(function(){
 var toggleBtn = this;
     //^ store this into the toggleBtn variable
 $("#toggleSet div").each(function(index,element){
                           //^ binds `this` to the current element
  if( element.innerHTML != "Skip" ){
       //^ we could have used `this.innerHTML` here
     $(element).toggleClass("Activate"+$(toggleBtn).data("color"));
                                        //^ re-use the stored `this` value
  }
 });
});

Leave a Comment