Javascript creating function to share variables between other functions - javascript

I have a couple click functions with jQuery that share the same variables, so I created a function to return those variables.
While this works, I'm wondering whether programmatically speaking this is the right or most efficient way to do this:
function clickVars($me){
var $curStep = $('.cust-step-cur'),
$nextStep = $curStep.next('.cust-step'),
nextStepLen = $nextStep.length,
$list = $('.cust-list'),
$btnCheck = $('.cust-btn.checklist'),
hasChecklist = $me.hasClass('checklist');
return {
curStep: $curStep,
nextStep: $nextStep,
nextStepLen: nextStepLen,
list: $list,
btnCheck: $btnCheck,
hasChecklist: hasChecklist
};
}
// Checklist Click
$('.option-list a').on('click',function(){
var $me = $(this),
myVars = clickVars($me);
currentStepOut(myVars.curStep);
myVars.curStep.removeClass('cust-step-cur');
currentStepIn(myVars.nextStep, myVars.list, myVars.btnCheck);
});
// Navigation
$('.cust-btn').on('click',function(){
if(animReady === false)
return false;
var $me = $(this),
myVars = clickVars($me);
if(myVars.hasChecklist && myVars.list.hasClass('cust-step-cur'))
return false;
currentStepOut(myVars.curStep);
myVars.curStep.removeClass('cust-step-cur');
if(myVars.nextStepLen === 0 || $me.hasClass('checklist')) {
myVars.nextStep = myVars.list;
}
animReady = false;
currentStepIn(myVars.nextStep, myVars.list, myVars.btnCheck);
});
Is this a standard way of generated shared variables between multiple functions?

In AS3 it's good practice to do:
// Variable definitions
var enabled:Boolean = false;
public function myFunction(){
enabled = true;
}
So in JavaScript I've been doing:
// Variable defintions
var a,b,c,d,e = 0;
function alterVariables(){
a = 1;
b = 2;
}

You have to understand you are not sharing variables between functions. Moreover, each time you click those elements, clickVars function is called again and again, even if you click only one element multiple times. So this code is very bad expirience. Check this:
// Defined ones
var nodes = {
$elements : $('.elements'),
$otherElements : $('.otherElements'),
}
// In case you have multiple .selector elements in your DOM
$('.selector').each(function() {
// Defined ones for each element
var $element = $(this), isList = $element.hasClass('.list');
$element.bind('click', function(){
nodes.$elements.addClass('clicked');
});
});
$('.anotherSelector').each(function() {
// Yep, here is some duplicate code. But there won't be any
// performance improvement if you create special method for
// such small piece of code
var $element = $(this), isList = $element.hasClass('.list');
$element.bind('click', function(){
nodes.$elements.addClass('clicked');
});
});

Related

How to make properties and functions private in JavaScript?

