The following script works correctly although I need to make few amends. In each function I am getting the values need for the different formulas. However I tend to replicate the same line of code in different functions.
Ex.
function one(){ var v1= document.getElementById('one').value; }
function two(){ var v1= document.getElementById('one').value; }
Full code
I would like to declare all of the variables once and than only use the ones I need for the specific functions. If I declare them right at the top than once they are called they still hold the original value so I need to update that value to the current one if changed of course.
Your code will be very hard to read if you do it like in your fiddle.
Instead do
var myVars;
window.onload=function() {
myVars = {
'list_price': document.getElementById('list_price'),
'negotiated': document.getElementById('negotiated'),
.
.
'lease_payment': document.getElementById('lease_payment')
}
now you can do
var price = myVars.list_price.value;
or perhaps add a function
function getVal(id) {
var val = document.getElementById(id).value;
if (val =="" || isNaN(val)) return 0;
return parsetInt(val,10);
}
now you can do
var price = getVal("list_price");
mplungjan's solution is a great one. If you're at all concerned by your global vars leaking into the window scope, wrap your code in an Immediately Invoked Function Expression to prevent that from happening:
(function(){
// code goes here
}());
There are two ways to go about this:
Update your variable when the value changes
Use a function that always returns the correct value
1) You can add a listener for the change event or the keyup event that changes your global variable:
// save initial value
var val = document.getElementById('one').value;
// update the value when input is changed
addEventListener(document.getElementById('one'), 'change', function() {
val = document.getElementById('one').value;
});
console.log(val);
2) You can use a function that always returns the current value:
var val = function() { return document.getElementById('one').value; };
console.log(val());
2b) If you hate parenthesis, you can define a property that uses the function above as a getter:
Object.defineProperty(window, 'one', {
get : function() { return document.getElementById('one').value; }
});
console.log(one);
Related
I've got a list of items that have different data values that I'm trying to return on a click event. I can't seem to pass the value from the click event to the parent function. I may just be doing this all wrong. I just need the data value from each click event to replace the value of my systemName variable. Can someone help me out here?
function getSystemName(){
var systemName = '';
$('.pulldown li').bind('click', function(){
var systemName = '';
var systemName = $(this).find('a').data('value');
console.log(systemName);
});
return systemName;
}
// Edit: I should probably explain that I'm trying to get that value from the click event to update content on the page via jquery .load so perhaps like mentioned below....I'm not doing this properly because I can log the systemName properly on click but it doesn't seem to update the page content. Here is the entire code block.
$.ajaxSetup ({
cache: false
});
var ajax_load = "<img src='.../images/130.gif' alt='loading...' align='middle' />";
var loadUrl = ".../content.php";
var getSystemName = (function(){
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
return function() { return systemName };
})();
$("#results")
.html(ajax_load)
.load(loadUrl, "ki_systems=" +getSystemName());
In the code in the question, each time you call getSystemName() it will bind a click handler again (resulting in multiple click handlers) and then just return the local systemName variable that will always be blank.
If you are saying that each time you call getSystemName() you want it to return the system name associated with whichever li element was most recently clicked then you can declare the systemName variable as a global variable and bind the event handler (once) outside getSystemName():
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
function getSystemName() {
return systemName;
}
But that makes the function rather redundant: you could just refence the global variable directly.
On the other hand you could declare just the function and use a closure to avoid the need for global variables, something like this:
var getSystemName = (function(){
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
return function() { return systemName };
})();
// if no items have been clicked yet it will return an empty string by default
console.log( getSystemName() ); // ""
// click on an item with name "red"
console.log( getSystemName() ); // "red"
// click on an item with name "green"
console.log( getSystemName() ); // "green"
That last block could be a little confusing to the uninitiated. It is setting getSystemName to whatever is returned from an anonymous function expression that is executed immediately (note the extra empty parentheses at the end). And what is returned is a simple function that when called itself will return systemName, which is declared in the outer scope. The click handler also updates this same systemName. So when you actually call getSystemName() you are calling the little one-liner function from the return statement.
I think the problem here is that you're missing synchronous and asynchronous programming - you probably want a callback on the getSystemName() function. I expect there's something more architecturally wrong with the approach you're taking.
However, this is what it'd look like if I wrote that using your style:
function getSystemName(callback) {
$('.pulldown li').bind('click', function(){
console.log(systemName);
callback( $(this).find('a').data('value') );
});
}
So instead of this sort of code:
console.log("System Name: ", getSystemName())
You would have:
getSystemName( function (name) { console.log("System Name: ", name); } );
Remove the var from the second and third definitions. If you use var before the variable name a new local instance will be created.
systemName = '';
systemName = $(this).find('a').data('value');
console.log(systemName);
Consider this, on the second line you're only binding a callback function to the click event, not executing the code inside that function.
You can't.
Look at the code.
Set systemName to an empty string
When a list item is clicked set systemName to something else
Return systemName
You can't make 3 wait for 2. You need to do something with the data in the click handler and not try to return it.
Well, i suppose you are trying to set a value to see which node has been click, thus you need to change the scope of your systemName variable. Move it out so it will be global, like so:
var systemName = '';
$('.pulldown li').bind('click', function(){
systemName = $(this).find('a').data('value');
console.log(systemName);
});
Is it possible to change the state of a toggle function? Like:
myDiv.toggle ... function 1 , function 2
I click on the myDiv element, the function 1 executes
I click again, function 2
I click again, function 1
BUT
Change the state
function 1 again
etc.
But I need to be able to change the state from outside the toggle function.
Here is a javascript object that uses closure to track it's state and toggle:
var TOGGLER = function() {
var _state = true;
var _msg = "function1";
var function1 = function() {
_msg = "function1";
}
var function2 = function() {
_msg = "function2";
}
return {
toggle: (function () {
_state = !_state;
if (_state) {
function1();
} else {
function2();
}
return _msg;
})
}
}();
Here is a jsfiddle that shows how to use it to toggle based with the following jquery: http://jsfiddle.net/yjPKH/5/
$(document).ready(function() {
$("#search").click(function() {
var message = TOGGLER.toggle();
$("#state").text(message);
});
});
The toggle function is meant for simple use cases. Changing the state externally is not "simple" anymore.
You cannot easily/safely (it's internal so it may change during minor versions) access the state variable of the toggle function easily as it's stored in the internal dataset of the element.
If you really want to do it, you can try this code though:
$._data(ELEMENT, "lastToggle" + func.guid, 0);
func is the function you passed to .toggle(), so you need to save this function in a variable. Here's a minimal example: http://jsfiddle.net/xqgrP/
However, since inside the function there's a var guid = fn.guid || jQuery.guid++ statement, I somehow think that the devs actually meant to use guid instead of func.guid for the _data key - in that case a minor update is very likely to break things. And after the fix you'd have to iterate over the data set to retrieve the correct key as there is no way to access the guid from outside.
I am trying to organize my code using the revealing module pattern.
I have a very basic question about how to set up a setter method.
$(document).ready(function() {
var designs = (function() {
var curRow,
setCurRow = function(val) {
curRow = val;
},
initTable = function() {
setCurRow(0);
};
return {
curRow : curRow,
setCurRow : setCurRow,
initTable : initTable
}
}) ();
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.curRow);
});
The problem is that i dont get the someNewVal in the console output, I get undefined instead! I have a feeling I am doing something pretty silly here.
You can also solve this in another way by understanding the scopes of the variables and functions involved.
When you return your object constructor { curRow: curRow ... }, that just initializes the object member named curRow to the value of the variable curRow in the scope of the anonymous function; it doesn't create any persistent connection between them.
Once the anonymous function returns, calling designs.setCurRow is updating the curRow variable in that scope exactly as you expect, but that variable is now totally inaccessible to the outside world -- there is no connection between it and the curRow member of designs.
You can solve this by making the setCurRow method operate on this.curRow, as in the other solutions. In that case you don't need to make curRow a variable in the original scope, since it's entirely unused. The other solution is to add a 'getter' method to your current one:
var designs = (function() {
var curRow,
setCurRow = function(val) {
curRow = val;
},
getCurRow = function() {
return curRow;
},
initTable = function() {
setCurRow(0);
};
return {
getCurRow : getCurRow,
setCurRow : setCurRow,
initTable : initTable
};
}) ();
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.getCurRow());
Because getCurRow and setCurRow are functions that are closed in the scope containing the variable varRow, they can reach back into that scope and access and change variables that are only accessible within it.
In this case making curRow a member of the object you return is probably simpler, but the other way is useful too since you can use it to create effectively private members and methods.
Looks like you want an object, not a module:
$(document).ready(function() {
var designs = {
setCurRow: function(val) {
this.curRow = val;
},
initTable: function() {
this.setCurRow(0);
},
curRow: 0
};
designs.initTable();
designs.setCurRow(someNewVal);
console.log(designs.curRow);
});
The problem is that setCurRow sets the value of the variable curRow after designs.curRow has already been set. Consider something like this:
var a = 1;
b = a; // sets b = a = 1
b = 2; // sets b = 2; leaves a = 1
Your code is doing the same thing, but with object-properties and setter methods to make it look complicated. :-)
As ruakh pointed out, you never re-assign curRow on the returned object, so it is always the default value. Change it to:
setCurRow = function(val) {
this.curRow = curRow = val;
},
And everything should work*.
* At least mostly - you won't be able to use call and apply on setCurRow (or pass it to setTimeout or setInterval without binding it first to your object (designs), since this is bound at call time in JavaScript.
I seem to have an issue when creating copies of a template and tying the .click() method to them properly. Take the following javascript for example:
function TestMethod() {
var test = Array();
test[0] = 0;
test[1] = 1;
test[2] = 2;
// Insert link into the page
$("#test_div").html("<br>");
var list;
for (x = 0; x < test.length; x++) {
var temp = $("#test_div").clone();
temp.find('a').html("Item #" + test[x]);
temp.click(function () { alert(x); });
if (list == undefined)
list = temp;
else
list = list.append(temp.contents());
}
$("#test_div2").append(list);
}
The problem I am seeing with this is that no matter which item the user clicks on, it always runs alert(2), even when you click on the first few items.
How can I get this to work?
Edit: I have made a very simple example that should show the problem much clearer. No matter what item you click on, it always shows an alert box with the number 2 on it.
Correct me if I'm wrong, .valueOf() in JS returns the primitive value of a Boolean object.....
this would not happen ShowObject(5,'T');... ShowObject(objectVal.valueOf(), 'T');
why not use objects[x].Value directly? ShowObject(objects[x].Value, 'T');
WOOOOOSSSHHHH!
after searching deeply... I found a solution...
because it's a closure, it won't really work that way...
here's a solution,
temp.find('a').bind('click', {testVal: x},function (e) {
alert(e.data.testVal);
return false;
});
for best explanation, please read this... in the middle part of the page where it says Passing Event Data a quick demo of above code
I think your issue arises from a misunderstanding of scopes in JavaScript. (My apologies if I'm wrong.)
function () {
for (...) {
var foo = ...;
$('<div>').click(function () { alert(foo); }).appendTo(...);
}
}
In JavaScript, only functions create a new scope (commonly referred to as a closure).
So, every round of the for loop will know the same foo, since its scope is the function, not the for. This also applies to the events being defined. By the end of looping, every click will know the same foo and know it to be the last value it was assigned.
To get around this, either create an inner closure with an immediately-executing, anonymous function:
function () {
for (...) {
(function (foo) {
$('<div>').click(function () { alert(foo); }).appendTo(...);
})(...);
}
}
Or, using a callback-based function, such as jQuery.each:
function () {
$.each(..., function (i, foo) {
$('<div>').click(function () { alert(foo); }).appendTo(...);
});
}
For your issue, I'd go with the latter (note the changes of objects[x] to just object):
var list;
jQuery.each(data.objects, function (x, object) {
// Clone the object list item template
var item = $("#object_item_list_template").clone();
// Setup the click action and inner text for the link tag in the template
var objectVal = object.Value;
item.find('a').click(function () { ShowObject(objectVal.valueOf(), 'T'); }).html(object.Text);
// add the html to the list
if (list == undefined)
list = item;
else
list.append(item.contents());
});
I edited the question so it would make more sense.
I have a function that needs a couple arguments - let's call it fc(). I am passing that function as an argument through other functions (lets call them fa() and fb()). Each of the functions that fc() passes through add an argument to fc(). How do I pass fc() to each function without having to pass fc()'s arguments separately? Below is how I want it to work.
function fa(fc){
fc.myvar=something
fb(fc)
}
function fb(fc){
fc.myothervar=something
fc()
}
function fc(){
doessomething with myvar and myothervar
}
Below is how I do it now. As I add arguments, it's getting confusing because I have to add them to preceding function(s) as well. fb() and fc() get used elsewhere and I am loosing some flexibility.
function fa(fc){
myvar=something
fb(fc,myvar)
}
function fb(fc,myvar){
myothervar=something
fc(myvar,myothervar)
}
function fc(myvar,myothervar){
doessomething with myvar and myothervar
}
Thanks for your help
Edit 3 - The code
I updated my code using JimmyP's solution. I'd be interested in Jason Bunting's non-hack solution. Remember that each of these functions are also called from other functions and events.
From the HTML page
<input type="text" class="right" dynamicSelect="../selectLists/otherchargetype.aspx,null,calcSalesTax"/>
Set event handlers when section is loaded
function setDynamicSelectElements(oSet) {
/**************************************************************************************
* Sets the event handlers for inputs with dynamic selects
**************************************************************************************/
if (oSet.dynamicSelect) {
var ySelectArgs = oSet.dynamicSelect.split(',');
with (oSet) {
onkeyup = function() { findListItem(this); };
onclick = function() { selectList(ySelectArgs[0], ySelectArgs[1], ySelectArgs[2]) }
}
}
}
onclick event builds list
function selectList(sListName, sQuery, fnFollowing) {
/**************************************************************************************
* Build a dynamic select list and set each of the events for the table elements
**************************************************************************************/
if (fnFollowing) {
fnFollowing = eval(fnFollowing)//sent text function name, eval to a function
configureSelectList.clickEvent = fnFollowing
}
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', configureSelectList); //create the div in the right place
var oSelected = event.srcElement;
if (oSelected.value) findListItem(oSelected)//highlight the selected item
}
Create the list
function setDiv(sPageName, sQuery, sClassName, fnBeforeAppend) {
/**************************************************************************************
* Creates a div and places a page in it.
**************************************************************************************/
var oSelected = event.srcElement;
var sCursor = oSelected.style.cursor; //remember this for later
var coords = getElementCoords(oSelected);
var iBorder = makeNumeric(getStyle(oSelected, 'border-width'))
var oParent = oSelected.parentNode
if (!oParent.id) oParent.id = sAutoGenIdPrefix + randomNumber()//create an ID
var oDiv = document.getElementById(oParent.id + sWindowIdSuffix)//see if the div already exists
if (!oDiv) {//if not create it and set an id we can use to find it later
oDiv = document.createElement('DIV')
oDiv.id = oParent.id + sWindowIdSuffix//give the child an id so we can reference it later
oSelected.style.cursor = 'wait'//until the thing is loaded
oDiv.className = sClassName
oDiv.style.pixelLeft = coords.x + (iBorder * 2)
oDiv.style.pixelTop = (coords.y + coords.h + (iBorder * 2))
XmlHttpPage(sPageName, oDiv, sQuery)
if (fnBeforeAppend) {
fnBeforeAppend(oDiv)
}
oParent.appendChild(oDiv)
oSelected.style.cursor = ''//until the thing is loaded//once it's loaded, set the cursor back
oDiv.style.cursor = ''
}
return oDiv;
}
Position and size the list
function configureSelectList(oDiv, fnOnClick) {
/**************************************************************************************
* Build a dynamic select list and set each of the events for the table elements
* Created in one place and moved to another so that sizing based on the cell width can
* occur without being affected by stylesheet cascades
**************************************************************************************/
if(!fnOnClick) fnOnClick=configureSelectList.clickEvent
if (!oDiv) oDiv = configureSelectList.Container;
var oTable = getDecendant('TABLE', oDiv)
document.getElementsByTagName('TABLE')[0].rows[0].cells[0].appendChild(oDiv)//append to the doc so we are style free, then move it later
if (oTable) {
for (iRow = 0; iRow < oTable.rows.length; iRow++) {
var oRow = oTable.rows[iRow]
oRow.onmouseover = function() { highlightSelection(this) };
oRow.onmouseout = function() { highlightSelection(this) };
oRow.style.cursor = 'hand';
oRow.onclick = function() { closeSelectList(0); fnOnClick ? fnOnClick() : null };
oRow.cells[0].style.whiteSpace = 'nowrap'
}
} else {
//show some kind of error
}
oDiv.style.width = (oTable.offsetWidth + 20) + "px"; //no horiz scroll bars please
oTable.mouseout = function() { closeSelectList(500) };
if (oDiv.firstChild.offsetHeight < oDiv.offsetHeight) oDiv.style.height = oDiv.firstChild.offsetHeight//make sure the list is not too big for a few of items
}
Okay, so - where to start? :) Here is the partial function to begin with, you will need this (now and in the future, if you spend a lot of time hacking JavaScript):
function partial(func /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(this, allArguments);
};
}
I see a lot of things about your code that make me cringe, but since I don't have time to really critique it, and you didn't ask for it, I will suggest the following if you want to rid yourself of the hack you are currently using, and a few other things:
The setDynamicSelectElements() function
In this function, you can change this line:
onclick = function() { selectList(ySelectArgs[0], ySelectArgs[1], ySelectArgs[2]) }
To this:
onclick = function() { selectList.apply(null, ySelectArgs); }
The selectList() function
In this function, you can get rid of this code where you are using eval - don't ever use eval unless you have a good reason to do so, it is very risky (go read up on it):
if (fnFollowing) {
fnFollowing = eval(fnFollowing)
configureSelectList.clickEvent = fnFollowing
}
And use this instead:
if(fnFollowing) {
fnFollowing = window[fnFollowing]; //this will find the function in the global scope
}
Then, change this line:
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', configureSelectList);
To this:
var oDiv = setDiv(sListName, sQuery, 'dynamicSelect', partial(configureSelectListAlternate, fnFollowing));
Now, in that code I provided, I have "configureSelectListAlternate" - that is a function that is the same as "configureSelectList" but has the parameters in the reverse order - if you can reverse the order of the parameters to "configureSelectList" instead, do that, otherwise here is my version:
function configureSelectListAlternate(fnOnClick, oDiv) {
configureSelectList(oDiv, fnOnClick);
}
The configureSelectList() function
In this function, you can eliminate this line:
if(!fnOnClick) fnOnClick=configureSelectList.clickEvent
That isn't needed any longer. Now, I see something I don't understand:
if (!oDiv) oDiv = configureSelectList.Container;
I didn't see you hook that Container property on in any of the other code. Unless you need this line, you should be able to get rid of it.
The setDiv() function can stay the same.
Not too exciting, but you get the idea - your code really could use some cleanup - are you avoiding the use of a library like jQuery or MochiKit for a good reason? It would make your life a lot easier...
A function's properties are not available as variables in the local scope. You must access them as properties. So, within 'fc' you could access 'myvar' in one of two ways:
// #1
arguments.callee.myvar;
// #2
fc.myvar;
Either's fine...
Try inheritance - by passing your whatever object as an argument, you gain access to whatever variables inside, like:
function Obj (iString) { // Base object
this.string = iString;
}
var myObj = new Obj ("text");
function InheritedObj (objInstance) { // Object with Obj vars
this.subObj = objInstance;
}
var myInheritedObj = new InheritedObj (myObj);
var myVar = myInheritedObj.subObj.string;
document.write (myVar);
subObj will take the form of myObj, so you can access the variables inside.
Maybe you are looking for Partial Function Application, or possibly currying?
Here is a quote from a blog post on the difference:
Where partial application takes a function and from it builds a function which takes fewer arguments, currying builds functions which take multiple arguments by composition of functions which each take a single argument.
If possible, it would help us help you if you could simplify your example and/or provide actual JS code instead of pseudocode.