This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
AddClass/RemoveClass Issues
(3 answers)
Closed 8 years ago.
I made a like system but when I click on the like button it will run but I have to refresh the page for me to then be able to click on the unlike button even though it is showing up ? please help me I need it to be able to be clickable and work right after each other....
<script type="text/javascript">
$(document).ready(function(){
$('.like').click(function(){
if($(this).attr('title')=='like'){
$.post('/like.php',{imid:$(this).attr('id'),action:'like'});
$(this).removeClass('like').addClass('unlike');
$(this).attr('title','unlike');
}
});
});
</script>
<script type="text/javascript">
$(document).ready(function(){
$('.unlike').click(function(){
if($(this).attr('title')=='unlike'){
$.post('/like.php',{imid:$(this).attr('id'),action:'unlike'});
$(this).removeClass('unlike').addClass('like');
$(this).attr('title','like');
}
});
});
</script>
I think #Steven Britton's answer should work - except he's missing the event for the likes.
The following works for me:
<script type="text/javascript">
$(function(){
$('body')
.on('click', '.like', function(){
// Do whatever to register action
$(this).removeClass('like').addClass('unlike').attr('title', 'unlike');
})
.on('click', '.unlike', function(){
// Do whatever to register action
$(this).removeClass('unlike').addClass('like').attr('title', 'like');
});
});
</script>
I don't see the need for the test (if title = unlike) since you have different classes for like/unlike anyway. If you are going to do a test I would recommend doing it by testing for classes - testing for texts in the title will break as soon as you change to texts (e.g. translate the page). The texts are part of the presentation, the classes part of the data structure.
You could give all the links a common class (e.g. "like-unlike") in addition to the like or unlike class and use something like the following:
<script type="text/javascript">
$(function(){
$('body').on('click', '.like-unlike', function(){
if( $(this).hasClass('like') ){
// Do whatever to register action
$(this).removeClass('like').addClass('unlike').attr('title', 'unlike');
} else if( $(this).hasClass('unlike') ){
// Do whatever to register action
$(this).removeClass('unlike').addClass('like').attr('title', 'like');
} else {
// SHould never happen - log error?
}
});
});
</script>
To answer the comment about how to use a delegated event: .on is (when used like this) using a delegated event. I am binding the event to the document body, not to the like buttons. When the user clicks the like buttons the event bubbles up to the body at which point my delegated event is run (assuming the element clicked matches the selector I give as second argument to .on (.e.g ".like-unlike" in my final example).
The difference is that the original code was saying "when the page loads find all elements with class 'like' and attach this click event to them and all elements with class 'unlike' and attach that click event to them". When the click event is run it changes the class from like to unlike. But because the element did not have class "unlike" when the document loaded it was not found then and did not get that event. In fact it still has the "like" event handler even after the class is changed to "unlike" since that event handler was attached to the element when the page loaded. The fact that the list of elements to attach the event to was found using a specific class doesn't matter - those on the list get that event handler and keep it.
By attaching to the body using .on in it's three-argument form we are then running events based on what class the element has when it is clicked, instead of what class it had when the document loaded.
My second example (using the like-unlike class) would actually work as a non-delegated event as well, but I still think delegated events are better here - you might end up loading content with ajax requests or something.
Give this a shot (by using the ON, you're setting an event handler on classes that haven't been applied yet):
<script type="text/javascript">
$(document).ready(function(){
$(document).on('click','.unlike', function(){
if($(this).attr('title')=='unlike'){
$.post('/like.php',{imid:$(this).attr('id'),action:'unlike'});
$(this).removeClass('unlike').addClass('like');
$(this).attr('title','like');
}
});
});
</script>
Related
I am using following code on my page which I am loading in ajax.
$(document).ready(function() {
$('#button_id').click(function() {
//Do Something
});
});
Now When I click on the button action happens multiple times. I know that its happening because I am loading the ajax page multiple times.
Please help me solve this.
You can use .off() to remove existing listeners:
$(function() {
$('#button_id').off('click').click(function() {
//Do Something
});
});
If I am wrong about your implementation I apologize. Your problem may exist because the binding is created on first page load and then on subsequent ajax loads with new scripts being inserted and creating duplicate bindings. You should prevent any bindings from being generated on ajax loads to prevent duplicate bindings unless you are good with cleanup.
If the button you are clicking on exists in the ajax loaded area then you should use delegation to ensure that the click handlers still work.
For example:
$( "body" ).on( "click", "#button_id", function() {
//do something
});
This will add a binding to the body element, but more specifically to the id #button_id. A click event on the button will propagate and bubble up to the body element (or whatever parent element you choose).
This makes it so that dynamic elements can be inserted in the DOM and only one event handler is needed to listen for it.
No need for .on() or .off() calls for individual ajax loads. This allows your bindings to be much cleaner.
Of course, if your button is not likely to exist on the page all the time then it would not be a good idea to keep extra bindings. Only create these types of binding if they are always needed to prevent optimization issues.
A cleaner solution would be to remove that code from the ajax loaded HTML and use one single event handler in the master page
I guess your problem is the event is firing many times.
To fire only once try this:
$(document).ready(function() {
$('#button_id').on("click",function(e) {
e.preventDefault(); // This prevents the default non-js action (very used for anchors without links or hashes)
e.stopPropagation(); // Prevent the bubling of the event and spread more times
//Do Something
});
});
If doesn't work with e.stopPropagation(); try with e.stopInmediatePropagation();
Adding documentation for the last method I suggested. It could solve your problem.
http://api.jquery.com/event.stopimmediatepropagation/
This question already has answers here:
adding jQuery click events to dynamically added content
(4 answers)
Closed 9 years ago.
I have a simple code that check a select change and alert a message. This is working ok but when I insert new .select-payment elements on the page this method is only available to the first one and not the ones created via javascript
$(document).ready(function() {
return $(".select-payment").on("change", function() {
return alert("hello");
});
});
Any idea how to make it work for any element that is added after the page is loaded that has a .select-payment class?
$(document).on("change", ".select-payment", function() {
alert("hello");
});
Also returning from within the change handler hardly makes sense, even less, returning the result of an alert.
You could use event delegation like below,
$(document).on('change', '.select-payment', function () {..
Replace the document with any closeby container that exist in DOM when executing the above line
Event delegation binds the event to the parent element and executes the handler when event.target matches the specified selector.
When targeting dynamically created elements, you need to use .on()'s delegated syntax:
$(document).on("change", ".select-payment", function() {
From the docs:
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page.
why are you putting return statement ? You must attach your event handler to the document and not the existing .select-payment.
Try this : $(document).on("change",".select-payment",function(){...});
$(document).on("change", ".select-payment", function () {
alert("hello"); }
);
You can replace document with any closer parent element which will always exist in DOM for better performance. Like
$('#closestId').on("change", ".select-payment", function () {
alert("hello");
}
);
if you use $("document") jQuery will search for a node/tag named as document like and wont find anything as document is actually an object.
But you could use $("body") as body is a node/element of DOM.
I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});
I have a click event that is defined already. I was wondering what the best way would be to append another event handler to this event without touching this code (if possible).
Is there a way to just append another event handler to the already defined click event?
This is the current click event definition:
$(".HwYesButton", "#HwQuizQuestions")
.append("<a href='javascript:void(0);' rel='.HwYesButton'>Yes</a>")
.click(function(event) {
var button = $(this).parent();
var questionId = button.attr('id');
$iadp.decisionPoint["HwQuizYourself"].Input.inputProvided(questionId);
button.find(".HwNoButton").removeClass("HwButtonSelected");
$(this).addClass("HwButtonSelected");
button.removeClass("HwQuestionSkipped");
$iadp.flat.setBoolean(questionId, true);
return false;
});
The only thing you can do is to attach another (additional) handler:
$(".HwYesButton", "#HwQuizQuestions").click(function() {
// something else
});
jQuery will call the handlers in the order in which they have been attached to the element.
You cannot "extend" the already defined handler.
Btw. your formulation is a bit imprecise. You are not defining a click event. You are only attaching click event handlers. The click event is generated by the browser when the user clicks on some element.
You can have as many click handlers as you want for one element. Maybe you are used to this in plain JavaScript:
element.onclick = function() {}
With this method you can indeed only attach one handler. But JavaScript provides some advanced event handling methods which I assume jQuery makes use of.
I know this is an old post, but perhaps this can still help someone since I still managed to stumble across this question during my search...
I am trying to do same kind of thing except I want my action to trigger BEFORE the existing inline onClick events. This is what I've done so far and it seems to be working ok after my initial tests. It probably won't handle events that aren't inline, such as those bound by other javascipt.
$(function() {
$("[onClick]").each(function(){
var x = $(this).attr("onClick");
$(this).removeAttr("onClick").unbind("click");
$(this).click(function(e){
// do what you want here...
eval(x); // do original action here...
});
});
});
You can just write another click event to the same and the both will get triggered. See it here
<a id="clickme">clickme</a>
<script>
$('#clickme').click(function () {
alert('first click handler triggered');
});
$('#clickme').click(function () {
alert('second click handler triggered');
});
</script>
Ajax can complicate the issue here. If you call two events the first being an ajax call, then the second event will probably fire well before the response comes back.
So even though the events are being fired in the correct order, what you really need is to get in to the callback function in the ajax call.
It's difficult to do without editing the original code.
Yes you can. Let's say that I have a tag cloud, and clicking a tag will remove the tag by default. Something like:
<div class="tags">
<div class="tag">Tag 1</div>
<div class="tag">Tag 2</div>
</div>
<script>
$(document).ready(function(){
$('.tag').click(function(e){
e.preventDefault();
$(this).fadeOut('slow',function(){
$(this).remove();
});
return false;
});
});
</script>
Now let's say that's bundled into an included library for a project, but you want to provide a developer-friendly way to intercept those click events and prompt the user an AYS (Are You Sure?) dialog. In your mind you might be thinking something like:
<script>
$(document).ready(function(){
$('.tag').beforeRemove(function(){
if (AYS() === false) {
return false; // don't allow the removal
}
return true; // allow the removal
});
});
</script>
The solution, therefore, would be:
<div class="tags">
<div class="tag">Tag 1</div>
<div class="tag">Tag 2</div>
</div>
<script><!-- included library script -->
$(document).ready(function(){
$.fn.beforeRemove = $.fn.click;
$('BODY').on('click','.tag',function(e){
e.preventDefault();
console.log('debug: called standard click event');
$(this).fadeOut('slow',function(){
$(this).remove();
});
return false;
});
});
</script>
<script><!-- included in your page, after the library is loaded -->
$(document).ready(function(){
$('.tag').beforeRemove(function(){
var sWhich = $(this).text();
console.log('debug: before remove - ' + sWhich);
return false; // simulate AYS response was no
// change return false to true to simulate AYS response was yes
});
});
</script>
In this example, the trick is that we extended jQuery with a beforeRemove trigger that is a duplicate of the click trigger. Next, by making the library utilize the $('BODY').on('click','.tag',function(e){...}); handler, we made it delay and call after our page's beforeRemove trigger fired. Therefore, we could return false in beforeRemove if we got a negative AYS condition and therefore not allow the click event to continue.
So, run the above and you'll see that clicks on the tags won't remove the item. Now, switch the beforeRemove function to return true (as if you had a positive response to an AYS prompt), and the click event is allowed to continue. You have appended an event handler to a preexisting click event.
When using
$('.foo').click(function(){
alert("I haz class alertz!");
return false;
});
in application.js, and
<a href = "" class = "foo" id = "foobar_1" >Teh Foobar </a>
in any div that initializes with the page, when clicking "Teh Foobar" it alerts and doesn't follow the link. However, when using the same code in application.js, and
<a href = "" class = "foo" id = "foobar_1" >Teh Foobar </a>
is being returned into a div by a
form_remote_tag
when clicked, "Teh Foobar" fails to alert, and functions as a link.
What is happening, and how do I get around it?
New elements added to the document after you bind your events don't automatically get those event handlers. One way to fix this is - as John Millikin says - re-bind your events after you create new elements.
The other standard way is event delegation. Because events go all the way up and down the stack through all their parent elements, you can bind an event to an element that will be an ancestor of all your target elements.
For instance, this jQuery code would work (your syntax may vary for other JavaScript libraries):
$(document).ready(function() {
$('body').click(function(event) {
if ($(event.target).is('.foo')) { // <- this is the magic
alert('Something of class foo was clicked.');
return false;
}
});
});
Now when you click something of class foo this event will get fired unless something in between catches the event and cancels the bubbling. Actually, event will be called when almost anything is clicked - the "if" statement just filters out which events deserve the alert.
Markup returned from an AJAX call isn't present when you set up the page, so it doesn't have any onclick handlers associated with it. You'll need to hook into the Rails AJAX support so that when it loads your AJAX-powered div, it also executes your event setup code again.
You could also use Live Query jQuery plugin which is able to automatically bind events for matched elements after the DOM is updated. In your case it would be:
$('.foo').livequery('click', function() {
alert("I haz class alertz!");
return false;
});