I'm attempting to reuse the code from here Edit a variable within an array to create something simmilar but less complex.
I made the 'working' function:
var WorkT = function(gain,loss,message) {
coins += this.gain;
coins -= this.loss;
this.message = message;
}
workT1 = new WorkT(30,0,'<span class="red">+ 30 Gold Coins');
workT2 = new WorkT(15,0,'<span class="red">+ 15 Gold Coins');
workT3 = new WorkT(80,0,'<span class="red">+ 80 Gold Coins');
workT4 = new WorkT(1,0,'<span class="red">+ 1 Gold Coin');
WorkTs = [workT1,workT2,workT3,workT4];
And I'm trying to call it later on in my code with this:
$('#output').html(WorkTs[Math.floor(Math.random() * 4)].WorkT());
But, when I click the button, nothing changes. Can anyone tell me why?
Your WorkT instances have no WorkT() function.
You need to declare a function named WorkT (or else) inside your WorkT 'class' :
var WorkT = function(gain,loss,message) {
//...
this.work = function () {
//Do Something.
}
}
Or you won't be able to call it on your instances :
$('#output').html(WorkT[Math.floor(Math.random() * 4)].work());
It all depends on what you're trying to achieve here.
There are lots of errors in your code.
What is the coins in the WorkT Constructor? Is it a global variable, or you wanted to do this.coins?
this.gain and this.loss does not exist. They are just gain and loss.
There is no function called WorkT in the class WorkT. You probably want to define the function, too.
Here is how the code will look like, assuming coins is a global variable.
var WorkT = function(gain,loss,message) {
coins += gain;
coins -= loss;
this.message = message;
this.work = function(){
//Do something here.
}
}
And,
$('#output').html(WorkTs[Math.floor(Math.random() * 4)].work());
Related
I've the following script:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
});
});
And within the following code I would like to re-run the update(); function.
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
datumbtn.className = 'jaar';
dimensions1 = 'ga:month,ga:nthMonth';
start1 = moment(now).date(1).month(0).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(1, 'year').date(1).month(0).format('YYYY-MM-DD');
end2 = moment(now).date(1).month(0).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'M';
format2 = 'MMM';
update();
}
else {
datumbtn.className = 'maand';
dimensions1 = 'ga:date,ga:nthWeek';
start1 = moment(now).subtract(2, 'day').date(1).format('YYYY-MM-DD');
end1 = moment(now).format('YYYY-MM-DD');
start2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'month').format('YYYY-MM-DD');
end2 = moment(now).subtract(2, 'day').date(1).subtract(1, 'day').format('YYYY-MM-DD');
format1 = 'YYYYMMDD';
format2 = 'Do';
update();
}
}
But somehow this doesn't work. I also tried in the above script:
window.update = function (data) {}. But that also doesn't work.
How can I call the update(); function that is situated inside the gapi.analytics.ready(function() {} ?
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready().
It's really a simple matter of moving the function declaration
function update (data) {
// same as existing code
}
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update );
});
And passing in data needed when you call it in your other function
function datumwissel( datumbtn ) {
if ( datumbtn.className == 'maand' ) {
..........
update(datumbtn);
}.......
Important is that I cannot make it globally as it has to be situated inside the gapi.analytics.ready()
That's not actually true - you can have it global and there at the same time. Whether you want to, is a different manner, as that would pollute the global namespace and so on. However, here is how that can be achieved:
First, extract the update function outside of the ready handler like so
function update (data) {
var title = document.getElementById('view-name');
title.innerHTML = data.property.name + ' (' + data.view.name + ')';
activeUsers.set(data).execute();
renderWeekOverWeekChart(data.ids);
renderTopBrowsersChart(data.ids);
renderTopCountriesChart(data.ids);
setTimeout(function() {
var list = document.getElementsByTagName("tr")[0];
list.getElementsByTagName("th")[0].innerHTML = "Pagina's";
list.getElementsByTagName("th")[1].innerHTML = "Paginaweergaven";
}, 500);
}
This will create a new function with the name update which accepts one parameter called data. Thanks to hoisting it would not matter if it's before or after anywhere you want to use it, as it would be effectively "pulled" to the top.
Next, you can just use the function inside the ready handler like so:
gapi.analytics.ready(function() {
viewSelector.on('viewChange', update);
});
Since .on(events, handler) accepts a function as the second parameter, you can just provide a function reference there. It doesn't matter that your function is technically declared elsewhere, as it is still going to be called with the same arguments. Similarly, if you replace update with alert you will be giving the reference to window.alert so you will get an alert with data.
With that, you can just call the same function in your other piece of code.
That is true for any place that uses callbacks, including setTimeout - you can just give a function reference and it's going to be called. Internally, those kinds of functions almost always do something like callback() or callback(someData), occasionally callback.call(/* parameters */) where callback is the passed in argument. Whether you define that argument as you are calling the function, e.g., selector.on("click", function() {/* code */}) or separately, e.g.,
function clickHandler() { /* code */ }
selector.on("click", clickHandler)
matters little.
With that said, whether you want the function global is a different matter. Unless both pieces of code are in the same place, a global function may be the easiest way. You could, also, namespace anything your app uses, which would partially avoid the global pollution. Not completely, but sometimes you just need to have things living under window if you have multiple files, in which case, you can define your own little corner there to play with: window.myApp = window.myApp || {} would create a new object that can serve as namespace and so you will be able to do things like myApp.update = function(data) { /* code */ } and thus share that code.
If your two pieces of code are indeed in one file, then you merely need to create the function outside both using var update = function(data) { /* code */ } then hand it to each in the exact same way, since update is still going to be a function reference, however, if assigned to a variable, it won't be added to the global namespace (nor would the declaration be hoisted).
I have a checkbox and when the user clicks on it I want it to change the private variable output. then I want to update an object's property to the output. In the code below I can't get the change event to work I believe it's because I don't know how to interact with DOM elements through the Module pattern. so if you can help with that it would be great.
Something else that concerns me is that I want the price variable in the apple object to be updated when ever the user click. the price will change values to a random number every time the user clicks the checkbox. I always have a problem updating an object outside of a change event like you will see in my code. apple.price is outside change event. If you could me with that would be gratefully appreciated too.
var CreatItems = function(name, price, pic){
this.name = name;
this.price = price;
this.pic = pic;
}
CreatItems.prototype.listOut = function(){
console.log(this.name + this.price + this.pic)
}
var changingPrice = (function(){
// testMethod : function(){
// return 1.77
// }
var output;
var testMethod = function(){
return output
}
var clickedWithOnes = function(){
$("#withones").change(function(){
if(this.checked){
// output = "checked";
console.log("Checked")
}else{
// output = "unchecked";
console.log("unchecked")
}
})
}
return{
testMethod : testMethod,
clickedWithOnes : clickedWithOnes
}
})()
changingPrice.clickedWithOnes()
var price = changingPrice.testMethod()
// console.log(price)
var apple = new CreatItems("apple", price, "will be red div")
console.log(apple.price)
I get consoled "undefined"
Hi I created listing for you, review it: link here new
You have a next issue, you work almost good, you have just one issue: when you return your output value it returns it like an value, but if you want to keep connection between output and new apple variable you should return it like an object in this case it will be returned not like an value but like a link to the object.
I see some javascript and try to implement the function seperated to reuse it.
This is the old code:
var ListRenderRenderWrapper = function(itemRenderResult, inCtx, tpl)
{
var iStr = [];
iStr.push('<li>');
iStr.push(itemRenderResult);
iStr.push('</li>');
return iStr.join('');
}
And I would like to make something like this:
function wrapItems(itemRenderResult, inCtx, tpl)
{
var iStr = [];
iStr.push('<li>');
iStr.push(itemRenderResult);
iStr.push('</li>');
return iStr.join('');
}
var ListRenderRenderWrapper = wrapItems(itemRenderResult, inCtx, tpl);
is this ok or do I need to do it in another way?
If you just want to assign that function to a new variable so you can call it with a different name, simply do:
var ListRenderRenderWrapper = wrapItems;
The confusion may be coming from the fact that in JavaScript a function can be stored inside a variable and called as a function later.
This means that:
function thing() { /* code */ }
is the same as:
var thing = function() { /* code */ }
(Aside: I know there are subtle differences with hoisting etc, but for the purposes of this example they are the same).
I've been trying to organize code in a javascript project and have ended up with a bunch of nested functions. Supposedly that's bad practice, and I know it affects performance, but I'm having trouble coming up with an alternative. Here's an example of what I'm trying to do:
Code before nesting:
function Level1(dep1, dep2, dep3, dep4){
var tempResult1 = dep1 + dep2;
var tempResult2 = tempResult1/2;
var tempResult3 = dep3 + dep4;
var mainResult = tempResult2 + tempResult3;
return mainResult;
}
What it looks like after I try to separate responsibilities into a hierarchy:
function Level1(dep1, dep2, dep3, dep4){
var tempResult2 = getTempResult2(dep1, dep2);
var tempResult3 = getTempResult3(dep3, dep4);
var mainResult = tempResult2 + tempResult3;
return mainResult;
function getTempResult2(dep1, dep2){
var tempResult1 = getTempResult1(dep1, dep2);
return tempResult1/2;
function getTempResult1(dep1, dep2){
return dep1 + dep2;
}
}
function getTempResult3(dep3, dep4){
return dep3 + dep4;
}
}
Obviously for the operations used here, functions are a bit excessive, but it does help make the ones in my project a lot more manageable. I'm unfamiliar with any other way to do this, as this is my first javascript project. The suggestions I found on here only dealt with 1 level of nested functions, not 2, and I didn't see any good examples of how to implement nested scoping. If someone could give me an example of a way to accomplish the organization I'm looking for here, I'd be very greatful. Thanks.
have ended up with a bunch of nested functions
You can simply unnest them, since they're not used as closures (you pass everything necessary as arguments). You will even get a little performance advantage by not creating a local function object every time the outer function is called (which is well optimized though and probably negligible).
I just read a bunch about how nested functions were bad practice
Notice that sometimes nested functions are necessary, when you want to create a closure.
I don't like the fact that getTempResult1, 2, and 3 are accessible outside of Level1, or that getTempResult1 is accessible outside of getTempResult2.
You could use an IEFE to create an extra scope from which only Level1 is exported:
var Level1 = (function() {
function Level1(dep1, dep2, dep3, dep4) {
var tempResult2 = getTempResult2(dep1, dep2);
var tempResult3 = getTempResult3(dep3, dep4);
var mainResult = tempResult2 + tempResult3;
return mainResult;
}
var getTemptResult2 = (function() {
function getTempResult2(dep1, dep2) {
var tempResult1 = getTempResult1(dep1, dep2);
return tempResult1/2;
}
function getTempResult1(dep1, dep2) {
return dep1 + dep2;
}
return getTempResult2;
})();
function getTempResult3(dep3, dep4) {
return dep3 + dep4;
}
return Level1;
}());
Maybe this is what you want:
function Level1(dep1, dep2, dep3, dep4){
var tempResult2 = Level1.getTempResult2(dep1, dep2);
var tempResult3 = Level1.getTempResult3(dep3, dep4);
var mainResult = tempResult2 + tempResult3;
return mainResult;
}
Level1.getTempResult2 = function (dep1, dep2) {
var tempResult1 = Level1.getTempResult2.getTempResult1(dep1, dep2);
return tempResult1/2;
}
Level1.getTempResult2.getTempResult1 = function (dep1, dep2){
return dep1 + dep2;
}
Level1.getTempResult3 = function (dep3, dep4){
return dep3 + dep4;
}
Currently I tried
function a(val1, val2) { return a.foo(val1, val2) }
a.foo = function (x,y) { return x + y }
in my browser. The command a(1,2) prints 3 as aspected. Other example:
function a() { return a.foo(1,2) }
a.foo = function (x,y) { return a.foo.bar(x,y) }
a.foo.bar = function (x,y) { return x+y }
a(1,2) // -> 3
This is a design question. Since a good and elegant design depends always on quite a number of aspects of your problem domain, it is impossible from the simplified code in your question to really estimate what the best solution would be. I'll try to show you some options which in each case address ways to do data hiding and avoid nested functions or functions that are created multiple times.
Since you ask for data hiding and keeping getTempResult1 hidden from anything other than getTempResult2, I will assume that each of these functions are reasonably complex and might be written by different people, and you want to keep the internals hidden. That warrants creating a different class for each of these rather than just a function.
I will substitute your example code with a more tangible example and use a proper OOP approach to solve the problem. Let's say you are building an e-commerce application. Level1 will be an invoice class, and dep1-4 might be things like: purchase price, profit rate, discount rate, tax rate. We will make a very simple application which will calculate: purchase price - discount + profit + taxes = total price
I hope this ressembles your problem faithfully enough so you can appreciate some of the OOP techniques used (it's still way overkill in structure for the calculations done, but it's a lesson in OOP and allows for a great deal in scalability should the problem domain get more complex in the future, say you go international and must calculate taxes for different countries etc).
I will use the OoJs library to be able to do proper OOP in JavaScript. See the code below working on jsfiddle.
The idea of an ecommerce app is inspired from the book "Dessign patterns explained" by Shalloway and Trott
In conclusion you will find that this solution:
hides implementation details
there are no nested functions
every function is created only once
is scalable and maintainable and flexible in case of changing
requirements
So the code using our classes will look as follows:
// Create your namespace to avoid polluting global
//
var eComm = eComm || {}
// this will be some factory code which will return the needed objects, it won't actually use them.
// Normally this should be a class living in our eComm namespace
//
function factory( db, clientID )
{
// we would assume here that the hardcoded rates would be found in the database using the client id.
//
var discount = new eComm.Discount( 5 ) // in %
var profit = new eComm.Profit ( 20, discount ) // in %
var taxRate = new eComm.TaxRate ( 5 , profit ) // in %
// note that I use a simple aggragation approach, because I don't know the
// actual complexity of your problem domain. It makes this very simple ecommerce
// code not entirely ideal. If we would just perform a chain of operations on
// a number, other design patterns would be more suited, like a decorator.
// It is not appropriate to just pass taxRate to Invoice, because it is no different
// than profit or discount, it just comes later in a chain of calculations.
// I have taken this approach to show that it is possible to hide steps of the
// implementation down a hierarchy.
//
return new eComm.Invoice( taxRate )
}
// now when we will actually use it, it looks like this
// wrapped it in a function call because on global scope
// we would have to put this entirely at the bottom
// if you put all your code in classes you don't have this
// problem. They can occur in any order
//
function usage()
{
var invoice = factory( "database", 1654 /* the client id */ )
invoice.addPurchase( 1574 ) // price in some currency
invoice.addPurchase( 1200 ) // a second purchase
// in reality you would probably also pass an object representing an output
// device to Invoice (a printer, or a pdf generator, ...)
//
console.log( invoice.total() )
}
The actual classes. It looks long, but that's because the less they do, the bigger the overhead (relatively speaking). I ommit more and more code as we go down for brevity as the classes all look very much alike.
;( function class_Invoice( namespace )
{
'use strict'; // recommended
if( namespace[ "Invoice" ] ) return // protect against double inclusions
namespace.Invoice = Invoice
var Static = OoJs.setupClass( namespace, "Invoice" )
// constructor
//
function Invoice( taxRate )
{
// should do validation as javascript is loosely typed
//
if( "TaxRate" !== OoJs.typeOf( taxRate ) )
;// throw an error
// Data members
//
this.taxRate = taxRate
this.totalPrice = 0
this.Protected( "taxRate", "totalPrice" ) // if you want them available to subclasses
var iFace = this.Public( total, addPurchase ) // make these methods public
return iFace
}
// all your method definitions go here
//
function addPurchase( price )
{
this.totalPrice += this.taxRate.calculate( price )
}
function total()
{
return this.totalPrice
}
})( eComm )
;( function class_TaxRate( namespace )
{
namespace.TaxRate = TaxRate
var Static = OoJs.setupClass( namespace, "TaxRate" )
// constructor
//
function TaxRate( rate, profit )
{
// do your validation on profit and rate as above
this.rate = rate
this.profit = profit
this.Protected( "profit" ) // if you want
return this.Public( calculate )
}
function calculate( price )
{
return this.profit.calculate( price ) * ( 1 + this.rate / 100 )
}
})( eComm )
;( function class_Profit( namespace )
{
namespace.Profit = Profit
var Static = OoJs.setupClass( namespace, "Profit" )
// constructor
//
function Profit( rate, discount )
{
this.rate = rate
this.discount = discount
return this.Public( calculate )
}
function calculate( price )
{
return this.discount.calculate( price ) * ( 1 + this.rate / 100 )
}
})( eComm )
;( function class_Discount( namespace )
{
namespace.Discount = Discount
var Static = OoJs.setupClass( namespace, "Discount" )
// constructor
//
function Discount( rate )
{
this.rate = rate
return this.Public( calculate )
}
function calculate( price )
{
return price - price * this.rate / 100
}
})( eComm )
usage()
you don't need to worry about the call stack getting a few levels deep.
this is an example of premature optimization
Try looking at the step module, which is intended for node.js. I use it all the time.
However, you might be to use the step.js script even outside of the node.js environment (note: I have not tested this). At the very least, it shows how you can flatten any number of levels of nesting.
I know this is already answered but I thought I would leave you with some additional resources to help you on this adventure. I think this is a perfect time for you to look deeper into javascript design patterns.
Learning JavaScript Design Patterns by Addy Osmani is a fantastic read / resource to learn about multiple patterns for creating javascript applications, creating reusable code, closures etc. Anyone who is having this internal disscussion on how to better organize my nested functions / scope, etc should read it.
Here is an example snippet from his article regarding The Factory Pattern
// Types.js - Constructors used behind the scenes
// A constructor for defining new cars
function Car( options ) {
// some defaults
this.doors = options.doors || 4;
this.state = options.state || "brand new";
this.color = options.color || "silver";
}
// A constructor for defining new trucks
function Truck( options){
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
this.color = options.color || "blue";
}
// FactoryExample.js
// Define a skeleton vehicle factory
function VehicleFactory() {}
// Define the prototypes and utilities for this factory
// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;
// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {
switch(options.vehicleType){
case "car":
this.vehicleClass = Car;
break;
case "truck":
this.vehicleClass = Truck;
break;
//defaults to VehicleFactory.prototype.vehicleClass (Car)
}
return new this.vehicleClass( options );
};
// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
vehicleType: "car",
color: "yellow",
doors: 6 } );
// Test to confirm our car was created using the vehicleClass/prototype Car
// Outputs: true
console.log( car instanceof Car );
// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );
Hope this article helps you and others looking for similar answers.
It's a good idea to have a function implement as less as possible for optimal re use. For example:
function doChores(){
//actually wash the dishes
//actually walk the dog.
}
Now let's say it's raining and I only want to wash the dishes, since washing the dishes is implemented in doChores I can't call it without walking the dog. Here is how it should be done:
function doChores(){
walkTheDog();
washTheDishes();
}
The function walkTheDog implements walking the dog and washTheDishes implements washing the dishes so they can be called sperately.
The problem you're facing is when you pass variables to a chain of functions. I usually pass one argument to a function and that argument contains an object with the needed parameters. Every function can read or mutate members of the passed object that they are concerned with. If at a later time you need to add more arguments then you don't need to change the signature of your funciton (for example function(arg1, arg2, newArg)) you'll always have function(args)
More info about the parameter passing can be found here: https://stackoverflow.com/a/16063711/1641941 under Passing (constructor) arguments
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.