this in Jquery failing [duplicate] - javascript

Using ES6 arrow functions with lexical this binding is great.
However, I ran into an issue a moment ago using it with a typical jQuery click binding:
class Game {
foo() {
self = this;
this._pads.on('click', function() {
if (self.go) { $(this).addClass('active'); }
});
}
}
Using an arrow function instead:
class Game {
foo() {
this._pads.on('click', () => {
if (this.go) { $(this).addClass('active'); }
});
}
}
And then $(this) gets converted to ES5 (self = this) type closure.
Is a way to have Traceur ignore "$(this)" for lexical binding?

This has nothing to do with Traceur and turning something off; this is simply how ES6 works. It's the specific functionality you're asking for by using => instead of function () { }.
If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn't the point of Traceur.
The way to solve this particular problem is not to use this to gain access to the clicked element, but instead use event.currentTarget:
Class Game {
foo(){
this._pads.on('click', (event) => {
if(this.go) {
$(event.currentTarget).addClass('active');
}
});
}
}
jQuery provides event.currentTarget specifically because, even before ES6, it is not always possible for jQuery to impose a this on the callback function (i.e. if it was bound to another context via bind.

Event binding
$button.on('click', (e) => {
var $this = $(e.currentTarget);
// ... deal with $this
});
Loop
Array.prototype.forEach.call($items, (el, index, obj) => {
var $this = $(el);
// ... deal with $this
});

Another case
The answer by meagar is correct and I've up-voted it.
However, there is another case:
$('jquery-selector').each(() => {
$(this).click();
})
Could be fixed as:
$('jquery-selector').each((index, element) => {
$(element).click();
})
This is a historical mistake in jQuery which puts the index, instead of the element as the first argument:
.each( function )
function
Type: Function( Integer index, Element element )
A function to execute for each matched element.
See: https://api.jquery.com/each/#each-function
Also applies to .map( function ) and .filter( function ).

(This is an answer I wrote for another version of this question, before learning it was a duplicate of this question. I think the answer pulls together the information fairly clearly so I decided to add it as a community wiki, although it's largely just different phrasing of the other answers.)
You can't. That's half the point of arrow functions, they close over this instead of having their own that's set by how they're called. For the use case in the question, if you want this set by jQuery when calling the handler, the handler would need to be a function function.
But if you have a reason for using an arrow (perhaps you want to use this for what it means outside the arrow), you can use e.currentTarget instead of this if you like:
class Game {
foo(){
this._pads.on('click', e => { // Note the `e` argument
if(this.go) {
$(e.currentTarget).addClass('active'); // Using it
}
});
}
}
The currentTarget on the event object is the same as what jQuery sets this to when calling your handler.

As Meager said in his answer on this same question If you want to write ES6, you need to write ES6 all the time,
so if you are using arrow function of ES6: (event)=>{}, then you have to use $(event.currentTarget) instead of $(this).
you can also use more nicer and cleaner way of using currentTarget as ({currentTarget})=>{},
Class Game {
foo(){
this._pads.on('click', ({currentTarget}) => {
if(this.go) {
$(currentTarget).addClass('active');
}
});
}
}
originally this idea was commented by rizzi frank in meagar's answer, and i felt it useful and i think that not all people will read that comment so i have written it as this another answer.

Related

What does $(this) refer to within jQuery submit() callback? [duplicate]

Using ES6 arrow functions with lexical this binding is great.
However, I ran into an issue a moment ago using it with a typical jQuery click binding:
class Game {
foo() {
self = this;
this._pads.on('click', function() {
if (self.go) { $(this).addClass('active'); }
});
}
}
Using an arrow function instead:
class Game {
foo() {
this._pads.on('click', () => {
if (this.go) { $(this).addClass('active'); }
});
}
}
And then $(this) gets converted to ES5 (self = this) type closure.
Is a way to have Traceur ignore "$(this)" for lexical binding?
This has nothing to do with Traceur and turning something off; this is simply how ES6 works. It's the specific functionality you're asking for by using => instead of function () { }.
If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn't the point of Traceur.
The way to solve this particular problem is not to use this to gain access to the clicked element, but instead use event.currentTarget:
Class Game {
foo(){
this._pads.on('click', (event) => {
if(this.go) {
$(event.currentTarget).addClass('active');
}
});
}
}
jQuery provides event.currentTarget specifically because, even before ES6, it is not always possible for jQuery to impose a this on the callback function (i.e. if it was bound to another context via bind.
Event binding
$button.on('click', (e) => {
var $this = $(e.currentTarget);
// ... deal with $this
});
Loop
Array.prototype.forEach.call($items, (el, index, obj) => {
var $this = $(el);
// ... deal with $this
});
Another case
The answer by meagar is correct and I've up-voted it.
However, there is another case:
$('jquery-selector').each(() => {
$(this).click();
})
Could be fixed as:
$('jquery-selector').each((index, element) => {
$(element).click();
})
This is a historical mistake in jQuery which puts the index, instead of the element as the first argument:
.each( function )
function
Type: Function( Integer index, Element element )
A function to execute for each matched element.
See: https://api.jquery.com/each/#each-function
Also applies to .map( function ) and .filter( function ).
(This is an answer I wrote for another version of this question, before learning it was a duplicate of this question. I think the answer pulls together the information fairly clearly so I decided to add it as a community wiki, although it's largely just different phrasing of the other answers.)
You can't. That's half the point of arrow functions, they close over this instead of having their own that's set by how they're called. For the use case in the question, if you want this set by jQuery when calling the handler, the handler would need to be a function function.
But if you have a reason for using an arrow (perhaps you want to use this for what it means outside the arrow), you can use e.currentTarget instead of this if you like:
class Game {
foo(){
this._pads.on('click', e => { // Note the `e` argument
if(this.go) {
$(e.currentTarget).addClass('active'); // Using it
}
});
}
}
The currentTarget on the event object is the same as what jQuery sets this to when calling your handler.
As Meager said in his answer on this same question If you want to write ES6, you need to write ES6 all the time,
so if you are using arrow function of ES6: (event)=>{}, then you have to use $(event.currentTarget) instead of $(this).
you can also use more nicer and cleaner way of using currentTarget as ({currentTarget})=>{},
Class Game {
foo(){
this._pads.on('click', ({currentTarget}) => {
if(this.go) {
$(currentTarget).addClass('active');
}
});
}
}
originally this idea was commented by rizzi frank in meagar's answer, and i felt it useful and i think that not all people will read that comment so i have written it as this another answer.

How to produce an effect when a checkbox button is checked while using jQuery? [duplicate]

Using ES6 arrow functions with lexical this binding is great.
However, I ran into an issue a moment ago using it with a typical jQuery click binding:
class Game {
foo() {
self = this;
this._pads.on('click', function() {
if (self.go) { $(this).addClass('active'); }
});
}
}
Using an arrow function instead:
class Game {
foo() {
this._pads.on('click', () => {
if (this.go) { $(this).addClass('active'); }
});
}
}
And then $(this) gets converted to ES5 (self = this) type closure.
Is a way to have Traceur ignore "$(this)" for lexical binding?
This has nothing to do with Traceur and turning something off; this is simply how ES6 works. It's the specific functionality you're asking for by using => instead of function () { }.
If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn't the point of Traceur.
The way to solve this particular problem is not to use this to gain access to the clicked element, but instead use event.currentTarget:
Class Game {
foo(){
this._pads.on('click', (event) => {
if(this.go) {
$(event.currentTarget).addClass('active');
}
});
}
}
jQuery provides event.currentTarget specifically because, even before ES6, it is not always possible for jQuery to impose a this on the callback function (i.e. if it was bound to another context via bind.
Event binding
$button.on('click', (e) => {
var $this = $(e.currentTarget);
// ... deal with $this
});
Loop
Array.prototype.forEach.call($items, (el, index, obj) => {
var $this = $(el);
// ... deal with $this
});
Another case
The answer by meagar is correct and I've up-voted it.
However, there is another case:
$('jquery-selector').each(() => {
$(this).click();
})
Could be fixed as:
$('jquery-selector').each((index, element) => {
$(element).click();
})
This is a historical mistake in jQuery which puts the index, instead of the element as the first argument:
.each( function )
function
Type: Function( Integer index, Element element )
A function to execute for each matched element.
See: https://api.jquery.com/each/#each-function
Also applies to .map( function ) and .filter( function ).
(This is an answer I wrote for another version of this question, before learning it was a duplicate of this question. I think the answer pulls together the information fairly clearly so I decided to add it as a community wiki, although it's largely just different phrasing of the other answers.)
You can't. That's half the point of arrow functions, they close over this instead of having their own that's set by how they're called. For the use case in the question, if you want this set by jQuery when calling the handler, the handler would need to be a function function.
But if you have a reason for using an arrow (perhaps you want to use this for what it means outside the arrow), you can use e.currentTarget instead of this if you like:
class Game {
foo(){
this._pads.on('click', e => { // Note the `e` argument
if(this.go) {
$(e.currentTarget).addClass('active'); // Using it
}
});
}
}
The currentTarget on the event object is the same as what jQuery sets this to when calling your handler.
As Meager said in his answer on this same question If you want to write ES6, you need to write ES6 all the time,
so if you are using arrow function of ES6: (event)=>{}, then you have to use $(event.currentTarget) instead of $(this).
you can also use more nicer and cleaner way of using currentTarget as ({currentTarget})=>{},
Class Game {
foo(){
this._pads.on('click', ({currentTarget}) => {
if(this.go) {
$(currentTarget).addClass('active');
}
});
}
}
originally this idea was commented by rizzi frank in meagar's answer, and i felt it useful and i think that not all people will read that comment so i have written it as this another answer.

Why does 'this' behave differently in a lambda than in 'function ()' using jQuery? [duplicate]

Using ES6 arrow functions with lexical this binding is great.
However, I ran into an issue a moment ago using it with a typical jQuery click binding:
class Game {
foo() {
self = this;
this._pads.on('click', function() {
if (self.go) { $(this).addClass('active'); }
});
}
}
Using an arrow function instead:
class Game {
foo() {
this._pads.on('click', () => {
if (this.go) { $(this).addClass('active'); }
});
}
}
And then $(this) gets converted to ES5 (self = this) type closure.
Is a way to have Traceur ignore "$(this)" for lexical binding?
This has nothing to do with Traceur and turning something off; this is simply how ES6 works. It's the specific functionality you're asking for by using => instead of function () { }.
If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn't the point of Traceur.
The way to solve this particular problem is not to use this to gain access to the clicked element, but instead use event.currentTarget:
Class Game {
foo(){
this._pads.on('click', (event) => {
if(this.go) {
$(event.currentTarget).addClass('active');
}
});
}
}
jQuery provides event.currentTarget specifically because, even before ES6, it is not always possible for jQuery to impose a this on the callback function (i.e. if it was bound to another context via bind.
Event binding
$button.on('click', (e) => {
var $this = $(e.currentTarget);
// ... deal with $this
});
Loop
Array.prototype.forEach.call($items, (el, index, obj) => {
var $this = $(el);
// ... deal with $this
});
Another case
The answer by meagar is correct and I've up-voted it.
However, there is another case:
$('jquery-selector').each(() => {
$(this).click();
})
Could be fixed as:
$('jquery-selector').each((index, element) => {
$(element).click();
})
This is a historical mistake in jQuery which puts the index, instead of the element as the first argument:
.each( function )
function
Type: Function( Integer index, Element element )
A function to execute for each matched element.
See: https://api.jquery.com/each/#each-function
Also applies to .map( function ) and .filter( function ).
(This is an answer I wrote for another version of this question, before learning it was a duplicate of this question. I think the answer pulls together the information fairly clearly so I decided to add it as a community wiki, although it's largely just different phrasing of the other answers.)
You can't. That's half the point of arrow functions, they close over this instead of having their own that's set by how they're called. For the use case in the question, if you want this set by jQuery when calling the handler, the handler would need to be a function function.
But if you have a reason for using an arrow (perhaps you want to use this for what it means outside the arrow), you can use e.currentTarget instead of this if you like:
class Game {
foo(){
this._pads.on('click', e => { // Note the `e` argument
if(this.go) {
$(e.currentTarget).addClass('active'); // Using it
}
});
}
}
The currentTarget on the event object is the same as what jQuery sets this to when calling your handler.
As Meager said in his answer on this same question If you want to write ES6, you need to write ES6 all the time,
so if you are using arrow function of ES6: (event)=>{}, then you have to use $(event.currentTarget) instead of $(this).
you can also use more nicer and cleaner way of using currentTarget as ({currentTarget})=>{},
Class Game {
foo(){
this._pads.on('click', ({currentTarget}) => {
if(this.go) {
$(currentTarget).addClass('active');
}
});
}
}
originally this idea was commented by rizzi frank in meagar's answer, and i felt it useful and i think that not all people will read that comment so i have written it as this another answer.

jQuery $(this).function return undefined [duplicate]

Using ES6 arrow functions with lexical this binding is great.
However, I ran into an issue a moment ago using it with a typical jQuery click binding:
class Game {
foo() {
self = this;
this._pads.on('click', function() {
if (self.go) { $(this).addClass('active'); }
});
}
}
Using an arrow function instead:
class Game {
foo() {
this._pads.on('click', () => {
if (this.go) { $(this).addClass('active'); }
});
}
}
And then $(this) gets converted to ES5 (self = this) type closure.
Is a way to have Traceur ignore "$(this)" for lexical binding?
This has nothing to do with Traceur and turning something off; this is simply how ES6 works. It's the specific functionality you're asking for by using => instead of function () { }.
If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn't the point of Traceur.
The way to solve this particular problem is not to use this to gain access to the clicked element, but instead use event.currentTarget:
Class Game {
foo(){
this._pads.on('click', (event) => {
if(this.go) {
$(event.currentTarget).addClass('active');
}
});
}
}
jQuery provides event.currentTarget specifically because, even before ES6, it is not always possible for jQuery to impose a this on the callback function (i.e. if it was bound to another context via bind.
Event binding
$button.on('click', (e) => {
var $this = $(e.currentTarget);
// ... deal with $this
});
Loop
Array.prototype.forEach.call($items, (el, index, obj) => {
var $this = $(el);
// ... deal with $this
});
Another case
The answer by meagar is correct and I've up-voted it.
However, there is another case:
$('jquery-selector').each(() => {
$(this).click();
})
Could be fixed as:
$('jquery-selector').each((index, element) => {
$(element).click();
})
This is a historical mistake in jQuery which puts the index, instead of the element as the first argument:
.each( function )
function
Type: Function( Integer index, Element element )
A function to execute for each matched element.
See: https://api.jquery.com/each/#each-function
Also applies to .map( function ) and .filter( function ).
(This is an answer I wrote for another version of this question, before learning it was a duplicate of this question. I think the answer pulls together the information fairly clearly so I decided to add it as a community wiki, although it's largely just different phrasing of the other answers.)
You can't. That's half the point of arrow functions, they close over this instead of having their own that's set by how they're called. For the use case in the question, if you want this set by jQuery when calling the handler, the handler would need to be a function function.
But if you have a reason for using an arrow (perhaps you want to use this for what it means outside the arrow), you can use e.currentTarget instead of this if you like:
class Game {
foo(){
this._pads.on('click', e => { // Note the `e` argument
if(this.go) {
$(e.currentTarget).addClass('active'); // Using it
}
});
}
}
The currentTarget on the event object is the same as what jQuery sets this to when calling your handler.
As Meager said in his answer on this same question If you want to write ES6, you need to write ES6 all the time,
so if you are using arrow function of ES6: (event)=>{}, then you have to use $(event.currentTarget) instead of $(this).
you can also use more nicer and cleaner way of using currentTarget as ({currentTarget})=>{},
Class Game {
foo(){
this._pads.on('click', ({currentTarget}) => {
if(this.go) {
$(currentTarget).addClass('active');
}
});
}
}
originally this idea was commented by rizzi frank in meagar's answer, and i felt it useful and i think that not all people will read that comment so i have written it as this another answer.

Solutions for binding JS objects for callbacks

I find that it's very common to have code like (this is TypeScript, but the equivalent JS is fairly obvious and this is really a JS problem, although one that TS could solve):
class Foo {
someField;
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Fails because `this` is not the instance of `Foo`
});
}
}
The solution, of course, is to use Function.bind() like so:
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Works as expected
}.bind(this));
}
Now the problem is that I have callbacks that must access the object instance a lot (ie, I need to be calling bind(this) on many callbacks). I've now wasted more time on bugs resulting from this than I'd like to admit. Is there some alternative? Am I missing some easier way to do this? Is there any reason that this is the default behavior as opposed to the function that we get from calling bind(this)?
One solution I know of is to do something like var me = this and then call me.someField (etc). It's a little nicer looking when I have many callbacks or nested callbacks. Although then I lose out on the iconicness of this, which I feel makes it the most clear where the member is located (on the class that I'm writing).
Typescript and ES6/ES2015 both support the "fat arrow function" syntax, which allows you to use this the way you do in most other languages - to refer to the class instance object.
e.g.
someMethod() {
doSomethingTakingACallback(() => {
this.someField;
});
}
This compiles to the following code:
Foo.prototype.someMethod = function () {
var _this = this;
doSomethingTakingACallback(function () {
_this.someField;
});
};
ES6 Arrow functions keep the this of the surrounding scope and thus do not need binding.
ES7 will (possibly) have the :: bind operator
Assign this to another variable outside of your callback method and use that.
class Foo {
someField : any;
someMethod() {
var that = this;
doSomethingTakingACallback(function () {
// use that.someField;
});
}
}

Categories