Adi
Adi

Reputation: 4022

javascript - reduce repetitiveness click events

I am just getting into my javascript (patterns, namespaces, objects, etc) and am trying to put in practice some of these on my latest project.

I have a simple form and I am tring to show/hide something based on a checkbox being checked. The HTML cant be changed (long story) so I am only focusing on the javascript issue at hand, which is, how can I nicely reduce repetitiveness when there is more then one ID?

The script (which Im sure can be condensed) is below:

var prereg = {

    hideItems: (function() {
        document.getElementById('companyName-element').style.display = 'none';
        document.getElementById('companyName-label').style.display = 'none';
    })(),

    showItems: function() {
        var checkbox = document.getElementById('businessCustomer'); 
        if(checkbox.checked) {
            document.getElementById('companyName-element').style.display = 'block';
            document.getElementById('companyName-label').style.display = 'block';
        } else {
            document.getElementById('companyName-element').style.display = 'none';
            document.getElementById('companyName-label').style.display = 'none';
        }
    },

    addEvent: function (el, ev, fn) {
         if (el.addEventListener) {
             el.addEventListener(ev, fn, false);
         } else if (el.attachEvent) {
             el.attachEvent('on' + ev, fn);
         } else {
             el['on' + ev] = fn;
         }
     }

}

prereg.addEvent(document.getElementById('businessCustomer-label'), 'click', prereg.showItems);
prereg.addEvent(document.getElementById('businessCustomer-element'), 'click', prereg.showItems);

Upvotes: 1

Views: 135

Answers (2)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76395

I think you're looking for event delegation, which is a fancy way of saying using a single handler for all events of the same type. Well, even different types are an option. But just an example:

suppose your html looks like this:

<form id="formId"><input type="checkbox" id="foo"/>
    <input type="checkbox" id="bar"/>

And so on (can't be bothered). Your JS could handle all click events with 1 event listener and handler:

var form = document.getElementById('formId');
if (form.addEventListener)
{
    form.addEventListener('click',clickDelegator,false);
}
else
{
    form.attachEvent('onclick',clickDelegator);
}
function clickDelegator(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;//target holds reference to clicked element
    if (target.tagName.toLowerCase() !== 'input')
    {
        //not interested in this, just let the event pass
        return e;// or return true; or return;... doesn't matter
    }
    switch(target.type.toLowerCase())//target is an input field, we've established that
    {
        case 'checkbox':
            //code for checkboxes;
            //target is a checkbox, use target.checked, target.id, as normal
        break;
        case 'button':
            //deal with buttons here
        break;
        //etc...
    }
}

If you want 1 delegator-function to process all events, you can, but then you'll have to switch...case all e.type's, of course which usually ends up in a useless, unmaintainable monter-function. But just so you know: it is possible.

Need closures? sure, no problem:

form.addEventListener('click',(function(someVar)
{
    return function(e)
    {
        e = e || window.event;
        clickDelegator.apply(this,[e,someVar]);
        //pass as argument, the clickDelegator was declared outside closure scope
        //and doesn't have access to someVar, but this way, it has the closure var
        //as an argument
    }
})(theClosureVar),false);

Google some more examples of event delegation, you won't regret it... it's one of those features of JavaScript that are sorely underrated and not as well known as they should. I wish I could give you a better example of the closure -delegator combination, because that's an exceptionally powerful one. Take mobile devices for instance: there's no click event, only a touchstart and touchend event. So you set the touchstart handler to bind a touchend listener, and add a timeout id via closure to the handler for the touchend's event. If some time has passed the user is probably not tapping the element, but using some other touch feature of his/her phone, so you can detach the listener.

I'm not good at explaining this, but look at some examples of how a custom tab event works, it usually boils down to something similar to my dribble here, now I'm going to sleep... good luck

Upvotes: 1

Matt Esch
Matt Esch

Reputation: 22956

Without restructuring your code too much, you just want to cache the objects you want to manipulate in an array and process them in a loop.

var prereg = (function () {

    // add your style elements to this array. We only call getElementId once
    // and not on each function call.
    var styleObjects = [ 
            document.getElementById('companyName-element'),
            document.getElementById('companyName-label')
        ],

        checkbox = document.getElementById('businessCustomer'),

        i;

    // Function applies display style to all elements in styleObjects array
    function applyStyle(display) {
        var i;
        for (i = 0; i < styleObjects.length; i += 1) {
            styleObjects[i].style.display = display;
        }
    }

    function hideItems() {
        applyStyle('none');
    }

    function showItems() {            
        if(checkbox.checked) {
            applyStyle('block');
        } else {
            applyStyle('none');
        }
    }

    function addEvent(el, ev, fn) {
        if (el.addEventListener) {
         el.addEventListener(ev, fn, false);
        } else if (el.attachEvent) {
         el.attachEvent('on' + ev, fn);
        } else {
         el['on' + ev] = fn;
        }
    }    

    // Attach event handler to all objects in styleObjects array
    for (i = 0; i < styleObjects.length; i += 1) {
        addEvent(styleObjects[i], 'click', showItems);
    }

    return {
        hideItems: hideItems,
        showItems: showItems,
        addEvent: addEvent
    };
}());

It would be trivial to modify this to accept the checkbox and array of elements to style as an argument if you wanted to do this multiple times.

Upvotes: 0

Related Questions