Reputation: 8075
This is going to be a hard one to explain...
I have a button which is a custom like button.
<button class="like-post">Like Post</button>
After the user clicks like, I do an AJAX call and add a row to the DB and then add and remove the class.
btn.removeClass('btn-success')
.removeClass("like-post")
.addClass('btn-default')
.addClass('unlike-post')
.html("<span class='glyphicon glyphicon-remove'></span> Un-like");
This is all working okay, except after the class have changed to 'unlike-post' the button still reacts to a like-post class trigger.
So I have this:
$(".like-post").on('click', function(){ });
Which carries out that action even after the button doesn't have a class of like-post. I have checked and the class is definitely getting removed / added correctly.
I want to carry out another action with the unlike-post class but can't until this is resolved.
I have noticed a similiar issue elsewhere on my site regarding .on(). I cannot recreate the issue in jsFiddle since it works fine there.
Using bootstrap + latest jQuery version. Any ideas? I know its a long shot but thought someone might have had a similiar issue.
Thanks.
Upvotes: 1
Views: 77
Reputation: 93551
The problem is you connect a handler to the element, after which it does not matter if the class changes.
Use a delegated handler attached to a non-changing ancestor:
$(document).on("click", ".like-post", function(){ });
Delegated events work by listening for the event to bubble up to an ancestor element. They then apply the jQuery filter at event time (not event registration time) to the elements in the bubble-chain. They then apply the function only to matching elements that caused the event. This type of handler can actually be more efficient (as it swaps a minutely slower event handler for a much faster setup time). As click speeds are very slow, you will never notice a delegated event handler being slower than a directly connected handler.
documentis the default if no other element is handy, but never use
body` for delegated events as it has a bug (if styling results in a calculated body height of 0 it will not respond to mouse events).
Generally though you will try to bind to an ancestor near to the changing elements.
Upvotes: 6
Reputation: 6269
I guess the problem relies on the definition of on. (see http://api.jquery.com/on/)
There are actually two way that "on" works.
$( ".like-post" ).on( "click", function() {
});
vs.
$( "body" ).on( "click", ".like-post", function() {
});
the first basically means:
bind the function on all elements having "life-post" currently as class and execute when a click happens.
The second is: when a click event happens within "body" on an element having class "like-post" execute the function.
The main difference is that the first binds on the "current state" of elements when the code is first executed (meaning the bind code), while the second case reflects the state of the dom elements when the "click" action happens.
Instead of body you can use $(document) or some other selector, but basically you have to avoid using the element directly and rather use a parent of it.
Alternatively you could also to a check like:
$( ".like-post" ).on( "click", function() {
// the class has been removed
if(!$(this).hasClass("like-post))
return;
......
});
Upvotes: 1
Reputation: 207501
When you add an event directly to the element, it does not magically remove the event when you change the class. You either need to unbind it or add logic inside of the function. Other option is to use event delegation
unbind example:
$(".like-post").on('click', function(){
$(this).unbind("click");
//logic here
});
Basic idea with a check:
$(".like-post").on('click', function(){
var btn = $(this);
if (btn.hasClass("xxx")) {
//do this logic
} else {
//do that logic
}
});
event delegation:
$(document).on("click", ".like-post", function(){ console.log("like clicked" });
Upvotes: 1
Reputation: 22673
This is because .on()
evaluates elements in one particular moment, when it's defined. It doesn't matter whether it still matches the selector or not.
If you need more flexible solution, pass more parameters to .on()
, like this:
$("body").on("click", ".like-post", function () {});
That way, only elements with like-post
class at the moment will be matched.
It doesn't need to be body
, it can be the deepest element that you know will always contain this .like-post
button.
Upvotes: 0