So i need to get the inner text of a given element through a Jquery event, and then set this text into a member of my class e.g.
myClass = function ()
{
this.index = 0;
this.onNavElementClick = function ()
{
this.index = parseInt(this.text());
}
this.myMain = function ()
{
$("nav#wow-so-much-inspiration").on("click", "a", this.onNavElementClick);
}
}
myObject = new myClass();
myObject.myMain();
HTML:
<nav id="wow-so-much-inspiration">
1
2
3
</nav>
But this won't work because of the two different scopes inside the onNavElementClick() function... And i don't like the idea of doing _this = this, i'm pretty sure there is a right way to do that without doing MacGyver coding.
jQuery event handlers also take the event object (including the target on which the event was triggered) as a first argument. Then you can use $.proxy to have your event handler bound to the outer this.
Something like this:
this.onNavElementClick = $.proxy(function (e)
{
this.index = parseInt($(e.target).text());
}, this);
You could make use of the bind method, but I believe this has been proven to have minor performance implications.
Example - which is essentially what the $.proxy answer does.
var myClass = function ()
{
this.index = 0;
this.onNavElementClick = (function (event) {
this.index = parseInt( $(event.target).text() );
}).bind(this);
this.myMain = function ()
{
$("nav#wow-so-much-inspiration").on("click", "a", this.onNavElementClick);
};
}
var myObject = new myClass();
myObject.myMain();
Another option is to use call or apply with a simple wrapper function.
var myClass = function ()
{
this.index = 0;
this.onNavElementClick = function (event)
{
this.index = parseInt( $(event.target).text() );
};
this.myMain = function ()
{
var self = this;
$("nav#wow-so-much-inspiration").on("click", "a", function (event) {
self.onNavElementClick.call(self, event);
});
};
}
var myObject = new myClass();
myObject.myMain();
Define onNavElementClick as a var within the my class constructor and reuse the function as needed.
var onNavElementClick = function(){...};
Related
Let's assume we have the following code:
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
Let's make two instances of the class:
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
Now if we console.log() the values we see that:
instance1.getValue(); // 10
instance1.getValue2(); // 20
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
console.log(instance1.getValue());
console.log(instance1.getValue2());
Why is that happening? It looks obviously that the _this variable gets the latest created instance properties. How to fix that? I need to keep a copy of this. Thanks!
Edit:
Here's the real situation
var HoverEffects = (function(){
var _this;
function HoverEffects($nav){
_this = this;
this._$activeNav = $nav.siblings('.active_nav');
this._$hoverableLis = $nav.find('>li');
this._$activeLi = $nav.find('>li.active');
if(!$nav.length || !this._$hoverableLis.length || !this._$activeNav.length || !this._$activeLi.length) return;
if(this._$activeNav.hasClass('bottom')){
this._$activeNav.align = 'bottom';
this._$activeLi.cssDefault = {
left: this._$activeLi.position().left,
width: this._$activeLi.width()
};
}
else if(this._$activeNav.hasClass('left')){
this._$activeNav.align = 'left';
this._$activeLi.cssDefault = {
top: this._$activeLi.position().top,
height: this._$activeLi.height()
};
}
else{
return;
}
this._$hoverableLis.hover(
function(){
// How to set the correct this inside this function?
if(this._$activeNav.align === 'bottom'){
this._$activeNav.css({
left: $(this).position().left,
width: $(this).width()
});
}
else if(this._$activeNav.align === 'left'){
this._$activeNav.css({
top: $(this).position().top,
height: $(this).height()
});
}
},
function(){
// Same here, wrong this
this._$activeNav.css(this._$activeLi.cssDefault);
}
);
}
return HoverEffects;
})();
var sideNavHoverMagic = new HoverEffects($('#side-navigation'));
var primaryNavHoverMagic = new HoverEffects($('#primary-navigation'));
Why is that happening?
Every time you call new MyClass, _this = this gets run. The second time overrides the first time.
So _this refers to new MyClass(20), which means that when you call getValue2 from any MyClass instance, 20 will be returned because all MyClass instances are referring to the same _this value.
Based on commentary on the Question:
If you're attempting to pass a function bound to the appropriate context there are a variety of ways to make sure that this refers to the right object. Before continuing, please read "How does the 'this' keyword work?", because there's no reason for me to repeat all of it here.
If you're binding event callbacks such as in a constructor:
function Example(something) {
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
The callback will have the wrong this value because it's not being called as this.callback, it's just being called as:
fn = this.callback;
fn(); //no reference to this
You can get around this in a number of ways.
Function.prototype.bind
You can bind the callback for every instance on their respective instance. This is very concise:
function Example(something) {
//generate a new callback function for each instance that will
//always use its respective instance
this.callback = this.callback.bind(this);
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
that = this
You can create the callback (closure) within the constructor and reference a variable inside the constructor.
function Example(something) {
//every Example object has its own internal "that" object
var that = this;
this.callback = function () {
//this function closes over "that"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
() => {} (Fat Arrow Syntax)
If you're using ES2015 you can use "fat arrow" syntax for creating lambdas that don't create a new context:
function Example(something) {
this.callback = () => {
//the callback function doesn't create a new "this" context
//so it referes to the "this" value from "Example"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
In JavaScript I am looking for a way to use bind() on an anonymous and async function.
Example:
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
I got this working by making the functions non-anonymous, but I think it makes my code look ugly. Especially after having 4 different anonymous functions in a row.
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
function _load(object) {
this.object = object;
}
var _loadThis = _load.bind(this);
var query = new Parse.Query("SomeObject");
query.get(id).then(_loadThis);
}
};
Is there a better way?
Well it's not necessarily "better", but you can call .bind() directly after the closing brace of your function instantiation expression:
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
}.bind(this));
A function instantiation expression gives you a function object reference, so putting a . after it and calling bind makes sense. What gets passed to the .then function, therefore, is the return value from the call to .bind.
This syntax is not correct:
exports.foo.prototype = {
load = function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
The prototype is an object who's properties are defined as load: function() {}, not load = function() {}.
It should be:
exports.foo.prototype = {
load: function(id) {
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
this.object = object; // this is the wrong this
});
}
};
A simple way to do it is to declare a variable to the correct 'this' and use closuers to keep a reference to it.
exports.foo = function () {};
exports.foo.prototype = {
load : function(id) {
var self = this;
var query = new Parse.Query("SomeObject");
query.get(id).then(function(object) {
self.object = object; // this is the wrong this
});
}
};
I have following piece of code. What is does is when use hover's over a link then it consoles this.
var Mod = function () {
$('.link').hover(this.hover, this.out);
};
Mod.prototype = function () {
var hover = function () {
console.log(this);
},
out = function () {
console.log(this);
};
return {
hover: hover,
out: out
}
}();
In my above code this references to $('.link') element but I want to use this for current object. So to achieve this, I can modify constructor to following.
var Mod = function () {
var self = this;
$('.link').hover(function () {
self.hover();
}, function () {
self.out();
});
};
This works fine but the constructor looks messy now. Second way is to use jquery $.proxy() again this will make my constructor look messy.
My question is that how can I pass this which references to current object to rest of the functions inside object while using the jquery's hover function as i am using it now in first example above?
The code in your question looks perfect to me. You're calling hover and out in the correct context making this valid and pointing to the instance of Mod inside those functions.
this in a member function should always point to the instance of the object, so I would keep doing it that way even though you think it's a mess. A good IDE will be able to assist you or the team with both syntax and auto-completion, which I think is more important.
Don't do this
While you could assign this to a data-member of .link it will make the code less readable and prone to error:
var Mod = function () {
$('.link').data("mod", this);
$('.link').hover(this.hover, this.out);
};
Mod.prototype = function () {
var hover = function () {
console.log($(this).data("mod"));
},
out = function () {
console.log($(this).data("mod"));
};
return {
hover: hover,
out: out
}
}();
Side note: you could simplify prototype definition and just write it like this:
Mod.prototype.hover = function() {
}
Mod.prototype.out = function() {
}
You could make it like that:
var Mod = function () {
$('.link').hover(this.listener('hover'), this.listener('out'));
};
Mod.prototype = function () {
var hover = function () {
console.log(this);
this.otherMethod();
},
out = function () {
console.log(this);
this.otherMethod();
},
listener = function(func) {
var self = this;
return function() {
self[func]();
}
},
otherMethod = function() {
console.log("That's a method of Mod.");
};
return {
hover: hover,
out: out,
otherMethod: otherMethod,
listener: listener
}
}();
Just use a helper which returns a function. Your constructor is clean, but your prototype is not so :)
I have the following code
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
} ());
In the same file I have the code to call the above function
I am trying to get to an end point where I can use the following code
$(document).ready(function () {
PROMO.Base.Init();
});
this gives the error
Cannot call method 'Init' of undefined
Now I know there are many ways to write javascript, but in this case I want to be able to call my functions, or least the Init method in the way shown above.
var PROMO = PROMO || {};
PROMO.Base = (function () {
var _self = this;
var Init = function () {
WireEvents();
};
var WireEvents = function () {
//wire up events
};
var reveal = {
Init: Init
};
return reveal;
} ());
You need to return the public facing functions. See updated code.
Working fiddle with both patterns, using IIFE and direct attribution.
Using var makes the definition private and your function is returning nothing. Use this:
PROMO.Base = {
Init: function() {
},
WireEvents: function() {
};
};
You are wrapping the definition with an IIFE(Immediately Executed Function Expression). So your PROMO.Base object will be assigned the value of that (function(){//blabla})(); returns. But your function doesn't have a return statement. By default it will return undefined.
Which is way your PROMO.Base will be undefined and you get this:
Cannot call method 'Init' of undefined
If you really want that IIFE:
var PROMO = PROMO || {};
// NEVER use _self = this inside static functions, it's very dangerous.
// Can also be very misleading, since the this object doesn't point to the same reference.
// It can be easily changed with Function.prototype.call and Function.prototype.apply
PROMO.Base = (function () {
_PROMO = {
Init : function () {
document.body.innerHTML += "itworks";
},
WireEvents : function () {
//wire up events
}
}
return _PROMO;
} ());
PROMO.Base.Init();
Update
The better and easier pattern is to simply assign the functions to PROMO.Base. Dully note you should not capitalize static functions, but only constructors. So if something is not meant to be instantiated, don't call it Init, it should be init. That is the convention.
var PROMO = {};
PROMO.Base = {};
PROMO.Base.init = function() {
console.log("this works");
};
PROMO.Base.wireEvents = function() {
console.log("this is a static function too");
};
You can attach it to the window object like ...
window.PROMO = (function($, _){
// this will access PROMO.Base
PROMO.Base = {
// inner functions here
Init:{}
};
})(jQuery, _);
Then load it as you do.
Or if you depend from jQuery
(function($){
var PROMO = {
// inner functions
Init: function(){},
WireEvents: function(){}
};
$.PROMO = PROMO;
})(jQuery);
On DOM ready
jQuery(function ($) {
var promo = $.PROMO || undefined;
promo.Base.Init();
});
I have this:
function test1()
{
this.count = 0;
this.active = 0;
this.enable = function () {this.active = 1;}
this.disable = function () {this.active = 0;}
this.dodo = function ()
{
$("html").mousemove(function(event) {
// I want to get here the "active" param value;
});
}
this.enable();
this.dodo();
}
instance = new test1();
instance.disable();
Let's say I want to check the active param of the test1 class in the commented place. How can I get it there ?
Thanks!
If you want access to all the member variables of the higher scope, you just need to save the this pointer from that scope into a local variable so you can use it inside the other scope:
function test1() {
this.count = 0;
this.active = 0;
this.enable = function () {this.active = 1;}
this.disable = function () {this.active = 0;}
var self = this;
this.dodo = function () {
$("html").mousemove(function(event) {
// I want to get here the "active" param value;
alert(self.active);
});
}
this.enable();
this.dodo();
}
instance = new test1();
instance.disable();
this.dodo = function ()
{
var active = this.active;
$("html").mousemove(function(event) {
alert(active);
});
}
When you call a function 'this' refers to the object the function was invoked from, or the newly created object when you use it together with the keyword new. For example:
var myObject = {};
myObject.Name = "Luis";
myObject.SayMyName = function() {
alert(this.Name);
};
myObject.SayMyName();
Note in JavaScript there are multiple ways to declare, define, and assign fields and methods to objects, below is the same code written more similarly to what you wrote:
function MyObject() {
this.Name = "Luis";
this.SayMyName = function() {
alert(this.Name);
};
}
var myObject = new MyObject();
myObject.SayMyName();
And yet another way to write the same thing:
var myObject = {
Name: "Luis",
SayMyName: function() {
alert(this.Name);
},
};
myObject.SayMyName();
There are also several different ways to invoke a function.