0

I am attempting to create an onclick function which has 1 of 2 outcomes on each click. In other words, when the user clicks the onclick element for the first time the outcome 1 occurs; then the user clicks the same element a second time, outcome 2 occurs. When the user clicks on the element a third time, outcome 1 happens again; forth click, outcome 2 occurs; and so on, like a loop. Below is some pseudo jQuery to give an illustration...

$(function(){
            var hits = 0;
    $('#myButton').click(function(){
                    if  (var hits = pos ) 
                    {
              $('#content1').fadeIn("slow");
              $('#content2').fadeOut("slow");
                    }
                    else
                    {
              $('#content1').fadeOut("slow");
              $('#content2').fadeIn("slow");
                    }
        return false;
    });
});

I have had a hard time searching for an answer because the terminology can vary tremendously. From 'variable' to 'trigger' to 'array' to 'listener' to 'handler' to 'conditional' to 'bind'.... where do I begin to search? I know there are multiple ways to create my function, but I know this is the best site to go to find the easiest, most efficient way. So on top of helping me out syntactically, I'd also like some terminology help with the cluster of words I listed. I know the general meaning of each term, but when applying them to the scenario/function I am trying to create, where do they each play a part? I think this will help straighten things out for a lot of people. Thanks in advance.

Thank you all for the quick and elaborate, professional feedback. I will use this new knowledge to its advantage. JQuery really does make things simple, almost too easy. However, the main reason I asked this question was to apply it to a keydown function, instead of a click function. I thought I could find an answer and swap some code around, maybe with normal javascript this would have worked. I have posted a new question that uses the knowledge and feedback you all have provided and come up with a different scenario involving the keydown function. I think I have the syntax pretty close thanks to your help, but however, only almost. If you would like to see, here is the url: bind event handler on keydown listen function JavaScript jQuery You guys truly rock.

5 Answers 5

5

jQuery has .toggle(handlers) for alternating clicks functionality:

$('#myButton').toggle(function() {
    $('#content1').fadeIn("slow");
    $('#content2').fadeOut("slow");
}, function() {
    $('#content1').fadeOut("slow");
    $('#content2').fadeIn("slow");
});

Fiddle

Now if all you want to do is switch 2 elements by fading them in/out respectively, you can use .fadeToggle():

$('#myButton').click(function() {
    $('#content1, #content2').fadeToggle("slow");
});

Fiddle

Sign up to request clarification or add additional context in comments.

1 Comment

.toggle(handlers) is deprecated in jQuery 1.8 and removed in jQuery 1.9.
2

As you want to know some terminology etc. I will provide you with some language-independent one.

Basically, I would call your requirement a 'state machine'.

In it's very basic, a state machine contains of "states", "events", and "transition functions". In your case you've got 2 states (let's call them "visible" and "hidden"), one event ("click") and 2 transition functions (we can call them "showContentOne" and "showContentTwo").

Two clarify terminology:

  • a state is your memory (it tells you what has happened in the past)
  • an event is any external activity which will cause the change of state
  • a transition function is a function, called when state-changing event occurs with a goal to change (or "transit") a state from one to another (possibly executing some side-effects).

Following, I will provide you with very basic (too basic in fact) explanation of concept, but keep in mind that I oversimplify things here - read appropriate articles in books or even wikipedia to get more precise information.

A state machine can be best described with a graph, in your case:

a state machine graph

So how to implement this thing properly?

you must have some variable to store state of your element. In your code snippet you have used global variable called "hits". As you use jQuery I would instead use it's "data" function which let's you bind arbitrary data to your DOM element (this way you will be able to install many independent state machines in single DOM tree). Then you need to bind event handlers (this is easy as you have only one event to consider - click) and define your state transition functions. Finally you need a function that will check state of an element, analyse incoming event and call appropriate state transition function.

Some basic implementation could look like this (I will not test this code, treat it as pseudo-code to help you get executable solution)

function showContentOne($element) {
     $('#content1').fadeIn("slow");
     $('#content2').fadeOut("slow");
     $element.data('state', 'hidden');
}

function showContentTwo($element) {
     $('#content1').fadeOut("slow");
     $('#content2').fadeIn("slow");         
     $element.data('state', 'visible');
}

function transitState($element, event) {
     var state = $element.data('state');
     if (state == 'visible' && event == 'click') {
          showContentTwo($element);
     } else if (state == 'hidden' && event == 'click') {
          showContentOne($element);
     } else {
          //unsupported transition - do nothing or raise error
     }
}

$(function(){
    $('#myButton')
        .data('state', 'visible') //initialize a state
        .click(function() { //install event handler
          transitState($(this), 'click');
        }
        //here you can possibly install more event handlers
    });
});

