views:

71

answers:

4

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'
    });
}​
+2  A: 

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?

J-P
How may I save the commom properties in the formEnhancement class?
You can save the common properties in the formEnhancement prototype. formEnhancement.prototype = {} ...this way every instance of formEnhancement shares these common methods and any objects that inherit formEnhancement will have access to these methods as well
elduderino
+1  A: 

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

madeinstefano
+1  A: 

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();
Nico
+1  A: 

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.

galambalazs