I have color stored in a data attribute on my button that I wanted to use in a toggle. However, when I tried to access the data information using this, no data was available. How can I keep my access to the correct this scope?
I was trying to only toggle the given color for the elements which didn't contain Skip.
html
<div>
<input id="toggleButton" type="button" value="Toggle" data-color="Red" />
</div>
<div id="toggleSet">
<div>Element</div>
<div>Skip</div>
<div>Element</div>
</div>
css
.ActivateRed{ color: red; }
js
$('#toggleButton').click(function(){
$("#toggleSet div").each(function(index,element){
if( element.innerHTML != "Skip" ){
$(element).toggleClass("Activate"+$(this).data("color"));
//^this has no data to access?
//Why am I getting undefined?
}
});
});
Here is a jsFiddle of my attempt. I keep getting Activateundefined as the class name. Why isn't this accessing my toggleButton's data?
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
}
});
});
Related
I am reading a book and it says that this inside a function is set in 4 different ways. Default, implicit, explicit binding + using a new syntax. In all cases, this is set at function call. Take a look at example.
When I call jajca(), I am expecting this to be set via default binding.. thats is when jajca() is called this is already set to the object that was created using new syntax. Yet for some reason I don't understand, this inside jajca points to window even though that via default binding, this should be pointing to the object created using new.
function jajca() {
this.musicFolderPath = "value2"; // never gets assigned to the correct this.musicFolderPath
}
function Music(){
this.musicFolderPath;
jajca(); // music.musicFolderPath is undefined
// jajca.call(this); music.musicFolderPath is correctly set
}
var music = new Music();
//music.musicFolderPath is undefined
Can you explain why this is not set as I expected? If explicit binding is used, than this points to the object created using new keyword as expected.
When I call jajca(), I am expecting this to be set via default binding.. thats is when jajca() is called this is already set to the object that was created using new syntax. Yet for some reason I don't understand, this inside jajca points to window even though that via default binding, this should be pointing to the object created using new.
The gist of the idea is that whenever you call another function, the object that this represents will be "reset". (- According to the various rules of what "this" means -- this is what your book goes into. For example, when you do obj.bar(), the value of this in bar will be obj. That's why the "this" value "stays the same" when you do this.baz(): it's just saying, let the this inside baz be equal to the current this value.)
It's a pretty in-depth topic that your book likely goes into better detail than I could write in a brief StackOverflow answer, but if you keep that in mind while you're learning, you'll understand things easier.
In this case, yes, you'd want to do jajca.call(this) to make the value of this inside jajca be what you want it to be.
I am currently working through this tutorial: Getting Started with jQuery
For the two examples below:
$("#orderedlist").find("li").each(function (i) {
$(this).append(" BAM! " + i);
});
$("#reset").click(function () {
$("form").each(function () {
this.reset();
});
});
Notice in the first example, we use $(this) to append some text inside of each li element. In the second example we use this directly when resetting the form.
$(this) seems to be used a lot more often than this.
My guess is in the first example, $() is converting each li element into a jQuery object which understands the append() function whereas in the second example reset() can be called directly on the form.
Basically we need $() for special jQuery-only functions.
Is this correct?
Yes you only need $() when you're using jQuery. If you want jQuery's help to do DOM things just keep this in mind.
$(this)[0] === this
Basically every time you get a set of elements back jQuery turns it into a jQuery object. If you know you only have one result, it's going to be in the first element.
$("#myDiv")[0] === document.getElementById("myDiv");
And so on...
$() is the jQuery constructor function.
this is a reference to the DOM element of invocation.
So basically, in $(this), you are just passing the this in $() as a parameter so that you could call jQuery methods and functions.
Yes, you need $(this) for jQuery functions, but when you want to access basic javascript methods of the element that don't use jQuery, you can just use this.
When using jQuery, it is advised to use $(this) usually. But if you know (you should learn and know) the difference, sometimes it is more convenient and quicker to use just this. For instance:
$(".myCheckboxes").change(function(){
if(this.checked)
alert("checked");
});
is easier and purer than
$(".myCheckboxes").change(function(){
if($(this).is(":checked"))
alert("checked");
});
this is the element, $(this) is the jQuery object constructed with that element
$(".class").each(function(){
//the iterations current html element
//the classic JavaScript API is exposed here (such as .innerHTML and .appendChild)
var HTMLElement = this;
//the current HTML element is passed to the jQuery constructor
//the jQuery API is exposed here (such as .html() and .append())
var jQueryObject = $(this);
});
A deeper look
thisMDN is contained in an execution context
The scope refers to the current Execution ContextECMA. In order to understand this, it is important to understand the way 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.
jQuery binds this
Execution 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.
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 constructor will also accept html strings and elements. When you pass this to the jQuery constructor, you are passing the current element for a jQuery object to be constructed with. The jQuery object then contains an array-like structure of the DOM elements matching the selector (or just the single element in the case of this).
Once the jQuery object is constructed, the jQuery API is now exposed. 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.
Yeah, by using $(this), you enabled jQuery functionality for the object. By just using this, it only has generic Javascript functionality.
this reference a javascript object and $(this) used to encapsulate with jQuery.
Example =>
// Getting Name and modify css property of dom object through jQuery
var name = $(this).attr('name');
$(this).css('background-color','white')
// Getting form object and its data and work on..
this = document.getElementsByName("new_photo")[0]
formData = new FormData(this)
// Calling blur method on find input field with help of both as below
$(this).find('input[type=text]')[0].blur()
//Above is equivalent to
this = $(this).find('input[type=text]')[0]
this.blur()
//Find value of a text field with id "index-number"
this = document.getElementById("index-number");
this.value
or
this = $('#index-number');
$(this).val(); // Equivalent to $('#index-number').val()
$(this).css('color','#000000')
I am working with Esri's Javascript Library 3.10, which is based on Dojo. I'm having an issue with scope, and despite trying different variations, I'm still having the same result. There is probably a much better way to do this, but I can't seem to figure it out.
I want to iterate through an object containing keys to a set of checkboxes, then assign an event handler using dojo/on to set a value based on the key, however, the key, "setting" inside the On(...) function is the same for all four iterations.
for (var setting in this.appSettings) {
console.log(setting); //prints four different things
if (this.hasOwnProperty(setting)) {
this.checkboxHandles[setting] =
On(this[setting], 'change', function (checked) {
//this.setValue(setting, checked)
console.log(setting) //prints the last 'setting' whenever checkbox is changed
});
}
}
So setting inside the On function is always the same value, even though the for loop is looping through four unique keys. First, why does this happen, and what are some suggestions to solving this?
That's one of the JavaScriptessentials. Closures always refer to their outer scope variables by reference. This means that the event handler references to the setting variable. However, when the for-loop continues, the setting variable is referencing the next value in the array.
The solution to this problem is what we call an IIFE or an Immediately Invoked Function Expression.
If you replace the event handler with a function that returns a function, for example:
On(appSettings[setting], 'change', function (setting) {
return function(checked) {
//this.setValue(setting, checked)
console.log(setting) //prints the last 'setting' whenever checkbox is changed
}
}(setting));
So, what happens here is that the setting variable is passed to the function wrapper, the advantage of this is that they are passed by value, not by reference.
So, if you access the setting variable from inside that function, it will refer to the locally scoped setting variable which is passed to that function.
How weird it may look, it works: http://jsfiddle.net/8sxqn53d/
An interesting slidedeck that explains this behavior can be found here.
I use this line of code to add an event listener to my div's that are created through a forloop:
for(var i in mail){
//create div
parent.addEventListener("click",function(){read_msg(mail[i].id);},false);
//append to parent
}
This is causing the problem of mail[i].id being the last id for all of them. I've read some examples of how to solve it but i find it still very confusing.
I was suggested the solution of :
(function(){read_msg(mail[this].id)}).bind(i);
But am told this is not a great solution to use, was hoping someone could explain how you get read_msg to hold the correct value of id ? It always seems a bit messy in terms of a solution.
It is because you are using a closure variable i in your event handler function.
The variable i resides in the scope of the outer function, ie there is only one instance of the variable i. When the handler method is called and i is accessed javascript will look at the scope of the handler function first, if it does not find the variable there it will look at the parent closure scopes, then it will find the variable in the parent scope.
In the parent scope the value keep changing the value of i as the loop is executing that is why all the callback have the same value for i.
The solution here is to create a local closure
for(var i in mail){
(function(myvar){
parent.addEventListener("click",function(){read_msg(mail[myvar].id);},false);
//append to parent
})(i);
}
Here what we does is we have a Immediately Invoked Function Expression, to which we are passing the value of i as parameter myvar. So each iteration in the loop will create a independent closure.
you can use Object+Array methods in most browsers to side-step the pesky loop scope "bug" in js:
function addClick(key){
//create div
parent.addEventListener("click",function(){read_msg(mail[key].id);},false);
//append to parent
}
Object.keys(mail).forEach(addClick);
since functions have scope and forEach eats functions, you don't need the extra anon wrapper when you use Array methods.
if you want to go all out new JS hotness:
function addClick(key){
parent.addEventListener("click", this.method.bind( this.elm, this.source[key].id ), false);
}
Object.keys(mail).forEach(addClick, {elm:parent, source: mail, method:read_msg });
where you invert the source object of the key to allow using objects other than "mail", elements other than "parent" to attach events upon, and methods other than "read_msg", all without having to touch the logic or use the word "return"... Basically, whatever you setup in the traditional C-style for-loop initializer, you move to this.something and re-apply the logic as the need arises.
because each anonymous function you define as an event handler with each loop iteration will share the same scope as all the others, they will all reference the same var (i) as the array address for the message you are trying to display. Because your are redefining the var i with each loop, you will always see the last message in your message array displayed on each click event because the last value assigned to i will have been the length of your "mail" array.
heres how to fix it:
var helper = function(index) {
parent.addEventListener("click", function(){read_msg(mail[index].id);},false);
}
for(var i in mail) {
helper(i);
}
After reading related questions #1 , #2
I still haven't found an answer to the following question:
Javascript can set context (i.e. set this) with: bind , call and apply.
But when I'm write an event handler:
document.getElementById('myInput').onclick = function ()
{
alert(this.value)
}
Who/What actually attaches this to the object itself ?
P.S. When using jQuery's :
$("#myInput").bind(function (){...})
there is an internal implementation of (bind, call or apply)
So when I am not using jQuery, who is doing it?
Why, the DOM/JavaScript of course, it's supposed to work that way by W3C.
Event handlers are invoked in the context of a particular object (the current event target) and are provided with the event object itself.
Source
How exactly that happens, we don't know. It's an implementation detail.
All we know is, that the semantics as defined by the W3C are achieved in some way, but which part of the browser does that and and how, that is left up to the browser developers, and they can implement it as they see fit.
To sum up all the discussions:
In general it is JavaScript that binds this to o inside a function call, when o.x() is called.
However, there are some alternative methods of calling functions (like f.apply() and f.call()) that change this behaviour.
onclick is a special case, and the method used to invoke it is unknown and depends on the DOM implementation.
The answers saying it is the DOM are wrong.
This is part of JavaScript itself, as a language. The DOM is ONLY what the name indicates "Document Object Model", which is just how HTML is represented for manipulation by using JavaScript. Objects related to the DOM follow the behavior specified by the standards, but this is implemented by using JS for it. It is the JS engine what does this, in communication with whatever layout engine is being used (Gecko, Trident, WebKit, Presto, etc.). So, if WebKit detects an event, it passes it to the JS engine as the DOM specification indicates so that it can manipulated by the JS programmer (which is why you're even asking about this, because you can work with it).
In other words, if you're writing something in JavaScript, the only engine that understands how to read and execute that is the JS engine. This engine (v8, SpiderMonkey/Jugger/Trace) will receive data from the layout engine and use it so that you can interact with it. Similarly, on the other hand, whenever you run code that affects the layout, the changes will be detected by the layout engine and it will change the layout so that the user perceives the changes: even if the JS code might have initiated this, it is the layout engine that takes care of the layout.
What "this" is when you assign a function to an object, is simply wherever the function belongs to. So, if you assign a function to instance of object a, then said function will refer to a whenever you use "this" inside of it.
If you wanted to think of it in implementation terms, think of it this way:
Whenever you are calling a method, you do so by first telling an instance that you want to call a method with N parameters. This instance calls the method but adds itself into the context, as "this".
In Python this is done more explicitly by making the first parameter of all instance methods the instance itself. Here it is the same, but the instance is passed implicitly instead of explicitly.
Remember, the instance owns the method. When you do "document.getElementById('something')" the call returns an object (which happens to be an HTMLElement object that is part of the DOM, but that's coincidental to how JS interacts with the DOM), and then you are assigning the function as the property click.
Then, whenever you call the method, the JavaScript engine passes the instance by default, just like it passes other variables (like arguments is also generated without you doing anything, also done by the JS engine which implements the ECMAScript standard).
I would recommend checking pages 63:
"The this keyword evaluates to the value of the ThisBinding of the current execution context."
but most importantly, page 68 "Function calls"
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
In your example, of an onclick handler it's perfectly straight forward: a DOM element is an object, you're defining the onclick property to be a function. That function effectively becomes a method of that DOMElement/object.When that object is clicked, the function is called as a method of that element, so this points to its owner, the element.
Put simply, the context in which the function executes is the same as the context in which is was created (again: in your example as a method of a DOM Element), unless a reference to a function object is assigned to another object, or when that function object is invoked in another context using call or apply & co.There's a little more to it than this, of course: as I hinted at above, functions are objects themselves and are said to be loosely coupled to their "owner". Well, actually they don't have an owner as such, each time a function is called, its context is determined:
var foo = someObject.someFunction;//reference to method
someObject.someFunction();//this === someObject, context is the object preceding the function
foo();//implies [window].foo(); ==> this is window, except for strict mode
As #wroniasty pointed out, my talking about ownership might be slightly confusing. The thing is, functions are objects, they're not owned by anything. When an object is assigned a method, all that object really owns is a reference to a given function object. When that function is called via that reference, this will point to the object that owned the calling reference. When we apply that to your elem.onclick = function(){}, we see the element only owns a reference to a function expression that was declared in some scope (global, namespace-object, doesn't matter). When the click event fired, that reference will be used to call the handler, thus assigning a reference to the element to this. To clarify:
document.getElementById('foo').onclick = (function()
{//This function returns the actual handler
var that = this;//closure var
console.log(this);//logs window object
//defined in global context
return function(e)//actual handler
{
console.log(this === that);//false
console.log(this);//elem
console.log(that);//window
};
})();//IIFE
So the handler was declared in the global context, and the handler can access its the context it was declared in using that, thanks to closures (but that's another story). The point is, the event references the handler using the onclick property of the element foo. That property is a reference to a function object, so the function object sets its context to whatever object made the call.
I do hope this clears up any confusion I caused with regard to ownership of functions, and perhaps how context in JS is determined.
http://dmitrysoshnikov.com/ecmascript/chapter-3-this/#this-value-in-the-function-code
Basically, it's done by JavaScript internals.
The context is the object calling the function, e.g.
elem.onclick(); // elem === this
However:
func = elem.onclick;
func() // global === this
This really has nothing to do with the DOM as has been mentioned, but how JavaScript is designed to work when you call a function within an object.
Take this as an example:
var myObject = {
id: 1,
onclick: null
}
myObject.onclick = function() {
console.log(this.id);
}
Calling myObject.onclick() will log 1 to the console, which means myObject is its context.
Since onclick is also a property of an object, this will be the parent object, in your case an HTMLElement.
For illustration purposes, although implementations may differ, think of the following function
function f() { alert(this.name); }
as
function f(this) { alert(this.name); }
Imagine this as a secret parameter that you can override with bind, apply and call but that normally gets set to the calling object by the browser.
Example
var a = {},
b = {};
a.name = "John";
b.name = "Tom";
// "this" param added secretly
function printName( ) {
console.log( this.name )
};
a.printName = printName
b.printName = printName;
When calling the printName function the browser sets that "secret" this parameter to the calling function. In the example below this is b and so "Tom" is printed to the console.
printName( ); // global context assumed so this === window
b.printName( ); // this === b and outputs "Tom"
printName.call( a ); //this === a and outputs "John"
Further info here.