I developed this short script but I'm wondering what is the best way to make $ul, $el and some functions e.g. select private. At the moment these are part of public interface but I would like to hide these. Wrap into another function returning DropDown object maybe? What would be the proper way to do this? Code below:
namespace = {};
namespace.DropDown = function(el) {
this.$el = $(el)
this.$trigger = this.$el.find(".trigger");
this.$ul = this.$el.find("ul");
this.$li = this.$ul.find("li");
this.$trigger.text(this.$ul.find(".selected").text());
this.$trigger.on("click", $.proxy(this.open, this));
}
namespace.DropDown.prototype.open = function(e) {
e.stopImmediatePropagation();
this.$ul.addClass("open");
// position selected element in the middle
var scrollUp,
panelCenter = this.$ul.scrollTop() + (this.$ul.innerHeight() / 2),
selectedPositionTop = this.$ul.scrollTop() + this.$ul.find(".selected").position().top;
if (selectedPositionTop > panelCenter) {
scrollUp = selectedPositionTop - panelCenter;
this.$ul[0].scrollTop = this.$ul[0].scrollTop + scrollUp;
} else {
scrollUp = panelCenter - selectedPositionTop;
this.$ul[0].scrollTop = this.$ul[0].scrollTop - scrollUp;
}
// position elements whole container (list container)
var triggerTop = this.$trigger.offset().top + (parseInt(this.$trigger.css("padding-top")) || 0) + (parseInt(this.$trigger.css("border-top") || 0)),
t = Math.abs(triggerTop - this.$ul.find(".selected").offset().top);
this.$ul.css("top", -t + "px");
this.$li.one("click", $.proxy(this.select, this));
$(document).one("click", $.proxy(this.close, this));
}
namespace.DropDown.prototype.close = function() {
this.$li.off("click");
this.$ul.removeClass("open");
this.$ul.css("top", "0px");
}
namespace.DropDown.prototype.select = function(e) {
$(document).off("click");
e.stopImmediatePropagation();
this.$li.removeClass("selected");
$(e.target).addClass("selected");
this.$trigger.text(this.$ul.find(".selected").text());
this.close(e);
}
$(function() {
new namespace.DropDown($(".dropdown")[0]);
new namespace.DropDown($(".dropdown")[1]);
new namespace.DropDown($(".dropdown")[2]);
});
EDIT:
I managed to wrap it into another function so I could get the properties and functions e.g. $ul, select off the protootype and make private via closure. It does work and I am able to keep guts of the object private but is this the best way to got? it seems overly complicated to me. Also I'm sure I'm not the first to come up with this and so is there a name for this pattern? Modified code below:
namespace = {};
namespace.DropDown = function(el) {
var $el = $(el),
$trigger = $el.find(".trigger"),
$ul = $el.find("ul"),
$li = $ul.find("li");
DropDown = function() {
$trigger.text($ul.find(".selected").text());
$trigger.on("click", $.proxy(this.open, this));
}
function select(e) {
$(document).off("click");
e.stopImmediatePropagation();
$li.removeClass("selected");
$(e.target).addClass("selected");
$trigger.text($ul.find(".selected").text());
this.close(e);
}
DropDown.prototype.open = function(e) {
e.stopImmediatePropagation();
$ul.addClass("open");
// position selected element in the middle
var scrollUp,
panelCenter = $ul.scrollTop() + ($ul.innerHeight() / 2),
selectedPositionTop = $ul.scrollTop() + $ul.find(".selected").position().top; //- $ul.find(".selected").outerHeight();
if (selectedPositionTop > panelCenter) {
scrollUp = selectedPositionTop - panelCenter;
$ul[0].scrollTop = $ul[0].scrollTop + scrollUp;
} else {
scrollUp = panelCenter - selectedPositionTop;
$ul[0].scrollTop = $ul[0].scrollTop - scrollUp;
}
// position elements whole container (list container)
var triggerTop = $trigger.offset().top + (parseInt($trigger.css("padding-top")) || 0) + (parseInt($trigger.css("border-top") || 0)),
t = Math.abs(triggerTop - $ul.find(".selected").offset().top);
$ul.css("top", -t + "px");
$li.one("click", $.proxy(select, this));
//$ul[0].scrollTop = this.scrollPos;
$(document).one("click", $.proxy(this.close, this));
}
DropDown.prototype.close = function() {
$li.off("click");
$ul.removeClass("open");
$ul.css("top", "0px");
}
return new DropDown();
};
$(function() {
new namespace.DropDown($(".dropdown")[0]);
new namespace.DropDown($(".dropdown")[1]);
new namespace.DropDown($(".dropdown")[2]);
});
EDIT 2:
I should have mentioed that I want to keep the methods on prototype rather than directly on the object itself via this.functionname. I want to avoid method duplication as this will happen if these are attached to this directly. For this simple reason this question is not duplicate.
You can create private methods and variables via closures, however another approach which may be simpler and create more fluid / cleaner reading code could be to designate private/public via convention. For example:
this._privateVariable = true;
this.publicVariable = true;
using underscores for private, no underscore for public etc.. /edited thanks HMR
This is the general form for making public and private attributes
var MyClass = (function(){
var myclass = function(){}
var privateMethod = function(){ return 1; }
myclass.prototype.publicMethod = function(){ return privateMethod() + 1; }
return myclass;
})();
Properties that are put on myclass, or on myclass.prototype, will public. However, variables that are declared with var will not be publicly available, though other methods will still have access to them. In the above example, publicMethod is the only publicly available method, but it is able to call privateMethod, because they were defined in the same scope.
like this also:
function SampleClass() {
// private
var _type = 'aClass';
// private
var _dothis = function (action) {
return 'i did this ' + action;
};
return {
// public
type: _type,
// public
dothis: function (action) {
return _dothis(action);
}
};
}

