Is my javascript closure written correctly? - javascript

I am using the following function closure in a jqgrid (a jquery grid) to retain changes in edits when paging in a variable called 'retainedChanges'- does this look ok; Im i breaking any good practices in javascript;
the code works alright just want to make sure I dont introduce features that can break in the future
(function($){
var retainedChanges;
retainedChanges = new Array();
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);

This works fine, although you should probably accept jQuery as an argument:
(function($){
This way, even if the $ symbol is being used for something else outside of your closure, it won't effect your code inside the closure.
2 more things:
1) You should declare and assign you variable together, and use [] instead of new Array().
2) You're missing a $ symbol here: ('#grid').
For a full rundown, look at this:
(function($){
var retainedChanges = [];
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);

You are passing jQuery into a function that has no arguments and never uses the jQuery object passed in. You may have meant:
(function($){
Other than that it looks fine.

There are several things you could improve:
1) You pass jQuery to the function, but do not use it (you use global object $, if it is defined). Modify your code to accept one parameter, named $:
(function($){
2) You can shorten retainedChanges declaration:
var retainedChanges = new Array();
3) If you are trying to write jQuery plugin, then follow the following tutorial: jQuery: Plugins/Authoring
If not, then maybe use different global object than jQuery?

Related

Bind variable to this

I'm creating a simple jQuery plugin and am having trouble making it modular.
For example:
$.fn.testMethod = function(option1, option2) {
var something1 = option1;
var something2 = option2;
};
My problem starts to occur when I have more than one element using this method. For example:
$('.element1').testMethod(1, 2);
$('.element2').testMethod(3, 4);
The second element ends up using the variable of the first later down the line if I'm changing stuff around. What would be a better way of doing this and locking the variables to the specific element they're being used with? If this is too vague, I can paste in my full code but it is a bit complex.
When working with jQuery plugins, you generally want to do an each loop and return the collection.
That also means you can't use just simple variables, but that's where jQuery's data comes in handy.
$.fn.testMethod = function(option1, option2) {
return this.each(function() {
$(this).data('something1', option1);
$(this).data('something2', option2);
// do stuff
var something = $(this).data('something1'); // etc
});
};
On the other hand, the arguments and variables inside the function will be unique to each function call, so there's no way the second call to the function would use the variables from the first call unless you're doing something else strange.

about javascript / jquery syntax

I couldn't find the syntax something like this anywhere:
var mz = jQuery.noConflict();
mz('#zoom01, .cloud-zoom-gallery').CloudZoom();
This means: jQuery.noConflict()('#zoom01, .cloud-zoom-gallery').CloudZoom();
And something like this:
$(window)[this.off?'off':'on']("scroll", fixDiv );
So, I'm wondering about the syntax something like of these:
jQuery.noConflict()(syntax) and $(window)[syntax](syntax) and I also think there might be something like this $(selector){syntax}
Can anyone elaborate of those syntax?
The best place to start is the documentation
$.noConflict()
Many JavaScript libraries use $ as a function or variable name, just
as jQuery does. In jQuery's case, $ is just an alias for jQuery, so
all functionality is available without using $. If you need to use
another JavaScript library alongside jQuery, return control of $ back
to the other library with a call to $.noConflict(). Old references of
$ are saved during jQuery initialization; noConflict() simply restores
them.
In other words, noConflict() sets a variable to equal jQuery, so this
var mz = jQuery.noConflict();
mz('#zoom01, .cloud-zoom-gallery').CloudZoom();
is the same as
$('#zoom01, .cloud-zoom-gallery').CloudZoom();
or
jQuery('#zoom01, .cloud-zoom-gallery').CloudZoom();
noConflict() does not directly take selectors, it's just a function that sets jQuery in a certain scope to a variable so you can have multiple versions of jQuery (which you shouldn't) or use other libraries that also uses $ for something, it does not mirror the selector engine or anything else, even if it might seem so at first glance, it simply returns an instance of jQuery
In javascript there is dot notation and bracket notation, so an object can be accessed as
object.propertyName
or
object['propertyName']
as everything in javascript is an object, even jQuery methods, they can be accessed as
$('#element').fadeIn(200);
or
$('#element')['fadeIn'](200);
it's the same thing, so doing
$(window)['on']("scroll", fixDiv );
is the same as
$(window).on("scroll", fixDiv );
the advantage of using brackets is that they can contain any string, even variables, or in this case ternary statements, or the returned result of a function
var event = 'on';
$(window)[event]("scroll", fixDiv );
or
var event = this.off ? 'off' : 'on';
$(window)[event]("scroll", fixDiv );
that one also uses this, which in the global scope would be window, and it's the same as
$(window)[this.off ? 'off' : 'on']("scroll", fixDiv );
The ternary statement itself is just a fancy condition, and this
var event;
if (this.off) {
event = 'off';
} else {
event = 'on';
}
is exactly the same as
var event = this.off ? 'off' : 'on';
Added for the edited question :
jQuery() or $() is a function, something we can tell from the parenthesis, so it's something like
function jQuery(arguments) {
// do something
}
which can be called as
jQuery(some_arguments);
and as var $ = jQuery one can also do $();
Now that we know it's a function, it makes sense that we can do
$('#element_id')
and internally jQuery checks what kind of argument we passed, it sees that is's a string, and it's starting with #, so it's an ID, and then jQuery can do document.getElementById() and get that DOM element, and at the same it wraps that element in a new array-like object, usually referred to as a jQuery object.
We can also pass in a DOM node, array, object or anything else, and jQuery tries to figure out what it is, and wrap in that jQuery object for us to use with other jQuery methods, so this :
$({x:10, y:20})
is the same as
var obj = {x:10, y:20};
$(obj)
and its turned into one of those jQuery objects with the properties x and y. Passing in an object like this means we can chain on methods, and those properties are available in the methods.
$({x:10, y:20}).animate({x:50}, 1000);
And that's basically how it works, simplified a lot.
As for passing objects to methods, that's a very common way to pass arguments.
To see how it works, it's easiest to create a method:
$.fn.doStuff = function(argument) {
this.css(argument);
}
inside a jQuery plugin, this is the jQuery object, and we can now use the mothod above that does nothing more than pass the arguments to jQuery's css().
We know we can pass an object to css() like this :
$('#element').css({left: '10px', top: '20px'});
so using our plugin we can do the same
var obj = {left: '10px', top: '20px'};
$('#element').doStuff(obj);
and it ends up doing exactly the same thing. Of course, we could do anything with the object :
$.fn.doStuff = function(args) {
if ( typeof args == 'string' ) {
alert(args); // if it's a string, just alert it
} else if ( typeof args == 'object' ) {
for ( var key in args ) { // if it's an object, iterate
this[0].style[key] = args[key]; // and do something
}
}
}
foo['bar'] syntax is to get the property bar from object foo.
foo() is to execute the function foo.
And you can combine these as you wish.
jQuery.noConflict() returns a function so you could execute the result by jQuery.noConflict()(syntax).
$(window) returns an object so you could get a property from it by $(window)[syntax], and if the property is a function, then you could execute it by $(window)[syntax](syntax).
This is just javascript syntax.
person.name is exactly the same as person["name"]
The same happens with methods
$(window).on(...) is exactly the same as $(window)["on"](...)
One cool thing about the second way is that you can make the member name variable, for example:
So doing:
var windowMethod = "on";
$(window)[windowMethod](...)
is the same as
$(window)["on"](...)
And you can have an expression inside the brackets, so this:
$(window)[this.off ? 'off' : 'on']("scroll", fixDiv );
would be exactly the same as doing this:
if(this.off)
$(window).off("scroll", fixDiv);
else
$(window).on("scroll", fixDiv);
But the former is shorter.
Hope this helps. Cheers
PS: The jQuery.noConflict()(syntax) is straightforward, .noConflict() just returns a function and then we append some other parens to call it just as any other function.

how to namespace many events in a Javascript object literal

I am using jQuery events to capture events across a rails app. Basically, there are a set of event captures' on DOM elements that then call other functions. What I'd like to do is provide some namespacing to these event captures and an looking for the best way:
I currently have (but like 60 of them):
$(document).ready(function(){
$('.edit-item').on('click', arc.event_handler.edit_item);
});
and would like something like the following - basically provide the edit_item so we know where to look:
$(document).ready(function(){
var events.edit_item= {
$('.edit-item').on('click', arc.event_handler.edit_item);
};
});
But this is giving me an error. I am familiar with basic object literal syntax like:
var my = {
say_my_name: function(){
alert('my name');
}
}
but not sure how to apply it with jQuery functions. How would I do this?
I am aware that there are anonymous functions for namespacing this more agressively but, honestly, just want this one change right now
thx in advance
You seem to want
var events = {
"edit_item": $('.edit-item').on('click', arc.event_handler.edit_item)
};
or
var events = {};
events.edit_item = …;
// equal to
events["edit_item"] = …; // Here you could use any expression (like a variable)
// instead of the string literal
Now events.edit_item is the jQuery object returned by the expression.
Perhaps this is useful:
var events;
$(document).ready(function(){
events = {
edit_item: $('.edit-item').on('click', arc.event_handler.edit_item),
other_item: $('.other-item').on(/* something else */),
//...
// the last item without trailing comma
};
});
Please note the commas at the end of the lines. IE however dislikes the comma after the last line, so omit it.
The events object contains the jQuery objects, so you can bind more events to it or do other jQuery operations on them.

passing javascript object to function in a jquery way?

Yeah so I've been messing around with javascript a while but only recently got into stuff like object orientation, prototyping and using objects for all functions and vars.
But, many frameworks like jQuery or extJS have something I have yet to grasp, you can define an object by using a built in function for searching the dom, example:
var a = $('#mydiv');
and then you can do a.click(function);
So my question is, how can I create a "framework" of sorts where I can write code in that style, like example:
var mydiv = document.querySelector('mydiv');
mydiv.neph(args,args);
So I have my object defined, in this case it's a dom element or whatever, now I pass it to my function "neph" with arguments, I want to create code that allows me to do this. But since mydiv does not have any function, it only has in this case the dom element right so mydiv.neph does not exist, but in jquery you can define whatever var and .click or .mouseover or whatever does exists within the object as functions? Confusion ensues! :D
Ok sorry if this is a retarded question btw =P
jQuery and other libraries define a function called $ that takes several optional parameters. The object returned by calling $ is not a DOM element, but a jQuery object wrapping a DOM element up with a set of convenient functions.
You can do something similar yourself:
<html>
<body>
<input id="derp" type="text"/>
<script type="text/javascript">
function $(id)
{
return new myLibrary(id);
};
function myLibrary(id)
{
this.el = document.getElementById(id);
};
myLibrary.prototype.help = function()
{
alert(this.el.id);
return this;
};
// Woah! My own fake jquery!
$("derp").help();
</script>
</body>
</html>
jQuery is far more sophisticated, of course. For example, it will use apply and call to set this correctly in calls like jQuery.each.
You need to create a Prototype in javascript. This is what allows you to add a function to an object that's already defined (i.e. the .click() function that you gave as an example).
You can have a look at the jQuery code, it's open source. It's not the simplest code, but you can still see how it works and how they do it.
Mike's comment is a good answer: Look at jquery or Ext-Core's sources.
Maybe what you're missing is that, in jquery, for instance $() returns a jquery object, which wraps the plain vanilla DOM node, providing extended functionality.
In jQuery, $ is just an alias to the jQuery object. So when you call $('#mydiv'); you're really calling a function like jQuery('#mydiv'); So part of what makes jQuery so cool is that every the $() function returns this, which means when you call the $() you're getting a handle to the jQuery object and all of the methods it has on it. That is what allows you to do something like this:
var a = $('#mydiv');
a.click(function() { // etc etc etc });
So to pull off your example:
var mydiv = document.querySelector('mydiv');
mydiv.neph(args,args);
You'd have to create an object that has a function called neph on it and return that object in the context of mydiv when you call querySelector.
var myFramework = function() {
this.context = undefined;
this.neph = function(arg, args) {
console.log(arg, args);
}
};
var myCuteFunction = function(element) {
var f = new myFramework();
f.context = element;
return f;
}
// ...
var x = myCuteFunction('#mydiv');
x.neph(arg, args);
Since nobody has really answered about Ext, you can easily extend the element wrapper prototype:
Ext.override(Ext.Element, {
myMethod: function(a, b){
console.log(a, b, this);
}
});
"this" will refer to the Ext.Element object.

Cached Jquery selector behavior that I do not understand

Given the following code why am I getting different values for a and b? I would have thought they would return the same thing:
(function() {
var a = $('#foo');
var Test = function(){
console.log(a); //outputs 'jQuery()'
var b = $('#foo');
console.log(b); //outputs 'jQuery(select#foo)' which is what I want
};
})();
This question stems from me trying to stick frequently used selectors into vars. Originally I was doing it in each method (like I did with var b in the above example) but then I found I needed to use the selectors in multiple methods so I moved the assignment out to be available (or so I thought) to all of the methods in that anonymous function. As you can see, it does not work. Why is this?
EDIT: this code is loaded by a method that is triggered by a click. The id foo is present at page load and is not dynamically set or changed.
Make sure that the code isn't called until after your page finishes loading!
$(function() {
// your code
});
Also, of course, you'll want to be careful about caching things that might be changed on the page by other parts of your client-side application.
Just to improve on the previous answer - b is only evaluated when you call the Test function, probably once the page is loaded. Cache the selectors inside document ready:
$(document).ready(function(){
var a = $('#foo');
});
I only cache selectors when I'm using the same selector inside a block of code. I use the $variable naming convention for this:
var $divs = $('div');
You can also chain functions together to avoid having to cache the selector:
$('div').append('hello world').addclass('hello').show();

Categories