可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
toggleClass
is great if I just have two presentations styles I want to use, but a lot of times I've got more than two states that I want to use.
For example, if I want to rotate through three classes:
if ($(this).hasClass("A"))
$(this).removeClass("A").addClass("B");
else if ($(this).hasClass("B"))
$(this).removeClass("B").addClass("C");
else if ($(this).hasClass("C"))
$(this).removeClass("C").addClass("A");
I'd like to have a simple method that did this for me, something like:
$(this).rotateClass('A', 'B', 'C');
Is there an existing jQuery method or plugin that does this?
回答1:
Here's my attempt at it.
$.fn.rotate= function( classes ) {
$element = this; // element the rotate is being applied to
for( var i = 0; i < classes.length; i++ ){ // look through the classes array
if( $element.hasClass( classes[i] ) ){ // check if current element has the class specified in the array
$element.removeClass( classes[i] ); // if it does then remove the class
$element.addClass( classes[ ++i % classes.length ] );
return $element; // return the current element so other functions can be applied to it.
}
}
}
You would use it by calling .rotate
on the element and passing in an array of classes to rotate.
Here's an interactive demo. I just added the animate to show that you can apply other jQuery functions to the element after the rotate is finished.
$('#test').rotate(['A','B','C']);
The reason we use the modulus operator is, if i > the length of the array then we want the correct index. E.g array of classes is ['A','B','C'], if the current class on our element is 'C' then the index of the current class is 2 (arrays are 0 index based), then if we increment 2 + 1 = 3 to get the class we want to rotate it to but we don't have any class at index 3 hence 3 % 3 = 0 which is the index of 'A', that's what we want, hence the modulus will get us the correct index every time.
回答2:
Here's what I use:
$.fn.rotateClass = function(classes) {
var $set = this.each(function () {
var $this = $(this);
var index = (($this.data("currentClassIndex")) ? $this.data("currentClassIndex") : 0) % classes.length;
$this.removeClass(classes.join(" "))
.addClass(classes[index])
.data("currentClassIndex", ++index);
});
return $set;
}
No for loops, no hasClass calls, compatible with sets of elements, very compact and very performant.
Usage:
$("#randomDiv").rotateClass(["classA", "classB", "classC", "classD"]);
回答3:
This plugin code will rotate between the passed classes (however many there are (2-n). If none of the passed classes are found on the object, then the first class is set.
$.fn.rotateClass(/* pass multiple class names here */) {
for (var i = 0; i < arguments.length; i++) {
if (this.hasClass(arguments[i])) {
this.removeClass(arguments[i]));
i++;
if (i >= arguments.length) {
i = 0;
}
this.addClass(arguments[i]);
return(this);
}
}
// none found so set the first class
if (arguments.length > 0) {
this.addClass(arguments[0]);
}
return(this);
}
This version of the code assumes that you are applying the same classes to all the DOM objects in the jQuery object and they all have the same state. It could be extended to treat each DOM object in the jQuery object separately and rotate each ones separately if that was required.
回答4:
Put them in an Array:
var arr = ['A', 'B', 'C'];
var i = 0;
Then in the handler:
i = ++i % arr.length;
$(this).toggleClass( this.className + ' ' + arr[ i ] );
The use of this.className
assumes that there's not other classes applied to this element.
JSFIDDLE DEMO
If there may be other classes, use this:
$(this).removeClass( arr[ i ] );
i = ++i % arr.length;
$(this).addClass( arr[ i ] );
JSFIDDLE DEMO
If there may be other elements, and each need their own state, you could use a closure to associate a unique i
with each element:
$('div').each(function() {
var i = 0;
$(this).click(function() {
$(this).removeClass( arr[ i ] );
i = ++i % arr.length;
$(this).addClass( arr[ i ] );
});
});
JSFIDDLE DEMO
回答5:
You can still use toggleClass
and pass it a function instead:
$(this).toggleClass(function() {
if ($(this).hasClass('red')) {
return 'green red';
}
if ($(this).hasClass('green')) {
return 'blue green';
}
if ($(this).hasClass('blue')) {
return 'red blue';
}
});
Not the easiest code to maintain though, so it would probably be better to wrap it in a plugin.
http://jsbin.com/inaduy/edit#javascript,html,live
And now made in to a very basic plugin (that needs a bit of validation). You just pass an array of objects containing pairs of classes to swap.
var classArray = [{current: 'red', next:'green'}, {current:'green', next:'blue'}, {current:'blue', next:'red'}];
$('#hello').click(function() {
$(this).toggleRotate(classArray);
});
(function($) {
$.fn.toggleRotate = function(classes) {
return $(this).toggleClass(function() {
var l = classes.length;
for (var i = 0; i < l; i++) {
if ($(this).hasClass(classes[i].current)) {
return classes[i].current + ' ' + classes[i].next;
}
}
// If it makes it here return the first current value
return classes[0].current;
});
};
})(jQuery);
http://jsbin.com/inaduy/2/edit
回答6:
My current solution:
$.fn.mapClasses = function(mapping){
return this.each(function(){
this.className = this.className.split(/\s+/).map(function(klass){
return mapping[klass] || klass
}).join(' ');
});
};
$.fn.rotateClasses(){
var mapping = {};
var prev = arguments[0];
for (var i = arguments.length - 1; i >= 0; i--){
mapping[ arguments[i] ] = prev;
prev = arguments[i];
}
return this.mapClasses(mapping);
};