1

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.

Charlotte
  • 13
  • 1
  • 3
  • 1
    **isn't working** is not a description of a problem. ps: why do you use `this` as well as `$(this)`? – zerkms Aug 12 '10 at 00:59

9 Answers9

4

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
  • 623,446
  • 136
  • 1,297
  • 1,155
3

Here's one I use. In JavaScript:

$.fn.cycleClasses = function() {
  var classes, currentClass, nextClass, _this = this;
  classes = Array.prototype.slice.call(arguments);

  currentClass = $.grep(classes, function(klass) {
    return _this.hasClass(klass);
  }).pop();

  nextClass = classes[classes.indexOf(currentClass) + 1] || classes[0];

  this.removeClass(currentClass);
  return this.addClass(nextClass);
};

In CoffeeScript:

$.fn.cycleClasses = (classes...) ->
  currentClass = $.grep classes, (klass) =>
    this.hasClass(klass)
  .pop()

  nextClass = classes[classes.indexOf(currentClass) + 1] || classes[0]

  this.removeClass(currentClass)
  this.addClass(nextClass)

Usage:

$('.someElement').cycleClasses('a', 'b', 'c')
VisualBean
  • 4,908
  • 2
  • 28
  • 57
Aupajo
  • 5,885
  • 6
  • 30
  • 28
2

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
  • 25,055
  • 38
  • 135
  • 187
  • 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 Aug 12 '10 at 01:12
  • 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 Aug 12 '10 at 01:16
  • 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 Aug 12 '10 at 01:29