JQuery click-through class cycle
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, onc开发者_如何学Ce 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.
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.
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')
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.
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"></script>
<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>
Have you tried Toggle? It should keep the state for you internally.
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');
});
There is a jQuery plugin that does exactly this called cycleClass...
github: https://github.com/keithmgould/cycleClass
jquery plugin repo: plugins.jquery.com/project/cycleClass
Best,
Keith
PS: here is the documentation:
Example: $("#linky").cycleClass(["foo","bar","jonez"]);
Functionality: if linky has any of the classes listed in the array then all of the classes will be removed, and the class after the farthest class found (modulus) will be added.
Scenarios:
- if "foo" found, replace with "bar"
- if "jonez" found, replace with "foo"
- if "bar" and "jonez" found, replace both with "foo"
- if none found, "foo" (first element) added
This jQuery function, not a plugin, will cycle through any number of classes that have been set up as a csv of classes in the cycler element's data-classes attribute. Two classes acts just like a toggle. Start with a class not in the list and the initial state is untouched.
Cycle it with $(selector).cycleClass().
I run mine through a server side template engine, hence the {{#objModel}} and {{/objModel}} remove if you don't.
Works on any element that has class and data-* attributes. The code below has a cycle button to change the class of the code block. But it could have just been on the button itself and changed its own class.
I originally posted this to to answer the toggle class topic/question then found this cycle Class topic.
See it in use at www.PluginExamples.com.
{{#objModel}}
<style>
#cycler.A code {outline:3px solid red;}
#cycler.B code {outline:3px solid blue;}
#cycler.C code {outline:3px dotted green;}
#cycler.D code {outline:3px dotted red;}
#cycler.E code {outline:3px dashed blue;}
#cycler.F code {outline:3px dashed green;}
</style>
<button onclick="$('#cycler').cycleClass();">Cycle</button>
<div id="cycler" data-classes=",A,B,C,D,E,F">
<code
id="cycleClass"
><div id="cycler" data-classes=",A,B,C,D,E,F,">...</div>
<button onclick="$('#cycler').cycleClass();">Cycle</button>
$( ".cycler" ).cycleClass();
$.fn.cycleClass = function(){
if( !$(this).data("aClasses") ){
var classes = $(this).attr("data-classes").split(",");
$(this).data("aClasses", classes);
$(this).data("classes", classes.join(" "));
$(this).data("class", classes.length);
}
$(this).data("class",($(this).data("class")+1) % $(this).data("aClasses").length);
$(this).removeClass($(this).data("classes"))
.addClass( $(this).data("aClasses")[$(this).data("class")] );
return $(this);
}
</code>
</div>
<script>
(function($){
$.fn.cycleClass = function(){
if( !$(this).data("aClasses") ){
var classes = $(this).attr("data-classes").split(",");
$(this).data("aClasses", classes);
$(this).data("classes", classes.join(" "));
$(this).data("class", classes.length);
}
$(this).data("class",($(this).data("class")+1) % $(this).data("aClasses").length);
$(this).removeClass($(this).data("classes"))
.addClass( $(this).data("aClasses")[$(this).data("class")] );
return $(this);
}
});
</script>
{{/objModel}}
[1]: http://www.pluginexamples.com
Basic traditional style logic -
$(".toCycle").click(function(){
var rotator = this;
var cycle = ["bg-default", "bg-warning", "bg-success", "bg-danger"];
var classList = $(this).attr("class").split(/\s+/);
$.each(classList, function(index, item) {
var i = cycle.indexOf(item);
var n;
if (i > -1) {
$(rotator).removeClass(item);
n = i+1;
if (n == cycle.length) //control the overflow
n = 0;
$(rotator).addClass(cycle[n]);
}
});
});
精彩评论