javascript passing function as parameter - javascript

If I have this:
$(SomeID).on({
click: function () { SomeFunction1(); },
mouseenter: function () { SomeFunction2(); },
mouseleave:function () { SomeFunction3(); }
}, '.SomeClass');
I can rewrite it as
$(SomeID).on({
click: SomeFunction1,
mouseenter: SomeFunction2,
mouseleave: SomeFunction3
}, '.SomeClass');
But what if I need to pass some parameter like this:
$(SomeID).on({
click: function () { SomeFunction1($(this)); },
mouseenter: function () { SomeFunction2($(this).index()); },
mouseleave: function () { SomeFunction3($(this).index()); }
}, '.SomeClass');
Is there an alternative?
Thanks.

As #Jashwant says, the same this would be used in the function anyway, so it's the one value you don't need to worry about (in your example).
Note that you could do as you describe if you needed to, it's easy for static values, and is called currying. A javascript example would be: http://www.dustindiaz.com/javascript-curry/

You should modify implementation of SomeFunctions to get them work without parameter.
For example, if you have:
function SomeFunction2(arg) {
//do something assuming arg to be $(this).index()
}
You can write it like that:
function SomeFunction2() {
var arg = $(this).index();
//do exactly the same
}
After doing that for all three callbacks, you can use your second code sample to bind them.

