jakestack
jakestack

Reputation: 231

How to use addClass and removeClass repeatedly on a single element?

So what I want to achieve is just change the classes of a HTML link on every click like this:

No luck so far. What could I be doing wrong?

Here's the single line of HTML code where I'm trying my jQuery code on:

<a class="first" href="#">Test 1</a>

Here's my jQuery:

$( "#menu li a.first" ).click(function() {

   $( "#menu li a.first" ).removeClass("first").addClass("second");

}

$( "#menu li a.second" ).click(function() {

   $( "#menu li a.second" ).removeClass("second").addClass("third");

}

$( "#menu li a.third" ).click(function() {

   $( "#menu li a.second" ).removeClass("third").addClass("fourth");

}

Thanks in advance!

Upvotes: 21

Views: 7935

Answers (10)

MonkeyZeus
MonkeyZeus

Reputation: 20737

Not sure if this would solve your issue but I would shoot for a conditional statement and only one delegated event listener:

$("#menu li").on("click", "a", function () {
  if ($(this).hasClass("first")) {
    $(this).removeClass("first").addClass("second");
  } else if ($(this).hasClass("second")) {
    $(this).removeClass("second").addClass("third");
  }
  // etc...
});

Upvotes: 3

viral
viral

Reputation: 3743

Try binding event to parent,

My try,

var $classes = ['first', 'second', 'third'];

$(function(){
    $('#subject').click(function(){
        current = $(this).find('a:first');
        index = $.inArray(current.attr('class'), $classes);
        if($classes.length > index+1)
            current.removeClass($classes[index]).addClass($classes[index+1])
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id='subject'>
	<a class="first" href="#">Test 1</a>
</div>

Upvotes: 0

Nergal
Nergal

Reputation: 1015

Okay so there is a few workaround for this, which wasn't mentioned yet.

You can use Javascript object for this not just array. Object could make it easier if you want a chain instead of list.

var classNames = {first:'second', second:'third', third:'fourth'};

$('#menu li a').on('click', function() {
    if(typeof classNames[this.className] !== 'undefined'){
        this.className = classNames[this.className];
    }
});

Second method is to use .on('click', [selector], handler) instead click which can handle dynamicly loaded, added or changed elements.

$('#menu li').on('click', 'a.first', function() {
    $(this).removeClass("first").addClass("second");
});

$('#menu li').on('click', 'a.second', function() {
   $(this).removeClass("second").addClass("third");
});

$('#menu li').on('click', 'a.third', function() {
   $(this).removeClass("third").addClass("fourth");
});

Not even close to perfect but still a working solution.

You can use if .. else or switch .. case inside a function to create a decision tree.

So basically there is a lot of solution. Pick the best.

Upvotes: 0

Tepken Vannkorn
Tepken Vannkorn

Reputation: 9723

No, you can't. As JavaScript only runs after the page loads ( if you put them inside the $( document ).ready() function ), further functions down below will never be executed. It can only detect the <a class="first" href="#">Test 1</a> but not the <a class="second" href="#">Test 1</a> because the <a class="second" href="#">Test 1</a> are generated after the page loads and, therefore, will never be executed, unless you are using Ajax.

Update: This can be done. Please see @i3b13's comment below.

Upvotes: -1

Rajaprabhu Aravindasamy
Rajaprabhu Aravindasamy

Reputation: 67207

You can do it with the usage of .data()

HTML:

<a class="first" href="#" id="test">Test 1</a>

JS:

$(".first").data("classes",["one","two","three","four"]).click(function() {
  var elem = $(this);
  var cnt = (elem.data("cnt") || 0)
  var classes = elem.data("classes");
  elem.removeClass().addClass(classes[cnt % classes.length] + " first").data("cnt",++cnt);
});

Demo

$(".first").data("classes",["one","two","three","four"]).click(function() {
  var elem = $(this);
  var cnt = (elem.data("cnt") || 0)
  var classes = elem.data("classes");
  elem.removeClass().addClass(classes[cnt % classes.length] + " first").data("cnt",++cnt);
});
.one{
  color:red;
}
.two{
  color:yellow;
}
.three{
  color:green;
}
.four{
  color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<a class="first" href="#" id="test">Test 1</a>

Upvotes: 4

GMchris
GMchris

Reputation: 5648

The problem is you're trying to attach the event handler before it even has the class second or third.

Besides this approach is pretty verbose. I suggest simply providing an array of classes. Like so:

var classNames = ['first', 'second', 'third'];

Then add a different identifier to the button, for instance add a class class-changer. And attach the following event handler.

$('.class-changer').on('click', function() {
    var $el = $(this)
    for (var i= 0; i < classNames.length; i++) {
        if ($el.hasClass(classNames[i]) && classNames[i+1]) {
           $el.removeClass(classNames[i]).addClass(classNames[i+1]);
           break;
        }
    }
});

Upvotes: 21

iabw
iabw

Reputation: 1108

Assuming you actually only have 1 link whose state you're trying to change, instead of a bunch of links in your menu that you want to ALL be moved from ".first" to ".second" when one is clicked, I would suggest this as the most idiomatic way (pun not intended).

// Only select the menu once
var $menu = $('#menu');

// Delegate to elements with the correct class.
// Specifying the "li a" is probably unnecessary,
// unless you have other elements with the same classes in "#menu".
$menu.on('click', '.first', function(e) {

    // Inside a jQuery event handler,
    // `this` refers to the element that triggered the event.
    // If the event is delegated, it's the delegation target
    // (".first" in this instance), not the bound element ("#menu").
    $(this).removeClass('first').addClass('second');

});

$menu.on('click', '.second', function(e) {
   $(this).removeClass('second').addClass('third');
});

$menu.on('click', '.third', function(e) {
   $(this).removeClass('third').addClass('fourth');
});

Resources:

Upvotes: 4

Daniel
Daniel

Reputation: 243

If you want to bind an event the selected element must exist previously.

To bind an event handler to elements that does not yet exist (ex. dynamically created or modified) you can do this:

$(document).on('click', '#menu li a.first', function() {

    $( "#menu li a.first" ).removeClass("first").addClass("second");

});

$(document).on('click', '#menu li a.second', function() {

   $( "#menu li a.second" ).removeClass("second").addClass("third");

});

$(document).on('click', '#menu li a.third', function() {

   $( "#menu li a.third" ).removeClass("third").addClass("fourth");

});

Upvotes: 1

Ibrahim Khan
Ibrahim Khan

Reputation: 20740

Put all classes in an array and on click of the link add class one by one like following.

var classes = ["first", "second", "third", "fourth"];
$("#menu li a").click(function () {
    var index = classes.indexOf(this.className);
    var newIndex = (index + 1) % classes.length; //return to first after reaching last

    $(this).removeClass(classes[index]).addClass(classes[newIndex]);
});
.first { color: red; }
.second { color: green; }
.third { color: blue; }
.fourth { color: purple; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="menu">
    <li>
        <a class="first" href="#">Test 1</a>
    </li>
</ul>

Upvotes: 10

Dmitry Yudin
Dmitry Yudin

Reputation: 978

<a class="changable first" href="#">Test 1</a>
$( ".changable" ).click(function(event) {
classes = ['first','second','third','fourth']
changed=false
for (c in classes){
    if (event.target.classList.contains(classes[c]) && changed==false){
       $(this).removeClass((classes[c]));
       index_to_add=classes.indexOf(classes[c])+1
       class_to_add=classes[index_to_add]
       $(this).addClass(class_to_add);
       changed=true;
    }
}

});

Upvotes: 0

Related Questions