views:

96

answers:

2

Background

I would like to simulate the "active" behavior that most browsers support for links when they are clicked. For any element and event I would like to be able to add a css class to the element for the duration of the event. So in psuedo jQuery code I'd like to do this:

$(".button").click(function() { alert('clicked'); });

.....

$(".button").each( function()
    {
     var self = $(this);
     var innerClick = self.click;
     self.click( function()
      {
       self.addClass('active');
       innerClick();
       self.removeClass('active');
      });
    });

Bonus points for anyone that can suggest a solution that is robust enough to maintain the wrapping if I bind further handlers to 'click'.

A: 

Well, for the bonus point, it comes for free as you can as many handler you want to the same event. No regarding your initial question, I didn't get the point. Do you want to simulate the "active" behavior on non-link element? If so, binding the mousedown/mouseup envents to addClass/removeClass methods should do the trick.

gizmo
The mousedown, mouseout doesn't quite do it. Suppose the click action on the non-link element is a AJAX request that takes 3 seconds to come back. The "active" behavior on a link is that the link is active until the click returns.
the repeated bind doesn't quite fix it. Supposed I do:clickbindOne$myMagicWrapperclickBindTwothen the events will fire a bit like this:addClass('active');bindOneFuncremoveClass('active');bindTwoFuncwhen I want:addClass('active');bindOneFuncbindTwoFuncremoveClass('active');
+1  A: 

The problem is there is no general way to determine when an asynchronous call is done. You would need to set some kind of flag after the AJAX call (for example) is done and poll it with setInterval.

With synchronous functions, the browser probably won't update the display until the click handler is done anyways, so you would never see the active state. You can get around that by using setTimeout to remove the active class after 1/10 of a second. For this case, you could write a function like this:

function makeClickHandler(innerFunction) {
    return function(event) {
        var self = this;
        $(self).addClass('active');

        innerFunction.call(self, event);

        window.setTimeout(function() {
            $(self).removeClass('active');
        }, 100);
    }
}

You could then wrap any click handlers in the makeClickHandler function:

$(".button").click(makeClickHandler(function() { alert('clicked'); }));

Here's an example of how you could handle asynchronous events, but at this point, it might just be easier to add/remove the class in the individual event handlers.

function makeAsyncClickHandler(innerFunction) {
    return function(event) {
        var self = this;
        $(self).addClass('active').data("done", false);

        innerFunction.call(self, event);

        var intervalID = window.setInterval(function() {
            if ($(self).data("done")) {
                $(self).removeClass('active');
                window.clearInterval(intervalID);
            }
        }, 100);
    }
}

The inner function would have to call

$(this).data("done", true);

when the asynchronous part is finished.

It would probably be easier to write "start" and "finish" functions that you can call on the element at the beginning of the function and after the event is finished.

Matthew Crumley