jQuery plugin object looses variable?

I'm developing a simple plugin with jQuery. As I see it I need to do it like this:
(function($) {
var somePrivateFn = function() {
alert(this.x);
}
$.fn.myPlugin = function() {
if(typeof arguments[0] === "string") {
var fnargs = Array.prototype.slice(arguments, 1);
// pluginData will be undefined
var pluginData = this.pluginData;
somePrivateFn.apply(pluginData, fnargs);
}
return this.each(function() {
var data = this.pluginData = {};
data.x = 1;
};
}
})(jQuery);
Of course my plugin needs to store some variables for the jquery object it's working on. To do this I just place an additional variable "pluginData" at the jQuery object itself. The problem is it is not accessible later. How to deal with it? What's the best approach to do this?
Pass the object when you run the plugin. $('div').myPlugin(a_data_object) It will persist to within the this.each loop.
This var options=$.extend({}, pluginData); will keep the data in one iteration from passing through to the next.
If you want to manipulate then store that data on the element you would use $.data().
(function($) {
console.clear();
var somePrivateFn = function(data) {
console.log(data.x);
}
$.fn.myPlugin = function(pluginData) {
// send to another func
somePrivateFn(pluginData);
// use on each div
return this.each(function(i) {
var options=$.extend({}, pluginData);
// add some example data
options.y = "i am y";
options.iteration=i;
options.text=$(this).text();
// store it on the element
$(this).data('pluginData', options);
});
}
// run the plugin on div elements
$('div').myPlugin({x:"i am x"});
// check out the data later
$('div').each(function(){
console.log($(this).data('pluginData'));
});
})(jQuery);
Example: http://jsfiddle.net/xXt5W/1/
$.data() docs: https://api.jquery.com/jQuery.data/

Cloning anonymous function

Hello I've used this patter to get a static variable
var uniqueID = (function() {
var id = 0; // This is the private persistent value
// The outer function returns a nested function that has access
// to the persistent value. It is this nested function we're storing
// in the variable uniqueID above.
return function() { return id++; }; // Return and increment
})(); // Invoke the outer function after defining it.
Now I'm trying to clone this function, but backup and the original still return sequential values. How can i "freeze" the status of the function when copy it?
Thanks
OK, something like this extremely convoluted contraption should work (fiddle, http://jsfiddle.net/dPLj6/):
var uniqueIdFunction = function(initialValue) {
var id = initialValue || 0;
var result = function() { return id++; };
result.clone = function(){ return uniqueIdFunction(id); }
return result;
};
var uniqueId1 = uniqueIdFunction();
Use the clone method to get a clone. The original will keep it's own internal id value. The clone will take its initial internal id from the clone source.
Here is a function that generates unique id generators:
var createGenerator = function(id) {
var id = id || 0;
return function() { return id++; };
}
var g1 = createGenerator();
var g2 = createGenerator();
console.log(g1(), g1(), g1());
console.log(g2(), g2());
console.log(g1());
console.log(g2());
// OP's cloning scenario
var freezeId = g1();
var clone = createGeenrator(freezeId);
console.log(g1(),g1());
console.log(clone());
#pax162's answer is more in line with what the OP wants to do. I just decided to post the more normal way of doing it.

button variable scope - how to update variable in parent scope? jQuery

Updated jsFiddle: http://jsfiddle.net/leongaban/NmH97/
Inside my main function is a button state modifier function and several click functions.
Having a bit of trouble updating the boolean values in the parent function. I could solve this by using global variables, but I know I shouldn't need to do that here.
In the function below, when #toggle_company is clicked I pass the bool company (which is set to true by default) into the setupToggleButtons function. The current state is then switched, but the original company bool value is unchanged, how would you write this to target and update the variable company in the parent function wireSearchIcons?
var wireSearchIcons = function() {
// Boolean Flags
var company = true;
var phone = true;
var orange_on = '#F37B21'; var orange_off = '#f3cdb1';
var green_on = '#3DB54A'; var green_off = '#d8e0c3';
// Other code...
$("#toggle_company").unbind('click').bind('click', function () {
setupToggleButtons(company, '#toggle_company path', orange_on, orange_off);
});
function setupToggleButtons(bool, path, on, off) {
var path = $(path);
if (bool) {
path.css('fill', off);
bool = false;
return bool;
} else {
path.css('fill', on);
bool = true;
return bool;
}
console.log(bool);
}
}
When you use the variable in the function call, you will be sending the value that the variable contains, not the variable itself. Changing the parameter has no effect on the variable where the value came from.
Javascript doesn't support sending parameter by reference, so just return the value from the function, and assign it back to the variable:
$("#toggle_company").unbind('click').bind('click', function () {
company = setupToggleButtons(company, '#toggle_company path', orange_on, orange_off);
});
function setupToggleButtons(bool, path, on, off) {
var path = $(path);
if (bool) {
path.css('fill', off);
bool = false;
} else {
path.css('fill', on);
bool = true;
}
return bool;
}
It doesn't make sense to write a setupToggleButtons function that does so little. You should inline the code into the click handlers, or create the click handlers within the setup function.
var wireSearchIcons = function() {
// Flags
var company = true;
var phone = true;
var orange_on = '#F37B21'; var orange_off = '#f3cdb1';
var green_on = '#3DB54A'; var green_off = '#d8e0c3';
// Toggle Button States
$("#toggle_company").unbind('click').bind('click', function () {
company = !company;
$('#div_company').css('background', company ? orange_on : orange_off);
});
$("#toggle_phone").unbind('click').bind('click', function () {
phone = !phone;
$('#div_phone').css('background', phone ? green_on : green_off);
});
}
wireSearchIcons();

Simple jQuery issue - how to check if $(this) is the last $(this) clicked?

I am essentially adding radio-buttons functionality to buttons with the class "tagbutton". There is a flaw with my code, because _this is never _last...
taguid = "";
_last = "";
$('.tagbutton').live('click', function() {
_this = $(this);
if(_last) {
//There was a last object
if(_last == _this) { // The last object was the current object
alert('deactiveate for this obj');
} else { // The last object was not the current object
alert('deactivate last, activate this');
}
} else {
alert('first object activated');
var taguid = $(this).prev().attr('data-uid');
alert(taguid);
_last = $(this);
}
});
It's because the objects' references aren't the same. A simpler way might be to activate the clicked one, then deactivate the last one. It'll have the same effect:
var taguid = "";
var _last;
$('.tagbutton').live('click', function() {
if(_last) {
// There was a last object
// Activate this
// Deactivate last
} else {
alert('first object activated');
var taguid = $(this).prev().attr('data-uid');
alert(taguid);
_last = $(this);
}
});
The jQuery function $() returns a jQuery object, and when you call it a second time you get back a different object.
However, within your event handler the keyword this refers to the actual DOM element, so if you save that directly then your comparison should work:
_this = this;
// and then later
_last = this;

Categories

Resources