The meaning of this inside a javascript function does not depend on the lexical scope the function was defined in – for example, the following alerts "Hello, World!", event if this.name is not defined when greet is created
var x = { name: 'World' };
var greet = function() { alert('Hello, ' + this.name); };
x.greet = greet;
x.greet();
the following too alerts "Hello, World!":
var x = { name: 'World' };
var y = { name: 'Dude', greet: function() { alert('Hello, ' + this.name); } };
x.greet = y.greet;
x.greet();
Behind the scenes, what goes on is similar to:
var greet = function() { alert('Hello, ' + this.name); };
greet.call({ name: 'World' });
So you can safely mix your #2 and #3 snippets.
BTW:
most jQuery event handlers get called with a reference to the jQuery event object as the first parameter, so if you find how this works tricky (or if you fear you'll have to explain to each one of you colleagues), you can also use event.delegateTarget instead of this.
See for example:
$(document).click(function(evt){ alert (evt.delegateTarget === this); });

Related

why can't i call one method from another inside an object

pardon my javascript ignorance: Why can't i do something like this in javascript? Running this tells me that theCalled is not defined. the order of the functions doesn't matter of course.
var myObj = {
theCaller: function() {
console.log('The Caller');
theCalled();
},
theCalled: function() {
console.log("i was called");
}
}
myObj.theCaller();
Add "this" before you call .theCalled()
var myObj = {
theCaller: function() {
alert('The Caller');
this.theCalled();
},
theCalled: function() {
alert("i was called");
}
}
myObj.theCaller();

Object has not method error in JavaScript

I am using following code but it returns following error:
Uncaught TypeError: Object [object HTMLAnchorElement] has no method 'userInput'
Here is the code jsfiddle:
var ClickEvent = function (event) {
this.ev = $('.' + event);
this.ev.on('click', function () { this.userInput(); });
};
ClickEvent.prototype = function () {
return {
userInput: function () {
console.log('user');
},
show: function () {
console.log('show');
}
};
}();
var c = new ClickEvent('event');
I am calling userInput function inside on() callback function but it returns above error.
How can I solve this problem?
The problem is the execution context(this) inside the click callback handler does not point to the ClickEvent instance, it is referencing the dom element that was clicked.
You need to use
this.ev.on('click', $.proxy(function () { this.userInput(); }, this));
Demo: Fiddle
or
var that = this;
this.ev.on('click', function () { that.userInput(); });
Demo: Fiddle
this.userInput() is nested within the callback function, and thus is scoped within it. You could externalize the this instance you need as follow:
var ClickEvent = function (event) {
var $this = this;
$this.ev = $('.' + event);
$this.ev.on('click', function () { $this.userInput(); });
};
the "this" inside your onclick function is referencing "this.ev" which is
"$('.' + event);"
not your object with "userInput" and "show".

Ensure two handlers complete in a particular order

If I have two handlers attached to the same event, is there a way to ensure that one of them will always complete before the other.
Just for this example, I'm using a timeout to simulate a long operation. But could I have something like this:
var change_label = function () {
var option = $(".selected_option a").text();
$("li:first span").text(option);
};
$("#container").on("click", "li", function () {
var self = $(this);
var t = setTimeout(function () {
$(".sel").children("li").removeClass("selected_option");
self.addClass("selected_option");
}, 1000);
});
$("#container").on("click", "li", function () {
change_label();
});
and be sure that the text wouldn't be changed (by the second handler) until the class was applied by the first?
http://jsbin.com/ayihiv/1/edit
Here's an approach you might find interesting.
jQuery's Callbacks utility allows you to do some "loose coupling". In other words, you can have the setTimeout function stimulate an action or set of actions, without knowing what those actions are; they can be specified somewhere else in the code, the only requirement being that the same Callbacks queue is within scope of both blocks of code.
This is effectively a "pub/sub" pattern, although we are using it here to do something other than to publish and subscribe.
var cb = $.Callbacks('unique');
$("#container").on("click", "li", function() {
var self = $(this);
var t = setTimeout(function() {
$(".sel").children("li").removeClass("selected_option");
self.addClass("selected_option");
cb.fire(self.find("a").text());//<<<<<< stimulate any functions currently in the callbacks list cb, and pass the required text to them.
}, 1000);
});
var change_label = function(txt) {
$("li:first span").text(txt);
};
var log_it = function(txt) {
console.log('Text changed to: ' + txt);
};
var alert_it = function(txt) {
alert('Text changed to: ' + txt);
};
cb.add(change_label);
//cb.add(log_it);
//cb.add(alert_it);
DEMO. Try uncommenting the last two lines and you will see that additional things happen when the 1 second delay is up.

Variable Scope: this.remove is not a function

this.remove() is not a function. How come?
var vehicle = function () {
return {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
this.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(vehicle.init);
Sorry for the confusion. I'm trying to call my own "remove" function. This is simply a class to manage vehicles on my page. This is the beginning of it and it will have a lot more functions than just init/remove.
this is a DOM element. To use jQuery's .remove() method, you need to wrap it in a jQuery object.
$(this).remove();
EDIT: If you were hoping to call the remove() function in the vehicle object, then call:
vehicle.remove();
Also, if you were hoping to shorten your .ready() call, you can do this:
jQuery(vehicle.init);
From the jQuery 1.4 release notes:
The jQuery().ready() technique still works in 1.4 but it has been deprecated. Please use either jQuery(document).ready() or jQuery(function(){}).
Maybe you're looking for something like this?
var vehicle = new function () {
var self = this;
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
};
this.remove = function () {
alert('test');
};
};
...or like this maybe? It's kind of hard to tell what you're going for...
var vehicle = new function () {
function remove () {
alert('test');
}
this.init = function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
remove.call(this);
});
};
};
Note - we're all somewhat confused because it's not clear which "remove" function you want to call.
The problem is that you're passing in the reference to the "init" function, but when it's called the "this" variable will refer to the window object, not the value of "vehicle". Why? Because in Javascript the "this" value depends only on how a function is called. The fact that two functions are defined in the same object has absolutely nothing to do with it.
Try doing this instead:
jQuery(function() {
vehicle.init();
});
When you call the "init" function that way — by explicitly referencing it as a property of the "vehicle" object — then Javascript will bind "this" to the value of "vehicle".
edit oh wait I just noticed that you're also going to have to revise your "init" function, because that code inside the "click" handler is going to be called by jQuery in such a way as to bind "this" in that context to the affected element. Thus if you want to keep the "vehicle" reference around, you'd do this:
init: function () {
var originalThis = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
originalThis.remove();
});
},
Since you said you're trying to call your own remove function, here's how to do it:
var vehicle = (function () {
return {
init: function () {
var that = this; // step one
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
}()); // step zero - wrap the immediate invocation in parens
jQuery(function () {
vehicle.init(); // step two
);
var vehicle = function () {
return {
init: function () {
var self = this;
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
self.remove();
});
},
remove: function () {
alert('test');
}
}
}();
jQuery().ready(function() {
vehicle.init();
});
When invoking a function as a method "this" refers to the object that is invoking it. In jQuery the function passed is invoked as a method of the html element so "this" becomes the element.
To make sure you are refering to the correct object you'll need to create a reference to the original object.
var vehicle = function () {
var that = {
init: function () {
jQuery('.vehicle-year-profile .options .delete').bind('click', function (e) {
e.preventDefault();
that.remove();
});
},
remove: function () {
alert('test');
}
}
return that;
}();
jQuery().ready(vehicle.init);

How do you add objects to a javascript namespace?

var Test = (function() {
return {
useSub: function () {
this.Sub.sayHi();
},
init: function () {
$(document).ready(this.useSub);
}
};
})();
Test.Sub = (function () {
return {
sayHi: function () {
alert('hi');
}
};
})();
Test.useSub(); // works
Test.init(); // explodes
Above I am trying to create a Test namespace and add an object Sub to it. I was doing fine until I tried using the object in jQuery. The error is "Uncaught TypeError: Cannot call method 'sayHi' of undefined". If there is a better way to do this, I am open to it.
Edit:
Obviously this was demo code. In my real application the solution that I went with because I think it is the most clear is this one:
var Namespace (function () {
return {
init: function () {
$(document).ready(function() {
Namespace.onReady();
}
},
onReady: function() {
alert('Now I am back in the Namespace scope. Proceed as planned');
}
};
})();
Edit2: All jQuery callbacks seem to require they are used in this manner or else the scoping is screwed up.
I think it is a scope problem. If you do
$(document).ready(this.useSub);
then this.useSub will be executed in the window scope (so inside the function, this refers to the window object) and there doesn't exist a Sub attribute.
Try:
init: function () {
var obj = this;
$(function(){obj.useSub()});
}
For some reason it does not work using $(document).ready(function(){obj.useSub()}); but it works with the $() shortcut.
Here is one way
var Test = {
useSub : function () {
Test.Sub.sayHi();
},
init: function () {
$(document).ready(Test.useSub);
},
Sub: {
sayHi: function () {
alert('hi');
}
}
};
in this line:
$(document).ready(this.useSub);
you're passing a reference to a function and the scope is lost- when the function runs, this no longer means Test.

Categories