admin管理员组文章数量:1333451
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?
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?
-
1
I have seen a number of questions regarding a problem where saving the value of
this
is the solution to the problem. However, I could not find a canonical answer which explained what was wrong with usingthis
in that fashion. Most answers were single line answers suggesting to storevar that = this
and move on. In order to try to reduce the amount of time people have to answer these types of questions I hoped to provide a canonical answer here. Please edit or request changes to it if anything is either inplete or in need of correction. – Travis J Commented Jun 11, 2014 at 22:10 -
I though How to access the correct
this
/ context inside a callback? was the (non-jQuery-specific) canonical question. Not sure whether it would help to merge them. – Bergi Commented Jun 11, 2014 at 22:21 -
@Bergi - I was trying to specifically address the jQuery aspect of how
this
is used with this type of structure. jQuery tends to obfuscate it a little and so I was attempting to explain the process involved. I think that the linked question from Felix addresses the non-jQuery aspect very well. – Travis J Commented Jun 11, 2014 at 22:30 -
@TravisJ -
this
in JavaScript changes in different locations. It can also be changed programmatically via.apply
or.call
. – Derek 朕會功夫 Commented Jun 11, 2014 at 23:03 -
@Derek朕會功夫 - You are correct. In fact, that is part of what jQuery obfuscates by design. Internally jQuery will call
apply
on callback functions in order to bindthis
to the current element. I outline this in the answer below. – Travis J Commented Jun 11, 2014 at 23:08
1 Answer
Reset to default 9Explanation of the problem
this changed
The value of this
MDN 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 apply
MDN.
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 monly 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 es 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 mon 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
}
});
});
本文标签: javascriptSave access to this scopeStack Overflow
版权声明:本文标题:javascript - Save access to this scope - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742353643a2458983.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论