Toggling click handlers in Javascript

12,860

Solution 1

Update: Since this form of toggle() was removed in jQuery 1.9, the solution below does not work anymore. See this question for alternatives.

It looks like toggle() would solve your problem:

$("#mybutton").toggle(myFirstHandlerFunction, mySecondHandlerFunction);

The code above will register myFirstHandlerFunction and mySecondHandlerFunction to be called on alternate clicks.

Solution 2

Just use a boolean to toggle the functionality of the handler, there's no need to juggle which handler is listening:

$('#mybutton').bind('click', myHandlerFunction);
var first = true;

function myHandlerFunction(e) {
    if(first){
        // Code from the first handler here;
    }else{
        // Code from the second handler here;
    }
    first = !first; // Invert `first`
}

Solution 3

This solution is a hack, but it is short and sweet for your rough work:

$('#myButton').click(function() {
  (this.firstClk = !this.firstClk) ? firstHandler(): secondHandler();
});

It's a hack because it's putting a new property directly onto this which is the click-target HTML DOM element, and that's maybe not best practice. However, it thus avoids creates any new globals, and it can be used unchanged on different buttons simultaneously.

Note that the first part of the ternary operation uses = and not == or ===, i.e. it's an assignment, not a comparison. Note also that the first time the button is clicked, this.firstClk is undefined but is immediately negated, making the first part of the ternary operation evaluate to true the first time.

Here's a working version:

$('#a > button').click(function() {(this.firstClk = !this.firstClk) ? a1(): a2();});
$('#b > button').click(function() {(this.firstClk = !this.firstClk) ? b1(): b2();});

function a1() {$('#a > p').text('one');}
function a2() {$('#a > p').text('two');}

function b1() {$('#b > p').text('ONE');}
function b2() {$('#b > p').text('TWO');}
div {display: inline-block;width: 10em;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="a"><p>not yet clicked</p><button>click</button></div>
<div id="b"><p>NOT YET CLICKED</p><button>CLICK</button></div>
Share:
12,860
Nathan Friend
Author by

Nathan Friend

Remote Fullstack Engineer @ Stripe and cellist based out of Woodstock, Ontario. Previously @ GitLab. Fascinated with building usable, delightful software. GitLab profile Résumé My latest side project

Updated on June 04, 2022

Comments

  • Nathan Friend
    Nathan Friend almost 2 years

    I have an HTML button to which I attach an event, using jQuery's bind(), like so:

    $('#mybutton').bind('click', myFirstHandlerFunction);
    

    In myFirstHandlerFunction, I'd like this handler to replace itself with a new handler, mySecondHandlerFunction, like this:

    function myFirstHandlerFunction(e) {
        $(this).unbind('click', myFirstHandlerFunction).bind('click', mySecondHandlerFunction);
    }
    

    In the second click handler, mySecondHandlerFunction, I'd like to toggle the button back to its original state: unbind the mySecondHandlerFunction handler and reattach the original handler, myFirstHandlerFunction, like so:

    function mySecondHandlerFunction(e) {
        $(this).unbind('click', mySecondHandlerFunction).bind('click', myFirstHandlerFunction);
    }
    

    This works great, except for one small detail: because the click event has not yet propagated through each of the button's click handlers, the click event is passed on to the button's next click handler, which happens to be the handler that was just bound in the previous handler. The end result is mySecondHandlerFunction being executed immediately after myFirstHandlerFunction is executed.

    This problem can be easily solved by calling e.stopPropagation() in each handler, but this has the negative side-effect of cancelling any other click handlers that may have been attached independently.

    Is there a way to safely and and consistently toggle between two click handlers, without having to stop the propagation of the click event?