I've seen a number of approaches to creating your own namespaces in JS, but it does get a bit confusing.
We make use of JQuery and in one project we recently got involved with I noticed that developers used this approach:
var myObject = new function ($){
var localVar = 1,
init = function(){
...
},
return {
init: init
}
}
Why the use of 'new' function
Why pass in the $ into this new function?
Why this form of return?
In another project we used the following approach:
$.companyName = {
textNotFound: "The search returned no results",
search: {
orders: null,
stock: null
}
}
$.companyName.search.orders = function(param1,...){
...
}
$.companyName.search.stock = function(param1,...){
...
}
The usage scenario would look like this:
var orders = $.companyName.search.orders(...);
if (orders == null){
alert($.companyName.textNotFound);
}
else{
...
}
Is this second method not advisable and the first should be used instead?
Or, should we be authoring JQuery plugins? (I wouldn't think so, because this is site specific functionality)
Thanks,
Jacques
Related
I am trying to add a functionality to a web page that uses a jquery library which doesn't seem to have any documentation. (unknown origin) my problem is mainly due to the lack of understanding on jquery plugin model and/or inner workings of javascript.
1. the plugin is initiated as follows
jQuery('div.carousel').scrollGallery({
mask: 'div.mask',
slider: 'div.slideset',
slides: 'div.slide', ............ });
2. the plugin is defined in jquery as follows
;(function($){
function ScrollGallery(options) {
this.options = $.extend({
mask: 'div.mask', ...... }, options);
this.init();
3. in the Object.prototype declaration i see the following function numSlide defined.
ScrollGallery.prototype = {
....................
numSlide: function(c) {
if(this.currentStep != c) {
this.currentStep = c;
this.switchSlide();
}
},
.......... };
Question.
How do i reference numSlide(int) function externally?.
I tried the following methods and it did not work.
myx = jQuery('div.carousel').scrollGallery({ // var myx was added in the global scope
myx.numSlide(1); //error undefined is not a function
i tried adding return this; at the end of myx = jQuery('div.carousel').scrollGallery({ but it still returns the jQuery object.
i also tried
jQuery.scrollGallery().numSlide(2); //error undefined is not a function
jQuery.scrollGallery.numSlide(2); //same error
Do i need to add LIGHT BULB
// jquery plugin
$.fn.scrollGallery = function(opt){
return this.each(function(){
$(this).data('ScrollGallery', new ScrollGallery($.extend(opt,{holder:this})));
});
};
}(jQuery));
ANSWER (I think)
it looks like the ScrollGalary object is stored in a data for the selector. So i believe i can do the following jQuery('selector').data('ScrollGallery').numSlide(2);
I decided to post this anyway in-case if anyone in the future had a similar gullible situation.
One way of doing this will be to initiate ScrollGallery object first and then use it.
var test = new ScrollGallery();
test.numSlide();
if you want to extend jQuery and use the function you can assign it as follows
$.fn.scrollGallery = new ScrollGallery();
and use it
$("window").scrollGallery.numSlide();
Given a javascript object like this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
// access caller name here, so I can return cool/neat stuff
}
});
I want to be able to get children of myThing.gen, but know what is being asked for in the getter.
for example:
var coolThing = myThing.gen.oh.cool;
var neatThing = myThing.gen.oh.neat;
I want the "oh.cool" or "oh.neat" part in getter, so I can make decisions based on this, and return something specific to it. I am ok with solution not working in IE, or old browsers, as it is primarily for node.
The actual purpose of this is so that I can request myThing.gen.oh.neat and have the myThing.gen getter resolve to require('./oh/neat.js') and return it.
Since require cache's, this is an efficient way to dynamically load modular functionality, and have a tidy interface (rather than just dynamically building the require where needed) without having to know the structure ahead of time.
If there is no introspection-of-name function that can get this for me, I could just do something less elegant, like this:
myThing.gen = function(name){
return require('./' + name.replace('.', '/') + '.js');
}
and do this:
neatThing = myThing.gen('oh.neat');
I don't like this syntax as much, though. I looked at chai's dynamic expect(var).to.not.be.empty stuff, but couldn't figure out how to do it completely dynamically. Maybe there is not a way.
without actually solving the problem of dynamically discovering the caller, I can do this:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
return {
'oh':{
'cool': require('./oh/cool.js'),
'neat': require('./oh/neat.js')
}
};
}
});
Is there a way to do this dynamically?
You can't see what the property gen will be used for in the future, so you would need to return an object with properties that react to what the object is used for when it actually happens:
var myThing = {};
Object.defineProperty(myThing, 'gen', {
'get' : function() {
var no = {};
Object.defineProperty(no, 'cool', {
get: function(){ alert('cool'); }
});
Object.defineProperty(no, 'neat', {
get: function(){ alert('neat'); }
});
return { oh: no };
}
});
Demo: http://jsfiddle.net/UjpGZ/1/
I want to create jQuery plugin with config (for example plugin myplugin).
Than call $(elem).myplugin(config); After that I want to call methods from this plugin like $(elem).myplugin().method() with already stored config.
My offer is something like that:
(function($) {
$.fn.myplugin = function(options) {
var $this = $(this);
var getOptions = function() {
return $this.data('myplugin');
};
var initOptions = function(opt) {
$this.data('myplugin', opt);
};
var setOption = function(key, value) {
$this.data('myplugin')[key] = value;
}
var updateBorderWidth = function() {
$this.css('border-width',
getOptions().borderWidth * getOptions().coeficient);
};
var init = function(opt) {
initOptions(opt);
updateBorderWidth();
}
function changeBorder(width) {
setOption('borderWidth', width)
updateBorderWidth();
}
if(options) {
init(options);
}
return {
changeBorder : changeBorder
};
}
})(jQuery);
And usage:
$(function() {
var item1 = $('#test1').myplugin({ coeficient: 1, borderWidth: 1 });
var item1 = $('#test2').myplugin({ coeficient: 2, borderWidth: 1 });
$('#btn').click(updateBorder);
});
function updateBorder() {
$('#test1').myplugin().changeBorder($('#inpt').val());
$('#test2').myplugin().changeBorder($('#inpt').val());
}
Example: http://jsfiddle.net/inser/zQumX/4/
My question: is it a good practice to do that?
May be it's incorrect approach. Can you offer better solution?
Edit:
After searching for threads on jQuery plugin template I found these Boilerplate templates (updated) which are more versatile and extensive designs than what I've offered below. Ultimately what you choose all depends on what your needs are. The Boilerplate templates cover more use cases than my offering, but each has its own benefits and caveats depending on the requirements.
Typically jQuery plugins either return a jQuery object when a value is passed to them as in:
.wrap(html) // returns a jQuery object
or they return a value when no parameter is passed in
.width() // returns a value
.height() // also returns a value
To read your example calling convention:
$('#test1').myplugin().changeBorder($('#inpt').val());
it would appear, to any developer who uses jQuery, as though two separate plugins are being utilized in tandem, first .myplugin() which one would assume will return a jQuery object with some default DOM maniplulation performed on #test1, then followed by .changeBorder($('#inpt').val()) which may also return a jQuery object but in the case of your example the whole line is not assigned to a variable so any return value is not used - again it looks like a DOM manipulation. But your design does not follow the standard calling convention that I've described, so there may be some confusion to anyone looking at your code as to what it actually does if they are not familiar with your plugin.
I have, in the past, considered a similar problem and use case to the one you are describing and I like the idea of having a convenient convention for calling separate functions associated with a plugin. The choice is totally up to you - it is your plugin and you will need to decide based on who will be using it, but the way that I have settled on is to simply pass the name of the function and it's parameters either as a separate .myplugin(name, parameters) or in an object as .myplugin(object).
I typically do it like so:
(function($) {
$.fn.myplugin = function(fn, o) { // both fn and o are [optional]
return this.each(function(){ // each() allows you to keep internal data separate for each DOM object that's being manipulated in case the jQuery object (from the original selector that generated this jQuery) is being referenced for later use
var $this = $(this); // in case $this is referenced in the short cuts
// short cut methods
if(fn==="method1") {
if ($this.data("method1")) // if not initialized method invocation fails
$this.data("method1")() // the () invokes the method passing user options
} else if(fn==="method2") {
if ($this.data("method2"))
$this.data("method2")()
} else if(fn==="method3") {
if ($this.data("method3"))
$this.data("method3")(o) // passing the user options to the method
} else if(fn==="destroy") {
if ($this.data("destroy"))
$this.data("destroy")()
}
// continue with initial configuration
var _data1,
_data2,
_default = { // contains all default parameters for any functions that may be called
param1: "value #1",
param2: "value #2",
},
_options = {
param1: (o===undefined) ? _default.param1 : (o.param1===undefined) ? _default.param1 : o.param1,
param2: (o===undefined) ? _default.param2 : (o.param2===undefined) ? _default.param2 : o.param2,
}
method1 = function(){
// do something that requires no parameters
return;
},
method2 = function(){
// do some other thing that requires no parameters
return;
},
method3 = function(){
// does something with param1
// _options can be reset from the user options parameter - (o) - from within any of these methods as is done above
return;
},
initialize = function(){
// may or may not use data1, data2, param1 and param2
$this
.data("method1", method1)
.data("method2", method2)
.data("method3", method3)
.data("destroy", destroy);
},
destroy = function(){
// be sure to unbind any events that were bound in initialize(), then:
$this
.removeData("method1", method1)
.removeData("method2", method2)
.removeData("method3", method3)
.removeData("destroy", destroy);
}
initialize();
}) // end of each()
} // end of function
})(jQuery);
And the usage:
var $test = $('#test').myplugin(false, {param1: 'first value', param2: 'second value'}); // initializes the object
$test.myplugin('method3', {param1: 'some new value', param2: 'second new value'}); // change some values (method invocation with params)
or you could just say:
$('#test').myplugin(); // assume defaults and initialize the selector
Passing parameters to javascript via data attributes is a great pattern, as it effectively decouples the Javascript code and the server-side code. It also does not have a negative effect on the testability of the Javascript code, which is a side-effect of a lot of other approaches to the problem.
I'd go as far as to say it is the best way for server-side code to communicate with client-side code in a web application.
I have written some relatively simple jQuery plug-ins, but I am contemplating writing something more advanced in order to keep commonly used methods on the site easily accessible and DRY
For example, I might have something like this for a structure:
plugin
- popup
- element
...
=== popup ===
- login
- product
...
=== element ===
- shoppingCart
- loginStatus
...
So, to bind a popup login popup event, I'd like to be able to do:
$('#login_button').plugin.popup.login();
What's the best way to do this? Is there a better way of achieving what I want to do?
Cheers,
The way farhan Ahmad did it was pretty much right... it just needs deeper levels to suit your needs your implementation would look like this:
jQuery.fn.plugin = function(){
//"community" (global to local methods) vars here.
var selectedObjects = this; //-- save scope so you can use it later
// return the objects so you can call them as necessary
return {
popup: { //plugin.popup
login: function(){ //plugin.popup.login
//selectedObjects contains the original scope
console.log(selectedObjects);
},
product: function(){} //plugin.popup.product
},
element: { //plugin.element
shoppingCart: function() {}, //plugin.element.shoppingCart
loginStatus: function() {} //plugin.element.loginStatus
}
}
}
So now if you call:
$("#someDiv").plugin.login(); the result will be as expected. I hope this helps.
jQuery.fn.messagePlugin = function(){
var selectedObjects = this;
return {
saySomething : function(message){
$(selectedObjects).each(function(){
$(this).html(message);
});
return selectedObjects; // Preserve the jQuery chainability
},
anotherAction : function(){
//...
return selectedObjects;
}
};
}
We use it like this:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');
The selected objects are stored in the messagePlugin closure, and that function returns an object that contains the functions associated with the plugin, the in each function you can perform the desired actions to the currently selected objects.
I'm starting to do a lot of jQuery programming. I'm finding that my code is becoming a little hard to maintain and is not all that readable. My javascript is being included on every page of our web application, even though it is only used on one page. This has the benefits of having the javascript cached before loading the page, but I'm worried about accidentally creating functions with identical names (created by other programmers on my team).
I'm new to javascript, so there may be some basics that I'm missing. What are some techniques that I can apply to the following code, as well as other code in the future to make my jQuery code more maintainable and easy to read?
<script type="text/javascript">
$(document).ready(function(){
$('#registration-information .copy-button').click(copyField);
});
function copyField(e) {
e.preventDefault();
var $tr = $(this).closest('tr');
var originalText = jQuery.trim($tr.find('.contact-data').text());
var newText = jQuery.trim($tr.find('.registration-data').text());
var $button = $tr.find('.copy-button');
var $loadingIcon = $('<span class="loading-icon"></span>').hide().insertAfter($button);
var $undoLink = $('<a class="undo-link" href="#">Undo</a>').hide().insertAfter($button);
var field = $button.attr('id');
$undoLink.click(function(e){
e.preventDefault();
undoCopyField($tr, originalText);
});
$button.hide();
$loadingIcon.show();
$.ajax({
url: '/registrations/copy-field-to-contact',
data: {
id: '<?php echo $registration->ID ?>',
field: field,
data: newText
},
success: function(data){
if (data.success) {
$loadingIcon.hide();
$tr.find('.contact-data').html(newText);
$tr.find('td.form_field_label').removeClass('mismatched');
$tr.effect('highlight', 1000, function(){
$undoLink.fadeIn();
});
} else {
displayErrorMessage(data.error);
$loadingIcon.hide();
$button.show();
}
},
error: function(){
displayErrorMessage('Unknown reason');
$loadingIcon.hide();
$button.show();
}
});
}
function undoCopyField($tr, originalText) {
var $button = $tr.find('.copy-button');
var $loadingIcon = $tr.find('.loading-icon');
var $undoLink = $tr.find('.undo-link');
var field = $button.attr('id');
$undoLink.hide();
$loadingIcon.show();
$.ajax({
url: '/registrations/copy-field-to-contact',
data: {
id: '<?php echo $registration->ID ?>',
field: field,
data: originalText
},
success: function(data){
if (data.success) {
$undoLink.remove();
$loadingIcon.hide();
$tr.find('.contact-data').html(originalText);
$tr.find('td.form_field_label').addClass('mismatched');
$tr.effect('highlight', 1000, function(){
$tr.find('.copy-button').fadeIn();
});
} else {
displayErrorMessage(data.error);
$loadingIcon.hide();
$undoLink.show();
}
},
error: function(){
displayErrorMessage('Unknown reason');
$loadingIcon.hide();
$undoLink.show();
}
});
}
function displayErrorMessage(message) {
alert('Sorry, there was an error while trying to save your changes: ' + message);
}
</script>
Update: There are numerous sections of this code sample with chunks of code that are almost identical. Specifically the AJAX calls. Those two blocks are essentially the same, except for the actions that take place after the call has completed. I'd really like to figure out a way to DRY up those sections.
Two tips:
Use namespaces for your code to avoid name conflicts. There are of course no real namespaces in Javascript, but you can fake them using objects.
`
var MyCode=MyCode||{
copyField:function (e){
},
someOtherFunction:function(){
}
};
$(document).ready(function(){
MyCode.copyField(...);
});
Put your javascript code one or more separate files (one per namespace), and use third party libraries like combres to combine them into one file, minify them and take care of proper caching. It saves a lot of bandwidth and is cleaner than distributing all kinds of javascript functions across different pages.
One way to make your jQuery code cleaner and more maintainable is to break it into reusable jQuery plugins. This allows you to encapsulate related functionality in a single file. Each plugin is effectively a namespace so you will avoid function name collisions. You can also pass arguments to the plugin to customize the behaviour on a page by page or case by case basis.
There is a pretty good guide and template for writing plugins here
The only real readability issue I see here is that you could declare your variables without using the var each time by using a comma after each variable:
var $tr = $(this).closest('tr'),
originalText = jQuery.trim($tr.find('.contact-data').text()),
newText = jQuery.trim($tr.find('.registration-data').text());
I've always found this a bit easier to read then just a series of vars. To the eye, it looks like a code block that is started with var and ends when the indent returns.
Aside from that, it all looks good.
As a start...
Test drive the code using something like jsunit.
Create small well named classes, with small well named methods. The name of the class will describe it's responsibilities. The name of the method will describe it's responsibilities as well.
Refactor to remove duplication.
Limit the use of global scope.
Read Clean Code: A Handbook of Agile Software Craftsmanship [Paperback]
Robert C. Martin (Editor)
You can use YUI namespace model,
create a top level object (namespace)
lets var STW = {};
use this namespace function to create other classes or namespace
STW.namespace = function () {
var a = arguments, o = null, i, j, d;
for (i = 0; i < a.length; i = i + 1) {
d = ("" + a[i]).split(".");
o = STW;
for (j = (d[0] == "STW") ? 1 : 0; j < d.length; j = j + 1) {
o[d[j]] = o[d[j]] || {};
o = o[d[j]];
}
}
return o;
}
lets takes some example
in your first file
use STW.namespace("STW.namespace1");
STW.Namespace1.class1=function(){
//your code
}
in other files
STW.namespace("STW.namespace1.namespace2");
STW.namespace1.namespace2.class2=function(){
//your code
}
The stuff in the $.ajax parens is just a single argument like in any c-based function.
{} is JS's literal representation of an object. So if both cases have the same stuff between {} you could do this:
options = {
url: '/registrations/copy-field-to-contact',
data: {
id: '<?php echo $registration->ID ?>',
field: field,
data: newText
},
success: function(data){
if (data.success) {
$loadingIcon.hide();
$tr.find('.contact-data').html(newText);
$tr.find('td.form_field_label').removeClass('mismatched');
$tr.effect('highlight', 1000, function(){
$undoLink.fadeIn();
});
} else {
displayErrorMessage(data.error);
$loadingIcon.hide();
$button.show();
}
},
error: function(){
displayErrorMessage('Unknown reason');
$loadingIcon.hide();
$button.show();
}
}
And then for both:
$.ajax(options);
Now if there's a variation in the options for your next ajax call:
Example #1 - just change the URL to google
options.url = "http://www.google.com";
$.ajax(options);
Example #2 - change id property of the data property (which is another object)
options.data.id = "newID";
A word of caution on JQuery's selector syntax. Grabbing directly by ID is ideal. Grabbing just by class can be really slow in older versions of IE (which have no native getByClassName methods so there's more interpreter-level looping going on). The ideal way to write a selector narrows down to the closest parent ID available first and if you can be explicit add the tag name to the follow-up class selector to narrow down further (getElementsByTagName is native and speedy).
So if we could rely on .copy-button being an input tag:
$('#registration-information input.copy-button')
or we could rely on it being an input or an anchor tag
$('#registration-information input.copy-button, #registration-information a.copy-button')
those might be considerably faster options in IEs 6 and maybe 7 if there's a ton of HTML elements inside #registration-information. I try to give IDs to all unique containers likely to stay that way such that I've always got IDs handy for more efficient JQuery selectors.
But whatever you do, avoid these:
$('.copy-button')
That can choke IE 6 even on a largish html doc if there's enough of it going around. If you plan on using the results of a selector more than once, save it to a var. It's so concise it's easy to forget that these can be fairly resource intensive operations in some cases.