tags:

views:

50

answers:

5

I'm trying to make a cycle so that if you click on a list item it goes green, if clicked again it goes red, once more and it goes into its original state, there are some items in the list that will already have either green or red classes. So far I've written this but it isn't working:

$(document).ready(function () {
$("li").click(function () {
    if (this.hasClass ("red")) {
        $(this).removeClass("red")
        }
    if (this.hasClass ("green")) {
        $(this).addClass("red").removeClass("green")
        }
    else ($(this).addClass("green"))
}); });

Thank you for your help.

+1  A: 

Have you tried Toggle? It should keep the state for you internally.

Alexandre Rafalovitch
toggle would not help for 3 states, would it?
zerkms
Recent version allows more than 2 states. The example shows 3:"Bind two or more handlers to the matched elements, to be executed on alternate clicks."
Alexandre Rafalovitch
oh, yes... nice catch. "If more than two handlers are provided, .toggle() will cycle among all of them. For example, if there are three handlers, then the first handler will be called on the first click, the fourth click, the seventh click, and so on."
zerkms
+1  A: 

Create a "cursor" variable (classNum) which you use to keep track of the position, then let this cursor move through each position in an array containing all of the states you want. Haven't tested this code, but it's the basic idea.

var classes = ["default", "red", "green"];
$("li").click(function () {
  var classNum = $(this).data("classNum") || 0;
  $(this).removeClass(classes[classNum]);
  classNum = (classNum + 1)  % classes.length;
  $(this).addClass(classes[classNum]);
  $(this).data("classNum", classNum);
});

The nice thing about programming is you can use it to describe the way you actually think. You used the word "loop", in your original description, so try to create code which describes a repeating sequence, rather than using conditional tests. You'll probably find yourself using "if" less and less the more you progress as a programmer.

morgancodes
This works...but only if there's only one `<li>`, which is *very* unlikely :)
Nick Craver
Good point Nick. Edited. I'd prefer using a closure, feel like there's something dirty about .data. Generally I store local variables in closures instead. But this may be easier to understand.
morgancodes
@morgancodes - You can save a lot of extra jQuery objects there with [`$.data()`](http://api.jquery.com/jQuery.data/) :) From an overall standpoint though, it doesn't address the ones that already had a class of red then the page loads, the same issue as the `.toggle()` answers :)
Nick Craver
Ok, I give up. Seems like our gal has an acceptable solution anyway. What's this $.data() tip though? I sling jquery objects around with reckless abandon. You ever run in to a situation where you run in to problems b/c of too many?
morgancodes
+2  A: 

The problem is you can't use .hasClass() on this, it needs to be a jquery object, e.g. $(this). You really can't simplify it much more than you have for just 3 states, the fixed version would look like this:

$("li").click(function () {
  var $this = $(this);
  if ($this.hasClass ("red")) 
    $this.removeClass("red")
  if ($this.hasClass ("green")) {
    $this.toggleClass("red green");
  } else {
    $this.addClass("green")
  }
});

.toggleClass() is just a shortcut for toggling both, effectively swapping them.

Nick Craver
A: 

It is exact the sample you need: http://api.jquery.com/toggle/ "Example: Click to toggle highlight on the list item"

$('li').toggle(function() {
    $(this).addClass('green');
}, function() {
    $(this).toggleClass('green red');
}, function() {
    $(this).removeClass('red');
});
zerkms
The problem with `.toggle()` is that some of his elements have an initial state, so you'd need to start some elements at a non-zero index of the toggle function array, it's best to check inside the method in these cases :)
Nick Craver
@Nick Craver: ah, i missed that, +1 to your one then ;-) sorry, Charlotte.
zerkms
A: 

Ok, this random start position was really annoying me and there were always new jQuery methods I haven't played with much before. So, here is a module cycle solution for N>1 states, including default state with no initial class:

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"&gt;&lt;/script&gt;
<script type="text/javascript">
    $(document).ready(function(){
        var list = $("#list");

        //make default state _not_ a special case by adding a class to it
        $("li:not(.green,.red)", list).addClass("default"); 

        //declare cycle transition function
        var cycleClass = function(classFrom, classTo){
            list.delegate("li."+classFrom, "click", function(){
                $(this).toggleClass(classFrom + " " + classTo);
            });
        };

        //declare cycle sequence
        cycleClass("default", "green");
        cycleClass("green", "red");
        cycleClass("red", "default");
    });
</script>
<style type="text/css">
    .default {background-color: lightgray;}
    .green {background-color: green;}
    .red {background-color: red;}
</style>
</head>
<body>
<ul id='list'>
    <li>Start in default</li>
    <li>Another default</li>
    <li class='green'>Start in Green</li>
    <li class='red'>Start in Red</li>
    <li class='green'>Another Green</li>
    <li>Yes another default</li>
</ul>
</body>
</html>
Alexandre Rafalovitch