[javascript] jQuery click / toggle between two functions

I am looking for a way to have two separate operations / functions / "blocks of code" run when something is clicked and then a totally different block when the same thing is clicked again. I put this together. I was wondering if there was a more efficient / elegant way. I know about jQuery .toggle() but it kind of sucks.

Working here: http://jsfiddle.net/reggi/FcvaD/1/

var count = 0;
$("#time").click(function() {
    count++;
    //even odd click detect 
    var isEven = function(someNumber) {
        return (someNumber % 2 === 0) ? true : false;
    };
    // on odd clicks do this
    if (isEven(count) === false) {
        $(this).animate({
            width: "260px"
        }, 1500);
    }
    // on even clicks do this
    else if (isEven(count) === true) {
        $(this).animate({
            width: "30px"
        }, 1500);
    }
});

This question is related to javascript jquery

The answer is


one line solution: basic idea:

$('sth').click(function () {
    let COND = $(this).propery == 'cond1' ? 'cond2' : 'cond1';
    doSomeThing(COND);
})

examples on jsfiddle

example 1, changing innerHTML of an element in a toggle-mode:

$('#clickTest1').click(function () {
    $(this).html($(this).html() == 'click Me' ? 'clicked' : 'click Me');
});

example 2, toggling displays between "none" and "inline-block":

$('#clickTest2, #clickTest2 > span').click(function () {
    $(this).children().css('display', $(this).children().css('display') == 'inline-block' ? 'none' : 'inline-block');
});

modifying the first answer you are able to switch between n functions :

_x000D_
_x000D_
<!doctype html>_x000D_
<html lang="en">_x000D_
 <head>_x000D_
  <meta charset="UTF-8">_x000D_
  <meta name="Generator" content="EditPlus.comĀ®">_x000D_
<!-- <script src="../js/jquery.js"></script> -->_x000D_
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>_x000D_
  <title>my stupid example</title>_x000D_
_x000D_
 </head>_x000D_
 <body>_x000D_
 <nav>_x000D_
 <div>b<sub>1</sub></div>_x000D_
<div>b<sub>2</sub></div>_x000D_
<div>b<sub>3</sub></div>_x000D_
<!-- .......... -->_x000D_
<div>b<sub>n</sub></div>_x000D_
</nav>_x000D_
<script type="text/javascript">_x000D_
<!--_x000D_
$(document).ready(function() {_x000D_
 (function($) {_x000D_
        $.fn.clickToggle = function() {_x000D_
          var ta=arguments;_x000D_
         this.data('toggleclicked', 0);_x000D_
          this.click(function() {_x000D_
        id= $(this).index();console.log( id );_x000D_
            var data = $(this).data();_x000D_
            var tc = data.toggleclicked;_x000D_
            $.proxy(ta[id], this)();_x000D_
            data.toggleclicked = id_x000D_
          });_x000D_
          return this;_x000D_
        };_x000D_
   }(jQuery));_x000D_
_x000D_
    _x000D_
 $('nav div').clickToggle(_x000D_
     function() {alert('First handler');}, _x000D_
        function() {alert('Second handler');},_x000D_
        function() {alert('Third handler');}_x000D_
  //...........how manny parameters you want....._x000D_
  ,function() {alert('the `n handler');}_x000D_
 );_x000D_
_x000D_
});_x000D_
//-->_x000D_
</script>_x000D_
 </body>_x000D_
</html>
_x000D_
_x000D_
_x000D_


I don't think you should implement the toggle method since there is a reason why it was removed from jQuery 1.9.

Consider using toggleClass instead that is fully supported by jQuery:

function a(){...}
function b(){...}   

Let's say for example that your event trigger is onclick, so:

First option:

