I am trying to write my javascript code using modular pattern, but i am facing problem while calling the functions inside and also there are some security problems. I can easily open the console and type in my namespace name . variable and change the variable value.
JS
Below JS Code is working fine, but as i said i can change the gridValue from console. How do i avoid this. i will be using this variable across all my functions.
var myTicTacToe = {
turn:'X',
score: {
'X': 0,
'O': 0
},
gridValue: 0,
fnLoad:function () {
var select = document.getElementById("grid");
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
}
}
window.onload = function(){
myTicTacToe.fnLoad();
}
Below one is giving me problem while calling the fnLoad.
var myTicTacToe = function(){
var turn = 'X',
score = {
'X': 0,
'O': 0
},
gridValue = 0,
fnLoad = function () {
var select = document.getElementById("grid");
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
}
}
window.onload = function(){
myTicTacToe.fnLoad();
}
How can i protect my gridValue or any of the variable in First pattern ?
Why i am not able to call fnLoad just like first one ? how do i call it ?
Whats the difference between these 2 patterns ?
In the first code when i declared any variable using var, it gave me error. Why is it so ?
What you are referring to is the Revealing Module Pattern. Everything is private unless you explicitly return it
var myTicTacToe = (function(){
var turn = 'X',
score = {
'X': 0,
'O': 0
},
gridValue = 0,
fnLoad = function () {
var select = document.getElementById("grid");
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
};
return {
load: fnLoad //fnLoad is now publicly named "load".
};
})();
window.onload = function(){
myTicTacToe.load();
}
/* Or even
window.onload = myTicTacToe.load; //Because it's a single function.
*/
As for your concrete questions:
You can't. All object members are public by default with the normal Module Pattern.
Because you haven't returned it, see the example above.
One allows for private members, and one does not. The revealing pattern allows for privates.
I didn't understand which variable we are talking about. But if it's an object member, it should not be declared with a var.
You can use a closure to make gridValue a private variable. Consider this pattern:
var myTicTacToe = (function(){
var turn = 'X'.
score = {
'X': 0,
'O': 0
},
gridValue = 0;
return {
fnLoad: function () {
var select = document.getElementById("grid");
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
}
};
})();
window.onload = function(){
myTicTacToe.fnLoad();
}
Here we use an IIFE to close in the local variables so that only the fnLoad function has access to them. Later the fnLoad function is returned and invoked fill the namespace so it can be public ally accessed by other parts of your program.
This pattern can be extremely useful. Read more here: http://css-tricks.com/how-do-you-structure-javascript-the-module-pattern-edition/
This links is my constant reference when working with javascript. It does an amazing job of explaining the different patterns to use with javascript and I bet will help solve your issue.
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#designpatternsjavascript
Here is your code refactored to protect your private variables and only allow methods to be public that you declare. I am using the "Revealing Modular Pattern" below.
var myTicTacToe = (function () {
// PRIVATE AREA
var turn = 'X';
var score = {
'X': 0,
'O': 0
};
var gridValue = 0;
function fnLoad() {
var select = document.getElementById("grid");
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
}
// PUBLIC METHODS
return {
Load: fnLoad
}
})();
then call it like
myTicTacToe.Load();
If you need to pass a dependency in such as jQuery or another module you've created then you would create the parameter in the (function(HERE) and then pass it in at the bottom in the last set of parentheses. Here is an example of your code with jQuery.
var myTicTacToe = (function ($) {
// PRIVATE AREA
var turn = 'X';
var score = {
'X': 0,
'O': 0
};
var gridValue = 0;
function fnLoad() {
var select = $('#grid');
for (i = 3; i <= 100; i += 1) {
var option = document.createElement('option');
select.options[select.options.length] = new Option(i + ' X ' + i, i);
}
}
// PUBLIC METHODS
return {
Load: fnLoad
}
})($);
Related
I want to access variables ie. distance, vertex2Position, path which in two seperate function, inside main function called getResult. How can I achieve this without altering my code or altering my code in minimum way.
function getResult() {
document.getElementById("vertex1").onchange = function() {
var vertex1 = document.getElementById("vertex1").value;
var vertex1Position = graph.node.findIndex(e => e.id == vertex1) + 1;
document.getElementById("output").textContent = vertex1Position;
var distance = execute(vertex1Position); // How can I access distance in my result variable
};
var vertex2Position = 0;
console.log("whats here");
document.getElementById("vertex2").onchange = function() {
var vertex2 = document.getElementById("vertex2").value;
vertex2Position = graph.node.findIndex(e => e.name == vertex2)+ 1; // I also want to access vertex2Position in my result variable which is in outer function
document.getElementById("secondOutput").textContent = vertex2Position;
var path = getPath(vertex2Position); //How can I access path in var result
};
var result = distance.vertex2Position; // I want to store distance and vertex2Position in result variable
document.getElementById("searchResult").innerHTML = "test" + result + "" + path + "."; // I also want to access path
}
You should use something like this :
var container = (function(){
var distance;
var vertex2P;
return {
setDistance: function(distance){
this.distance = distance;
},
getDistance: function(){return this.distance;},
setVertex2P: function(vertex2P){
this.vertex2P = vertex2P;
},
getVertex2P: function(){return this.vertex2P;},
}}());
And then you can get and set the values in other functions like this
var result = function(){
container.setDistance(2);
container.setVertex2P(3);
console.log(container.getDistance() + container.getVertex2P());
}
result(); // 5
These are(maybe ) the best practices you can use in Javascript with this you avoid the global variables and added privacy to your variables, hope it helps you.
P.S you can short this with ECMASCRIPT 6
In javascript, you need understand about scopes. In your code, the
main scope is the getResult() function, so if you want to access
variables inside sub functions (functions inside the getResult()
function), you'll need declare the variables at beginning of this main
scope.
Example:
function getResult() {
var distance,
path,
vertex1,
vertex2,
vertex1Position,
vertex2Position = 0;
document.getElementById("vertex1").onchange = function() {
vertex1 = document.getElementById("vertex1").value;
vertex1Position = graph.node.findIndex(e => e.id == vertex1) + 1;
document.getElementById("output").textContent = vertex1Position;
distance = execute(vertex1Position);
}
document.getElementById("vertex2").onchange = function() {
vertex2 = document.getElementById("vertex2").value;
vertex2Position = graph.node.findIndex(e => e.name == vertex2)+ 1;
document.getElementById("secondOutput").textContent = vertex2Position;
path = getPath(vertex2Position); //How can I access path in var result
};
result = distance.vertex2Position;
document.getElementById("searchResult").innerHTML = "test" + result + "" + path + ".";
}
Note: You're using functions triggered by "onchange" event, so your variables will initiate as undefined, except for "vertex2Position"
So when I execute the following code it gets the value. Though the program goes through and clicks some stuff for me and the value naturally changes. But I wish to save the value of this beforehand and then compare it to the second value.
Executed code :
var Category = [];
var ID1;
var ID2;
var dispatchMouseEvent = function(target, var_args) {
var e = document.createEvent("MouseEvents");
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
}
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry "); //GETTING LEVEL 1 CATS
var Level1CatsLen = Level1Cats.length; //GETTING LEVEL 1 CAT LEN
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(i) { //GO TO NEXT LEVEL!
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
function GetLevel2() { //GET NEXT LEVEL
var Level2Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
return Level2Cats.length;
}
setTimeout(GoToLevel2(i),100); //RUN IT WITH TIMING
var Level2CatsLen = GetLevel2();
// END OF LEVEL 1
var extracats2 = Level2CatsLen - Level1CatsLen;
console.log(extracats2+"e");
if (extracats2 !== 2 || extracats2 !== 0) {
for (ii = 0; ii < extracats2; ii++) { //LEVEL 2
console.log(ii);
ID2 = Level2Cats[ii+Level1CatsLen].id;
var temp2 = Level2Cats[ii+Level1CatsLen].innerHTML;
temp2.replace(/&/, "&").replace(/<[^>]*>/gi, "");
var Level2Children = [];
for (l = 0; l < level1CatsLen; l++) {
Level2Children.push(Level2Cats[l].id);
}
//DO SOMETHING WITH CATEGORIES - Level 1
Category.push({Name: temp1, ID: ID2, ParentID: 'null', ChildrenIDs: Level2Children});
//FINISH
Though when it finishes, if I call Level1CatsLen it is not 16, which is the inital number it is now 33 which is the final stage.
Any ideas how one would go about doing this?
Updated answer:
There are a couple of issues here.
You have function declarations within control structures, which is a syntax error:
// ...
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(i) { // <=== Error
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
// ...
}
Browsers have a tendency to tolerate it, but the way they tolerate it varies from browser to browser. To create a function within a control structure, you need to use a function expression, not a function declaration:
var GoToLevel2 = function(i) {
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
};
But see #2.
There's no need to recreate your functions on every loop, if you're going to pass i into them anyway.
Your setTimeout call is incorrect:
setTimeout(GoToLevel2(i),100);
That calls GoToLevel2, passing in i, and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.
To set up a timed callback to GoToLevel2, you use the function reference. To ensure that it receives a specific value (i), you can use Function#bind to get a new function that will call GoToLevel2 with that i:
setTimeout(GoToLevel2.bind(null, i),100);
It's possible there are further issues, those are the ones that jumped out at me.
Original answer:
You're doing that, on this line:
var Level1CatsLen = Level1Cats.length;
As the DOM changes, Level1Cats.length may change (because getElementsByClassName returns a live NodeList), but Level1CatsLen won't.
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
var Level1CatsLen = Level1Cats.length;
snippet.log("Initial: Level1CatsLen = " + Level1CatsLen + ", Level1Cats.length = " + Level1Cats.length);
tick();
function tick() {
var div = document.createElement('div');
div.className = "p-pstctgry-lnk-ctgry";
div.innerHTML = String(Level1Cats.length + 1);
document.body.appendChild(div);
snippet.log("Updated: Level1CatsLen = " + Level1CatsLen + ", Level1Cats.length = " + Level1Cats.length);
if (Level1Cats.length < 10) {
setTimeout(tick, 500);
}
}
.p-pstctgry-lnk-ctgry {
border: 1px solid #888;
color: green;
}
<div class="p-pstctgry-lnk-ctgry">1</div>
<div class="p-pstctgry-lnk-ctgry">2</div>
<div class="p-pstctgry-lnk-ctgry">3</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I have created a simple ad serving script, that you would call with global parameters, then call an external script that uses the parameters to display the adverts.
<script>
var ad_width = 100;
var ad_height = 200;
var ad_div = 'someDivID';
</script>
<script src='./js/ads.js'></script>
The ads.js has a function like:
(function(){
displayads = function(){
// do stuff here
}
displayads();
});
This works but when I have a second implementation the global variables are just over written, I need some form of encapsulation, javascript is not my foremost language so I am not sure what I am looking for.
I suppose if you want to allow multiple easy to configure ads then you could store them in an array:
var ads = [
{
width: 100,
height: 200,
div: 'someDivID'
},
...
];
Or if you think that's too hard for your customers you could allow them to write their variables like the following:
var ad1_width = 100;
var ad1_height = 200;
var ad1_div = 'someDivID';
var ad2_width = 200;
var ad2_height = 100;
var ad2_div = 'someOtherDivID';
Then in your code, you could check for their variables by checking against the window object:
for (var i = 1; ; i++) {
var widthKey = 'ad' + i + '_width',
heightKey = 'ad' + i + '_height',
divKey = 'ad' + i + '_div';
if (window.hasOwnProperty(widthKey)) {
displayAd(window[widthKey], window[heightKey], window[divKey]);
} else {
break;
}
}
It's weird and not exactly The Right Way(tm) but it does make it easy for end users to configure. Just make sure your customers always number them sequentially if you follow that way and that they're not using any other JS that could possible use any of the same variable names.
Another possibility would be to setup a property on the window to listen for when those variables are being set:
var adWidths = [],
adHeights = [],
adContainers = [];
Object.defineProperty(window, 'ad_width', {
set: function(val) {
adWidths.push(val);
}
});
Object.defineProperty(window, 'ad_height', {
set: function(val) {
adHeights.push(val);
}
});
Object.defineProperty(window, 'ad_div', {
set: function(val) {
adContainers.push(val);
}
});
Now your customers can set their ads just by doing:
ad_width = 200;
ad_height = 100;
ad_div = 'someDivID';
ad_width = 100;
ad_height = 200;
ad_div = 'someOtherDivID';
And you can access each ad with something like:
for (var i = 0, len = adWidths.length; i < len; i++) {
displayAd(adWidths[i], adHeights[i], adContainers[i]);
}
But this does require you to run some code both before they run there's and after. This solution is not compatible with older versions of IE either.
I am using an old version of UltraWebGrid by Infragistics and need to replace some of the built in javascript. The compiled js looks like its adding a bunch of functions to an object type as an api of sorts. formatted like:
var igtbl_ptsBand = ["functionname1",function(){...},"functionname2",function(){...},...
and so on. How would I override this?
Basically the control is adding html to the page in a way that is not compatible with newer browsers and the javascript code that does this just needs a little tweak. I found the code... I just need to change it.
The code can be found here
I added an answer to dump code examples in and whatnot. I will not select this answer
Similar SO question
The array you mentioned seems to be a function table of sorts:
var igtbl_ptsBand = ["func1", function() { }, "func2", function() { } ]
I would recommend using chaining instead of just an override. With chaining you can inject your own code, but still call the original function. Let's say you want to replace "func2" and chain. You could do something like this:
var origFunc, findex, ix;
if (igtbl_ptsBand.indexOf) {
// indexOf is supported, use it
findex = igtbl_ptsBand.indexOf("func2") + 1;
} else {
// Crippled browser such as IE, no indexOf, use loop
findex = -1;
for (ix = 0; ix < igtbl_ptsBand.length; ix += 2) {
if (igtbl_ptsBand[ix] === "func2") {
findex = ix + 1;
break;
}
}
}
if (findex >= 0) {
// Found it, chain
origFunc = igtbl_ptsBand[findex];
igtbl_ptsBand[findex] = function() {
// Your new pre-code here
// Call original func (chain)
origFunc();
// Your new post-code here
};
}
origFunc may have arguments, of course, and you may want to use the JavaScript call() function to set the "this pointer" to something specific, e.g.:
origFunc.call(customThis, arg1, arg2...);
If the arguments are in an array, you can use apply() instead of call().
I would not recommend doing this. You should always try to work with a third party library, not against it. That being said, this should work:
igtbl_ptsBand[igtbl_ptsBand.indexOf("functionYouWantToOverwrite") + 1] = function () {
// your new stuff...
};
Ok here is what I am doing. I will update this with my progress.
This fixes my problem. It turns out I had to apply the functionarray to all child objects of the parent "rows". To do this I added the code in my "Fix Rows" function. I split it up because i am running accross other browser JS error which I am fixing in this js file.
Here is the js file that I added to my .net page like so...
</form>
<script type="text/javascript" src="../../scripts/BrowserCompat.js"></script>
</body>
</html>
.
Brows_FixUltraWebGrid();
function Brows_FixUltraWebGrid() {
FixRows();
}
function FixRows() {
FixGridRows_render();
for (var i = 0; i < igtbl_ptsRows.length; i += 2)
igtbl_Rows.prototype[igtbl_ptsRows[i]] = igtbl_ptsRows[i + 1];
}
function FixGridRows_render() {
var origFunc, findex, ix;
if (igtbl_ptsRows.indexOf) {
// indexOf is supported, use it
findex = igtbl_ptsRows.indexOf("render") + 1;
} else {
// Crippled browser such as IE, no indexOf, use loop
findex = -1;
for (ix = 0; ix < igtbl_ptsRows.length; ix += 2) {
if (igtbl_ptsRows[ix] === "render") {
findex = ix + 1;
break;
}
}
}
if (findex >= 0) {
// Found it, chain
origFunc = igtbl_ptsRows[findex];
igtbl_ptsRows[findex] = function() {
// Your new pre-code here
// Call original func (chain)
//origFunc();
// Your new post-code here
var strTransform = this.applyXslToNode(this.Node);
if (strTransform) {
var anId = (this.AddNewRow ? this.AddNewRow.Id : null);
//new logic to include tbody if it is not there
var tadd1 = '';
var tadd2 = '';
if (!(/\<tbody\>/.test(strTransform))) {
tadd1 = '<tbody>';
tadd2 = '</tbody>';
}
this.Grid._innerObj.innerHTML =
"<table style=\"table-layout:fixed;\">" + tadd1 + strTransform + tadd2 + "</table>";
//old line
//this.Grid._innerObj.innerHTML = "<table style=\"table-layout:fixed;\">" + strTransform + "</table>";
var tbl = this.Element.parentNode;
igtbl_replaceChild(tbl, this.Grid._innerObj.firstChild.firstChild, this.Element);
igtbl_fixDOEXml();
var _b = this.Band;
var headerDiv = igtbl_getElementById(this.Grid.Id + "_hdiv");
var footerDiv = igtbl_getElementById(this.Grid.Id + "_fdiv");
if (this.AddNewRow) {
if (_b.Index > 0 || _b.AddNewRowView == 1 && !headerDiv || _b.AddNewRowView == 2 && !footerDiv) {
var anr = this.AddNewRow.Element;
anr.parentNode.removeChild(anr);
if (_b.AddNewRowView == 1 && tbl.tBodies[0].rows.length > 0)
tbl.tBodies[0].insertBefore(anr, tbl.tBodies[0].rows[0]);
else
tbl.tBodies[0].appendChild(anr);
}
this.AddNewRow.Element = igtbl_getElementById(anId);
this.AddNewRow.Element.Object = this.AddNewRow;
}
this.Element = tbl.tBodies[0];
this.Element.Object = this;
this._setupFilterRow();
for (var i = 0; i < this.Band.Columns.length; i++) {
var column = this.Band.Columns[i];
if (column.Selected && column.hasCells()) {
var col = this.getColumn(i);
if (col)
igtbl_selColRI(this.Grid.Id, col, this.Band.Index, i);
}
}
if (this.ParentRow) {
this.ParentRow.ChildRowsCount = this.length;
this.ParentRow.VisChildRowsCount = this.length;
}
}
console.log('overridden row render function executed');
};
}
}
Hallo,
I have 3 Different function in Javascript, the first one replaces HTML Selectboxs width custom selectbox created with ULs.
and the other 2 replace Checkbox and Radio buttons respectivly.
Now I want to derive classes out of these functions, and need your suggestions, what will be the best way to organize these functions into class, whether inheretance is possible?
I really appriciate your help.
Thanks.
Here is some sample code.
function replaceSelect(formid) {
var form = $(formid);
if (!form) return;
invisibleSelectboes = document.getElementsByClassName("optionsDivInvisible");
if (invisibleSelectboes.length > 0) {
for (var i = 0; i < invisibleSelectboes.length; i++) {
document.body.removeChild(invisibleSelectboes[i]);
}
}
var selects = [];
var selectboxes = form.getElementsByTagName('select');
var selectText = "Bitte auswählen";
var selectRightSideWidth = 21;
var selectLeftSideWidth = 8;
selectAreaHeight = 21;
selectAreaOptionsOverlap = 2;
// Access all Selectboxes in Search mask.
for (var cfs = 0; cfs < selectboxes.length; cfs++) {
selects.push(selectboxes[cfs]);
}
// Replace the select boxes
for (var q = 0; q < selects.length; q++) {
if (selects[q].className == "") continue;
var onchangeEvent = selects[q].onchange;
//create and build div structure
var selectArea = document.createElement('div');
var left = document.createElement('div');
var right = document.createElement('div');
var center = document.createElement('div');
var button = document.createElement('a');
// var text = document.createTextNode(selectText);
var text = document.createTextNode('');
center.id = "mySelectText" + q;
if ( !! selects[q].getAttribute("selectWidth")) {
var selectWidth = parseInt(selects[q].getAttribute("selectWidth"));
} else {
var selectWidth = parseInt(selects[q].className.replace(/width_/g, ""));
}
center.style.width = selectWidth + 'px';
selectArea.style.width = selectWidth + selectRightSideWidth + selectLeftSideWidth + 'px';
if (selects[q].style.display == 'none' || selects[q].style.visibility == 'hidden') {
selectArea.style.display = 'none';
}
button.style.width = selectWidth + selectRightSideWidth + selectLeftSideWidth + 'px';
button.style.marginLeft = -selectWidth - selectLeftSideWidth + 'px';
// button.href = "javascript:toggleOptions( + q + ")";
Event.observe(button, 'click', function (q) {
return function (event) {
clickObserver(event, q)
}
}(q));
button.onkeydown = this.selectListener;
button.className = "selectButton"; //class used to check for mouseover
selectArea.className = "selectArea";
selectArea.id = "sarea" + q;
left.className = "left";
right.className = "right";
center.className = "center";
right.appendChild(button);
center.appendChild(text);
selectArea.appendChild(left);
selectArea.appendChild(right);
selectArea.appendChild(center);
//hide the select field
selects[q].style.display = 'none';
//insert select div
selects[q].parentNode.insertBefore(selectArea, selects[q]);
//build & place options div
var optionsDiv = document.createElement('div');
if (selects[q].getAttribute('width')) optionsDiv.style.width = selects[q].getAttribute('width') + 'px';
else optionsDiv.style.width = selectWidth + 8 + 'px';
optionsDiv.className = "optionsDivInvisible";
optionsDiv.id = "optionsDiv" + q;
optionsDiv.style.left = findPosX(selectArea) + 'px';
optionsDiv.style.top = findPosY(selectArea) + selectAreaHeight - selectAreaOptionsOverlap + 'px';
//get select's options and add to options div
for (var w = 0; w < selects[q].options.length; w++) {
var optionHolder = document.createElement('p');
if (selects[q].options[w].className == "informal") {
var optionLink = document.createElement('a');
var optionTxt = document.createTextNode(selects[q].options[w].getAttribute('text'));
optionLink.innerHTML = selects[q].options[w].getAttribute('text');
optionLink.className = "informal";
cic.addEvent(optionLink, 'click', function (event) {
Event.stop(event);
});
Event.observe(optionLink, 'mouseover', function (event) {
Event.stop(event);
});
Event.observe(optionLink, 'mouseout', function (event) {
Event.stop(event);
});
}
else {
var optionLink = document.createElement('a');
var optionTxt = document.createTextNode(selects[q].options[w].text);
optionLink.appendChild(optionTxt);
cic.addEvent(optionLink, 'click', function (id, w, q, onchangeEvent) {
return function () {
showOptions(q);
selectMe(selects[q].id, w, q, onchangeEvent);
}
}(selects[q].id, w, q, onchangeEvent));
}
//optionLink.href = "javascript:showOptions(" + q + "); selectMe('" + selects[q].id + "'," + w + "," + q + ");";
optionHolder.appendChild(optionLink);
optionsDiv.appendChild(optionHolder);
if (selects[q].options[w].selected) {
selectMe(selects[q].id, w, q);
}
}
document.getElementsByTagName("body")[0].appendChild(optionsDiv);
Event.observe(optionsDiv, 'mouseleave', function (submenuid) {
optionsDiv.className = 'optionsDivInvisible'
});
cic.addEvent(optionsDiv, 'click', function (event) {
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
});
}
form.setStyle({
visibility: 'visible'
});
}
From the sounds of it, you're looking to create a unified API to encapsulate all of this "form enhancing" functionality. Possibly something like this:
var formEnhancement = {
SelectBox: function(){ /* ... */ },
CheckBox: function(){ /* ... */ },
RadioButton: function(){ /* ... */ }
};
formEnhancement.SelectBox.prototype = { /* ... define methods ... */ };
// etc. (other prototypes)
// Call something:
var myEnhancedSelectBox = new formEnhancement.SelectBox(
document.getElementById('id-of-a-select-box')
);
Does this answer your query?
I'd go with
var Library = (function()
{
function _selectBox()
{
// stuff
}
function _checkBox()
{
// stuff
}
function _radioButton()
{
// stuff
}
return {
SelectBox : _selectBox,
CheckBox : _checkBox,
RadioButton : _radioButton
};
})();
or
var Library = (function()
{
return {
SelectBox : function()
{
// stuff
},
CheckBox : function()
{
// stuff
},
RadioButton : function()
{
// stuff
}
};
})();
[Edit]
this way, you can actually declare "private" variables that can be accessible only from the library itself, just declaring var foo="bar"; inside Library's declaration, makes a foo variable that can't be accessed from outside, but can be accessed by anything within Library, this is why functions like _selectBox in my example remain private, but can still be accessed through Library.SelectBox, which would be the "public getter"
[/Edit]
also, instead of
var Library = (function(){})();
you could do something like this:
var Library = Library || {};
Library.UI = (function(){})();
this way, you can keep separate parts of your code library, you can keep them in separate files, which don't care about the order in which they are loaded, as long as they have
var Library = Library || {};
on top of them
the functions would then be called like this:
Library.SelectBox();
or in the case you chose to go with "subclasses"
Library.UI.SelectBox();
All the answers are general patterns I think none of them is really helpful. Just because you put your 3 huge function into an object doesn't make your code modular, reusable, maintainable.
So my first suggestion is to utilize function decomposition. You've mentioned inheritance. Now if your code is basically made of this 3 giant functions nothing can be inherited or shared. You should separate function logic by purpose into smaller, more straighforward ones.
A good example is that you've mentioned the word replacing is relevant in all your cases. Maybe you can set up a function that is responsible for DOM replacement independently of the element's type. Such function can be shared between your modules making your code more robust and allowing you to DRY.
The best way to organize this process is called wishful thinking, when you solve your problem with functions which are intuitive and helpful even though they may not even exist. This is related to how you can design effective interaces.
Put the functions in a namespace:
Declare it like this:
FormUtils = {};
and add its properties, which will be your functions
FormUtils.replaceSelect = function () {/*your code*/};
FormUtils.replaceCheckbox = function () {/*your code*/};
FormUtils.replaceRadio = function () {/*your code*/};
then you call this functions with their namespace:
FormUtils.replaceSelect();
This is a simple and very accepted design pattern to javascript