addClass and removeClass in jQuery - not removing class

138,592

Solution 1

What happens is that your close button is placed inside your .clickable div, so the click event will be triggered in both elements.

The event bubbling will make the click event propagate from the child nodes to their parents. So your .close_button callback will be executed first, and when .clickable is reached, it will toggle the classes again. As this run very fast you can't notice the two events happened.

                    / \
--------------------| |-----------------
| .clickable        | |                |
|   ----------------| |-----------     |
|   | .close_button | |          |     |
|   ------------------------------     |
|             event bubbling           |
----------------------------------------

To prevent your event from reaching .clickable, you need to add the event parameter to your callback function and then call the stopPropagation method on it.

$(".close_button").click(function (e) { 
    $("#spot1").addClass("spot");
    $("#spot1").removeClass("grown");
    e.stopPropagation();
});

Fiddle: http://jsfiddle.net/u4GCk/1/

More info about event order in general: http://www.quirksmode.org/js/events_order.html (that's where I picked that pretty ASCII art =])

Solution 2

The issue is caused because of event bubbling. The first part of your code to add .grown works fine.

The second part "removing grown class" on clicking the link doesn't work as expected as both the handler for .close_button and .clickable are executed. So it removes and readd the grown class to the div.

You can avoid this by using e.stopPropagation() inside .close_button click handler to avoid the event from bubbling.

DEMO: http://jsfiddle.net/vL8DP/

Full Code

$(document).on('click', '.clickable', function () {
   $(this).addClass('grown').removeClass('spot');
}).on('click', '.close_button', function (e) {
   e.stopPropagation();
   $(this).closest('.clickable').removeClass('grown').addClass('spot');
});

Solution 3

Whenever I see addClass and removeClass I think why not just use toggleClass. In this case we can remove the .clickable class to avoid event bubbling, and to avoid the event from being fired on everything we click inside of the .clickable div.

$(document).on("click", ".close_button", function () { 
    $(this).closest(".grown").toggleClass("spot grown clickable");
});  

$(document).on("click", ".clickable", function () {
    $(this).toggleClass("spot grown clickable");
}); 

I also recommend a parent wrapper for your .clickable divs instead of using the document. I am not sure how you are adding them dynamically so didn't want to assume your layout for you.

http://jsfiddle.net/bplumb/ECQg5/2/

Happy Coding :)

Solution 4

I would recomend to cache the jQuery objects you use more than once. For Instance:

    $(document).on("click", ".clickable", function () {
        $(this).addClass("grown");
        $(this).removeClass("spot");
    });

would be:

    var doc = $(document);
    doc.on('click', '.clickable', function(){
       var currentClickedObject = $(this);
       currentClickedObject.addClass('grown');
       currentClickedObject.removeClass('spot');
    });

its actually more code, BUT it is muuuuuuch faster because you dont have to "walk" through the whole jQuery library in order to get the $(this) object.

Solution 5

I think you're almost there. The thing is, your $(this) in the "close button" listener is not the clickable div. So you want to search it first. try to replace $(this) with $(this).closest(".clickable") . And don't forget the e.stopPropagation() as Guilherme is suggesting. that should be something like:

$( document ).ready(function() {   
    $(document).on("click", ".close_button", function () { 
        alert ("oi");
        e.stopPropagation()
        $(this).closest(".clickable").addClass("spot");
        $(this).closest(".clickable").removeClass("grown");
    });  


    $(document).on("click", ".clickable", function () {
        if ($(this).hasClass("spot")){
            $(this).addClass("grown");
            $(this).removeClass("spot");
        }
    });
});
Share:
138,592
rhinoceros1
Author by

rhinoceros1

Updated on May 17, 2020

Comments

  • rhinoceros1
    rhinoceros1 about 4 years

    I'm trying to do something very simple. Basically I have a clickable div 'hot spot', when you click that it fills the screen and displays some content. I achieved this by simply changing the class of div, removing 'spot' and adding 'grown' and there's a little CSS animation to make it grow. This works fine.

    The problem is, within this div there is a close_button, which at the moment is just text. I want this to switch the classes back - i.e. remove grown and readd spot. It doesn't do this when clicked. I believe it's to do with the element not having those classes when the DOM loads, but I'm new to jQuery and don't know how to work around this.

    I think there's probably a much more sensible way of doing it, could someone point me in the right direction? I'd be very grateful. I've tried using toggleClass instead to no avail.

    $( document ).ready(function() {      
        $(".clickable").click(function() {  
            $(this).addClass("grown");  
            $(this).removeClass("spot");
        });   
    
        $(".close_button").click(function() {  
            alert (this);
            $("#spot1").removeClass("grown");  
            $("#spot1").addClass("spot");
        });   
    });
    

    UPDATE:

    I am using this code now,

    $( document ).ready(function() {   
        $(document).on("click", ".close_button", function () { 
            alert ("oi");
            $("#spot1").addClass("spot");
            $("#spot1").removeClass("grown");
        });  
    
    
        $(document).on("click", ".clickable", function () {
            if ($(this).hasClass("spot")){
                $(this).addClass("grown");
                $(this).removeClass("spot");
            }
        });
    });
    

    strangely the close_button function still won't add 'spot' or remove 'grown' though it will add any other classes and it will remove other classes... I added the if clause because I thought perhaps both function were being triggered at the same time, undoing each other, but it seems to make no difference

  • rhinoceros1
    rhinoceros1 almost 11 years
    I had tried this before and really thought it would solve the problem, but it does nothing... I put an alert in to confirm the click is working on the close button, the div id is definitely correct (spot1)...
  • rhinoceros1
    rhinoceros1 almost 11 years
    I've also noticed - I can add classes other than 'spot' fine, just not 'spot'! I then thought perhaps the first function was being called at the same time as the second and they were cancelling each other out, so I added: $(document).on("click", ".clickable", function () { if ($(this).hasClass("spot")){ $(this).addClass("grown"); $(this).removeClass("spot"); } }); $(document).on("click", ".close_button", function () { alert ("oi"); $("#spot1").addClass("spot"); $("#spot1").removeClass("grown"); }); ...to no avail
  • HQCasanova
    HQCasanova over 10 years
    Just for completion, there are actually several ways you can prevent events bubbling up, each with its own subtleties. For example, return false not only stops propagation but also stops the default action. See this article for further reference.