$('#test').on('click', function (event) {

    $(this).toggleClass('toggled');

    if ($(this).hasClass('toggled')) {
        a();
    } else{
        b();
    }
}

You can also send the handler functions as parameters:

Second option:

$('#test').on('click',{handler1: a, handler2: b}, function (event) {

    $(this).toggleClass('toggled');

    if ($(this).hasClass('toggled')) {
        event.data.handler1();
    } else{
        event.data.handler2();
    }
}

Use a couple of functions and a boolean. Here's a pattern, not full code:

 var state = false,
     oddONes = function () {...},
     evenOnes = function() {...};

 $("#time").click(function(){
     if(!state){
        evenOnes();
     } else {
        oddOnes();
     }
     state = !state;
  });

Or

  var cases[] = {
      function evenOnes(){...},  // these could even be anonymous functions
      function oddOnes(){...}    // function(){...}
  };

  var idx = 0; // should always be 0 or 1

  $("#time").click(function(idx){cases[idx = ((idx+1)%2)]()}); // corrected

(Note the second is off the top of my head and I mix languages a lot, so the exact syntax isn't guaranteed. Should be close to real Javascript through.)


I used this to create a toggle effect between two functions.

var x = false;
$(element).on('click', function(){
 if (!x){
  //function
  x = true;
 }
 else {
  //function
  x = false;
 }
});

Micro jQuery Plugin

If you want your own chainable clickToggle jQuery Method you can do it like:

_x000D_
_x000D_
jQuery.fn.clickToggle = function(a, b) {_x000D_
  return this.on("click", function(ev) { [b, a][this.$_io ^= 1].call(this, ev) })_x000D_
};_x000D_
_x000D_
// TEST:_x000D_
$('button').clickToggle(function(ev) {_x000D_
  $(this).text("B"); _x000D_
}, function(ev) {_x000D_
  $(this).text("A");_x000D_
});
_x000D_
<button>A</button>_x000D_
<button>A</button>_x000D_
<button>A</button>_x000D_
_x000D_
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
_x000D_
_x000D_
_x000D_


Simple Functions Toggler

LIVE DEMO

function a(){ console.log('a'); }
function b(){ console.log('b'); }

$("selector").click(function() { 
  return (this.tog = !this.tog) ? a() : b();
});

If you want it even shorter (why would one, right?!) you can use the Bitwise XOR *Docs operator like:
DEMO

  return (this.tog^=1) ? a() : b();

That's all.
The trick is to set to the this Object a boolean property tog, and toggle it using negation (tog = !tog)
and put the needed function calls in a Conditional Operator ?:




In OP's example (even with multiple elements) could look like:

function a(el){ $(el).animate({width: 260}, 1500); }
function b(el){ $(el).animate({width: 30}, 1500);  }

$("selector").click(function() {
  var el = this;
  return (el.t = !el.t) ? a(el) : b(el);
}); 

ALSO: You can also store-toggle like:
DEMO:

$("selector").click(function() {
  $(this).animate({width: (this.tog ^= 1) ? 260 : 30 });
}); 

but it was not the OP's exact request for he's looking for a way to have two separate operations / functions


Using Array.prototype.reverse:

Note: this will not store the current Toggle state but just inverse our functions positions in Array (It has it's uses...)

You simply store your a,b functions inside an array, onclick you simply reverse the array order and execute the array[1] function:

LIVE DEMO

function a(){ console.log("a"); }
function b(){ console.log("b"); }
var ab = [a,b];

$("selector").click(function(){
  ab.reverse()[1](); // Reverse and Execute! // >> "a","b","a","b"...
});

SOME MASHUP!

jQuery DEMO
JavaScript DEMO

Create a nice function toggleAB() that will contain your two functions, put them in Array, and at the end of the array you simply execute the function [0 // 1] respectively depending on the tog property that's passed to the function from the this reference:

function toggleAB(){
  var el = this; // `this` is the "button" Element Obj reference`
  return [
    function() { console.log("b"); },
    function() { console.log("a"); }
  ][el.tog^=1]();
}

$("selector").click( toggleAB );

DEMO

.one() documentation.

I am very late to answer but i think it's shortest code and might help.

function handler1() {
    alert('First handler: ' + $(this).text());
    $(this).one("click", handler2);
}
function handler2() {
    alert('Second handler: ' + $(this).text());
    $(this).one("click", handler1);
}
$("div").one("click", handler1);

DEMO With Op's Code

function handler1() {
    $(this).animate({
        width: "260px"
    }, 1500);
    $(this).one("click", handler2);
}

function handler2() {
    $(this).animate({
        width: "30px"
    }, 1500);
    $(this).one("click", handler1);
}
$("#time").one("click", handler1);

If all you're doing is keeping a boolean isEven then you can consider checking if a class isEven is on the element then toggling that class.

Using a shared variable like count is kind of bad practice. Ask yourself what is the scope of that variable, think of if you had 10 items that you'd want to toggle on your page, would you create 10 variables, or an array or variables to store their state? Probably not.

Edit:
jQuery has a switchClass method that, when combined with hasClass can be used to animate between the two width you have defined. This is favourable because you can change these sizes later in your stylesheet or add other parameters, like background-color or margin, to transition.


I would do something like this for the code you showed, if all you need to do is toggle a value :

var oddClick = true;
$("#time").click(function() {
    $(this).animate({
        width: oddClick ? 260 : 30
    },1500);
    oddClick = !oddClick;
});