I saw three different function definitions in jQuery. What is the correct way to use them?
Example 1:
function example() {
// code here
}
Example 2:
jQuery('#Example').click(function() {
// code here
});
Example 3:
var example = {
demo : function() {
// code here
}
}
Why should I choose which?
There is no 'best' here, as each function definition is separate and serves different purposes. Also note that only the second example has anything to do with jQuery.
Your first example is just a vanilla JS function definition, nothing special about this.
Your second is a jQuery click event handler which is used to declare logic which should be executed when the click event occurs on the selected element.
The third example is a function definition inside an object, which can be useful when using an OOP approach (as if to declare a class/model), or just passing a collection of values around in your logic.
Example 1 and 3 have nothing to do with jQuery. These are vanilla javascript snippets.
On example 2, it's recommended to use this syntax instead :
jQuery('#example_container').on('click', '#Example', function(event) {
// code here
});
This lets you delegate the event handling, which means even if you created that element after the page was loaded, the event will still be handled.
Your first example:
function example() {
// code here
}
Is the way to define a function and it is going to be available depending on its scope.
The second example:
jQuery('#Example').click(function() {
// code here
});
You are not only defining a Function but you are adding this function into the click event for a html element that has the id "Example"
The third Example:
var example = {
demo : function() {
// code here
}
}
You are defining a function inside an Object. So this function is going to be the method "demo" of the object "example" you just created.
Related
I am trying to create a basic javascript framework that you can pass different things into, including functions for it to execute later. Right now, I'm in a more simple testing phase, but I can't quite get the function calling to work. A piece of my code is here:
[My JS Fiddle][1]http://jsfiddle.net/mp243wm6/
My code has an object that holds different data, and I want to call the method later, but with data that is available at the time of creation. Here is a code snippet of the function that uses the function that is passed to the object:
clickMe : function() {
this.obj.click(function() {
this.func();
});
}
Any suggestions or things I should read are welcome.
The problem is that there're two different contexts:
clickMe : function() {
// here is one
this.obj.click(function() {
// here is another
this.func();
});
}
You can simple pass the function as parameter, like the following:
clickMe : function() {
this.obj.click($.proxy(this.func, this));
}
http://jsfiddle.net/mp243wm6/2/
The problem:
Considering your code in the JSFiddle, you have:
onClick : function() {
this.obj.click(function() {
this.func();
});
},
As noted, you have different contexts going on here.
Consider the snippet this.obj.click(function() { this.func(); }). The first this here is a reference to the framework.events object. The second this here is a reference to whatever will be this when this function get called. In the case of your JSFiddle, when this.func gets called, this is actually the DOM object that represents the <div id="test">TEST</div> node. Since it doesn't have a func function, calling func() on it causes:
Uncaught TypeError: undefined is not a function
You have to understand the following: you have to pass the correct this in which you want the function func to be called.
The solution:
A couple of ways to make it work as you would like:
1. with bind
this.obj.click(this.func.bind(this));
This way, you are telling: "call my this.func function, but make sure that it will be called using the this that I am passing as a parameter". Vanilla JS, no $.proxy stuff.
JSFiddle
2. with a copy of the reference to the actual function
onClick : function() {
var theFunctionReference = this.func;
this.obj.click(function() {
theFunctionReference();
});
},
This way, you will not rely on the value of this outside of the context of the framework.events object.
JSFiddle
The issue is that this is not bound to the correct object. I would suggest you look into Function.bind() because that creates a function with this pointing to the right thing.
Say I add to First.js:
$(document).ready(
function () {
dosomething A
});
function () {
dosomething C
});
});
and to Second.js:
$(document).ready(
function () {
dosomething B
});
});
will all 3 functions be executed after DOM is ready?
What will be the case when I register
to First.js:
$(document).ready(
A = function () {
dosomething A
});
C = function () {
dosomething C
});
});
to Second.js:
$(document).ready(
A = function () {
dosomething A
});
});
The later will override the first?
TIA
Your first example is invalid syntax. It will cause the javascript interpreter to throw an exception. You need to pass one and only one function to $(document).ready(fn). You can include multiple function calls inside the one function, but you can only pass one function to .ready().
Your second example is also a syntax error - an extra });. If that is removed, it will work and execute that one function.
Your third example in both first.js and second.js is also a syntax error. You can't put arbitrary javascript as the parameter to .ready(). It must be one function reference with proper syntax.
Now, what you may have been trying to ask if you actually provided legal syntax in your examples is that all functions you pass to .ready(fn) will be executed when the document is ready. jQuery keeps an array of all functions that have been passed and executes them all when the document becomes ready. The jQuery documentation for .ready() does not specify the calling order if .ready() has been called multiple times with multiple functions, though one could examine the source code and see what the order is likely to be.
"will all 3 functions be executed after DOM is ready?"
Yes. Each time you bind something to be executed at DomReady, jQuery will queue the function in an internal array, then execute them in the same order as they where "inserted".
The later will override the first?
Yes it will, unless you put var before the definition. JavaScript will put A in the window scope, so the next definition will override the first.
function() {
var A = 0; // this will only exist within the function
}
function() {
A = 1; // this will be added to the "global" scope (window).
}
The later will override the first?
No, you can assign multiple functions to individual events.
I'm assuming your syntax errors were unintentional. The way they are written, NO code is executed.
Yes, you can bind the same event several times without problems.
Yes, the second code will replace the value set by the first code.
I am working with a user control that has set of javascript functions that are called when an action is performed. This user control is used in a lot of places in the application.
When one of the inbuilt JS function completes execution, I need to fire a custom JS function on my page.
Is there a way for me to attach a function to be fired when another function completes execution? I don't want to update the inbuilt JS function to call this page JS function.
Hope this makes sense.
There are a couple design patterns you could use for this depending upon the specific code (which you have not shared) and what you can and cannot change:
Option 1: Add a callback to some existing code:
function mainFunction(callbackWhenDone) {
// do other stuff here
callbackWhenDone();
}
So, you can call this with:
mainFunction(myFunction);
Option 2: Wrap previous function:
obj.oldMethod = obj.mainFunction;
obj.mainFunction = function() {
this.oldMethod.apply(this, arguments);
// call your stuff here after executing the old method
myFunction();
}
So, now anytime someone does:
obj.mainFunction();
it will call the original method and then call your function.
You're basically trying to do callbacks. Since you're not mentioning what functions you're talking about (as in code), the best thing to do would be basically to wrap the function, -quick and dirty- and make it work with callbacks.
That way you can pass it a Lambda (Anonymous Function) and execute anything you want when it's done.
Updated to demonstrate how to add Callbacks:
function my_function($a, $callback) {
alert($a);
$callback();
}
my_function('argument', function() {
alert('Completed');
});
The ugliest and best solution is to monkey-patch the built-in function. Assume the built-in function is called "thirdParty":
// first, store a ref to the original
var copyOfThirdParty = thirdParty;
// then, redefine it
var thirdParty = function() {
// call the original first (passing any necessary args on through)
copyOfThirdParty.apply(this, arguments);
// then do whatever you want when it's done;
// custom code goes here
customFunction();
};
We've essentially created a modified version of the built-in function without ever touching the original version.
Since Javascript is highly dynamic you can modify the original function without modifying its source code:
function connect_after(before, after){
return function(){
before.apply(this, arguments);
after();
};
}
var original_function = function(){ console.log(1); }
original_function = connect_after(original_function, function(){ console.log(2); })
I am relatively new to javascript so please be patient if what i am asking is completely stupid!
I am trying to make a simple module. Inside the module i want to have a config object that holds settings for the module. I am also using jquery. The jquery selectors work only when in a function directly in the main object/module.
I understand that javascript has functional scope so I am suprised that I cannot use the jquery selectors anywhere inside the module.
EDIT:
I want to be able to directly set all of my configs inside the configs object using jquery selectors. This way i keep all the messy stuff inside one place and can then access configs.whatever throughout the rest of the module. At the moment jquery selectors do not work inside the configs module.
var OB = function() {
var configs = {
'mode' : 'test',
'numOfSelects' : $('.mySelect').find('select').length, // This doesnt work
}
var getMode = function() {
return configs.mode;
}
function init() {
alert(configs.numOfSelects); // This alerts 0 until the following line
alert($('.mySelect').find('select').length); // This correctly alerts 2
};
var handlers = {
successHandler : function() {
alert("Success");
},
errorHandler : function() {
alert("error");
}
}
return {
init : init,
getMode : getMode
}
}( );
$(document).ready(function(){
OB.init();
});
It isn't that jQuery isn't in scope — that's that the code isn't executing when you think it is. The variable config is defined when that anonymous function (var OB = function() {}()) is executed. The DOM isn't ready yet, so that DOM traversal doesn't find anything. When you do the DOM traversal in init(), that isn't executed until it's explicitly called inside the $(document).ready() handler, at which point that DOM is set up. That's the difference you're seeing.
OB() needs to be called after the DOM has completely loaded. Hence the answer by Marcelo, which calls OB() in the ready() method.
EDIT: It's funny that my original answer below was incorrect because I didn't notice two little parentheses at the end of the definition of OB, and it turns out that these are the culprit. You define and then immediately invoke OB, which is before the DOM has been fully loaded. Remove those parentheses and make the change I suggest below.
Calling OB() returns an object with init and getMode, but you haven't called OB(), you've only referred to OB. Try this instead:
$(document).ready(function(){
OB().init();
});
Also, I assume you want to later refer to getMode. In particular, you will to get the copy of getMode that has access to the same local scope that your init() call had access to. To achieve this, you will need to store the result of calling OB() for later use:
var ob;
$(document).ready(function(){
ob = OB();
ob.init();
});
function some_other_function() {
... ob.getMode() ...;
}
This question already has answers here:
How to invoke $(document).ready(function() {}) in unit testing
(5 answers)
Calling $(document).ready(function() {...}); from another file
(1 answer)
Closed 5 months ago.
I have a question in regards to unit testing jQuery's document.ready function().
Currently I have 2 scenarios in my code:
function myFunction()
{
$(document).ready(function() { ... });
}
And:
$(document).ready(function()
{
// some really long setup code here
});
I tried to write a unit test for the first scenario, but I just couldn't get it to run into the document.ready function. As for the second scenario, I haven't come up with a way to test it yet (I'm having trouble coming up with both a way to test it and the syntax).
So assuming I cannot change the source code, are there any ways to test those functions? (assuming it is a good idea to test them)
Thanks.
You do not need to test $(document).ready as it is part of the framework and is already unit tested. When writing unit tests you need to test two things:
Your interaction with the framework. This includes things like making sure that you call the right functions with the right parameters.
Your own code - that your code does the right thing.
So what you really need to do is to make sure that whatever code that gets called from $(document).ready is correct.
function myInit(){
//...
}
function myFunction()
{
$(document).ready(myInit);
}
All you need to do now is to unit test myInit function.
What you can also do is mock out $.ready function to make sure that you are calling it:
var readyCalled = false;
$.ready = function(func){
readyCalled = (myInit == func);
}
//Your code containing `myInit` will get executed somewhere here
//....
//Then test:
test("Should have called ready", function() {
ok(readyCalled, "ready should have been called with myInit as a parameter.")
});
The function that registers the on ready handler should register another function, not an anonymous codeblock. Then you can test the code that calls $.ready() separate from the code that runs on ready. So you have:
One test to verify the right function is set as the the ready handler
Another test to verify the ready handler does the right stuff
To test scenario 1, you'll need to inject a test double for jQuery. This is difficult as if you redefine $ or jQuery, odds are you'll screw up other code that relies on it for other processing (like the test runner). At the same time your code may still want to call jQuery directly when its using utility methods like array concatenation. Any inversion-of-control pattern should address this though (http://martinfowler.com/articles/injection.html).
Anyhow, here's some code using constructor injection (using JSMock for the mocking library, and QUnit (of jQuery) for the test runner):
// the code
var createComponent = function(_$) {
var that = {};
that.OnStart = function() {
_$.ready(this.OnReady);
};
that.OnReady = function() {
};
return that;
};
// the test
test("OnStart associates the ready handler", function() {
var sut;
var mock$ = mc.createMock($);
mock$.expects().ready(isA.TypeOf(Function)).andStub(function(callback) {
equals(callback, sut.OnReady);
});
sut = createComponent(mock$);
sut.OnStart();
mc.verify();
});
test("OnReady does the right stuff", function() {
//etc
});
I use this general pattern for all event handlers in JS... You might prefer to use prototype type classes. When you pass functions as parameters to jQuery, you need to be aware that the "this" value will not be set by jQuery when those callbacks are called. In the test, this breaks because equals(callback, sut.OnReady) no longer passes. To address this, you need to make the event handlers direct members of each instance. You can imagine when there are a number of then its nice to have a util that takes a list of them, but this demonstrates making 'OnReady' a member who does not rely on 'this'.
var Component = function(_$) {
this._$ = _$;
// repeat for each event handler thats tested
this.OnReady = function() {
Component.prototype.OnReady.apply(this);
}
}
Component.prototype.Start = function() {
this._$.ready(this.OnReady);
}
Component.prototype.OnReady = function() {
}