Now if you reimplement "transitState" to use some array or objects like state machine definition you can get quite robust solution to add/remove events and other state transition functions. You can install a state machine on any element, perhaps wrap this with jquery plugin.

Note: in your case my solution is serious over-engineering of this task. I provided it to give you some terminology. Your very basic problem (2 states, 2 transitions, one event) can be easily implemented in very similiar way you showed in original question, only change I would make is not using global variable, but data function to maintain state. Because you have got exactly two states, boolean variable to hold it will be perfectly fine.

$(function(){

$('#myButton')
    .data('isVisible', true)
    .click(function(){
         if  ($(this).data('isVisible')) { 
              $('#content1').fadeIn("slow");
              $('#content2').fadeOut("slow");
         } else {
              $('#content1').fadeOut("slow");
              $('#content2').fadeIn("slow");
         }
         $(this).data('isVisible', !$(this).data('isVisibile'));
         return false;
    });
});

And as others stated for changing visibility of elements on consecutive clicks you can use toggle functions.

sorry for my poor english :/

1 Comment

Its good you provided lengthy syntax in this case because it helps me understand. Thanks for the code!
1

Try:

$(function(){
            var hits = 0; //->variable
    // Everything below this line is called "binding" a handler to an event (click event)
    $('#myButton').click(function(){ // --> This whole function is the handler or listener
                    if  (hits % 2 !== 0) //for hits 2,4,6,8 etc. Also the condition
                    {
              $('#content1').fadeIn("slow");
              $('#content2').fadeOut("slow");
                    }
                    else
                    { // for hits 1,3,5,7
              $('#content1').fadeOut("slow");
              $('#content2').fadeIn("slow");
                    }
                   hits++;
        return false;
    });
});

1 Comment

I still have to continue study all of the answers, but this one is very helpful. Great sidenotes.
0

One way to easily achieve this, and perhaps make it somewhat more elegant, would be to write this simple plugin:

(function ($) {
    $.fn.rollerClick = function (handlers) {
        this.each(function () {
            this.hits = 0;
            $(this).click(function () {
                handlers[this.hits ++](this);
                if (this.hits == handlers.length) {
                    this.hits = 0;
                }
            }); 
        });
    };
)(jQuery);

Now you can use it like this:

$("selector").rollerClick([
    function (element) {
        alert('1');
    },
    function (element) {
        alert('2');
    },
    function (element) {
        alert('3');
    }
]);           

Then by clicking repeatedly over the selected item you will roll through the handlers, seeing alerts popping up showing "1", "2", "3", "1", "2", ... .

5 Comments

You've heard of jQuery's toggle() method, have you?
Of course :) But I think it's good to have something more versatile that will do more than just toggling the display property.
Actually, there are two methods called toggle(). The one I'm refering to does not toggle the display property but executes handlers on alternate clicks.
There are indeed 2 methods with the same name - .toggle() which is more of a .show/.hide toggle, and the .toggle() linked in my answer which acts as a click handler that executes the function objects passed as handlers in alternate clicks. Which of the toggle methods is called depends on the set of arguments passed. Having 2 methods with the same is a bit confusing, but it is like that even in version 1.8+ due to jQuery's back-compat support, changing a method's name isn't something to be done lightly.
I do hope that in future they may add an alias to one of those methods to ease the confusion.
0

May I suggest you have a look into jQuery's toggle() method which would be used like:

$(function() {
    $('#myButton').toggle(function(){...}, function(){...});
});

I also give you a modified version of the DIY way:

$(function(){
    var hits = 0;

    $('#myButton').click(function(){
        hits ^= 1;

        if  ( hits ) {
            $('#content1').fadeIn("slow");
            $('#content2').fadeOut("slow");
        } else {
            $('#content1').fadeOut("slow");
            $('#content2').fadeIn("slow");
        }

        return false;
    });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.