I have a table of cells, when a cell is clicked an event is triggered. I want to add cells dynamically so I will call OnClick again on all rows. When I call OnClick for the second time, any cells that have OnClick called twice stop firing any events.
The odd thing is at the event of my OnClick function, if I can "Return;" it works, however it throws an error saying "Return" isn't defined
function initBox() {
$(".selectable").on('click', function (event) {
//if its selected already, unselect it
if ($(this).hasClass('rowHighlightColor')) {
$(this).removeClass("rowHighlightColor");
selectedCellList = null;
}
else {
//unselect previous cell
if (selectedCellList != null) {
selectedCellList.removeClass("highlighted");
}
selectedCellList = $(this);
$(this).addClass("rowHighlightColor");
}
Return;
});
}
it needs to be return instead of Return (capital R)
however, writing return; returns undefined so you can just omit it.
edit:
you attach the event twice, make sure only to attach it once, else it causes (as you noticed) undesired behaviour like attaching class and immediately removing it again.
The reason why it works with "Return" is that the function is only run once because it throws an error when reaching it.
Use jQuerys .live() (or .on() for newer versions, as live is deprecated there) to automatically attach the click event to every new row you add. jQuery live Docs
You are adding multiple event handlers to existing cells. This is one reason why I prefer to use just the plain old .onclick property.
Anyway, to solve this issue you can either only apply the event to the new cells, or add an attribute to them when you do add an event, then check that attribute before adding the event again.
Related
This question already has answers here:
why is jQuery click event firing multiple times
(5 answers)
Closed 6 years ago.
I have a small todo list I made for practice. Here is a Fiddle
, I cant seem to figure out why the click event that toggle the class that adds and removes the line-through in the todo works on some items, but not others. I checked in the console and for some reason the ones that don't work, when clicked, are firing multiple times. If some one could give me some direction as to why this is happening I would greatly appreciate it.
// Toggle line-through todo
$('.todo-container').on('click', function(){
$(this).toggleClass('line-through');
console.log("fired")
})
Like Mike commented, you're adding a new handler to the elements again on every enter keypress, instead use event delegation
To further explain:
When you add an event handler as shown below, jQuery looks through the DOM and adds the handler directly to any elements that have the todo-container class.
$('.todo-container').on('click', function()...
or
$('.todo-container').click(function()...
An important caveat is that this will only add the handler to elements that currently exist on the page.
You saw this results of this when you realized that the handler did not function on newly created (dynamic) elements.
Not quite understanding why it didnt work, you moved the event bindings into the handler for the keyup effectively calling the binding each time a new element is created. This seemed to work at first but in practice is flawed because this again adds the handler directly to any elements that have the todo-container class. including the elements that already have a handler defined from a previous call.
Fix one, Event delegation (Prefered method)
In the below example we move the bindings back outside the keyup handler and use $('#todos').on('click', '.todo-container', to attach the listener to the '#todos' element (which always exists on the page). Then, any time you click inside that element, it checks if the child you clicked had the class "todo-container" and if so, will fire off your code. This is event delegation. This will catch events on any dynamic element that matches the selector
$(document).on('keypress', function(e) {
// Add todo
if (e.which === 13 && $('.input-field').val() != "") {
$('#todos').prepend(todoTemplate);
var todo = $('.todo-container');
$('#todos').children().first().find(todo).html($('.input-field').val());
$('.input-field').val("");
}
})
// Remove todo
$('#todos').on('click', '.todo-container-delete', function() {
$(this).parent().remove();
})
// Toggle line-through todo
$('#todos').on('click', '.todo-container', function() {
$(this).toggleClass('line-through');
console.log("hello")
})
Fix two, more specific targeting with :last
You could actually leave the bindings inside the keyup handler, if you specifically target only the newly added element like this:
$('#todos .todo-container:last').on('click', function(){
or
$('#todos .todo-container:last').click(function(){
Fix three, (not really recommended but possible) .off()
You could also leave the bindings inside the keyup handler, if you use .off() to remove the handlers from the previous elements before adding it to all the elements again like this:
$('.todo-container').off().on('click', function(){
I'd avoid this method though because if you dont specifically target a handler to remove (see documentation for how), you are removing all handlers applied to that element wich could definitely bite you down the road
So I have a button inside a list row that is used to delete the row from the page (calls ajax stuff to delete the object represented by the row, but that's not important for my question). The whole row is bound to a click event which would redirect to another page.
In other words, the containing row is click bound and the inner button is click bound, which is causing me problems since clicking the inner button also triggers the containing row click event (as it should).
I've tried binding a hover event for all delete buttons that unbinds the row click on mouseover, and rebinds it on mouseout, like this pseudocode below:
$('.delete-button').hover(
function() {
$('.list-row').unbind();
$('.delete-button').bind('click', function() { /* delete action */ });
},
function() {
$('.delete-button').unbind();
$('.list-row').bind('click', function() { /* list row action */ });
}
);
This isn't working very well, and I'm convinced there is a better way to approach it. Should I take the button out of the containing list-row? It's way easier to have it in there since my list row contains custom attributes that have data I need for the ajax calls and I can just var rid = $('.delete-button).parent().attr('row-id'); to get the data, but I'm not opposed to change :)
Thanks!
In your click event handler for the button, you need to call e.stopPropagation(). This will prevent the event from bubbling up the DOM tree. More info here: http://api.jquery.com/event.stopPropagation/
edit: you already accepted (thanks!), but maybe this code snippet would help explain some of the concepts better:
$('.list-row').click(function() {
/* list row action */
});
$('.delete-button').click(function(e) {
// die, bubbles, die
e.stopPropagation();
// if you also need to prevent the default behavior for the button itself,
// uncomment the following line:
// e.preventDefault();
// note that if you are doing both e.stopPropagation() AND e.preventDefault()
// you should just `return false;` at the end of the handler (which is jQuery-
// sugar for doing both of these at once)
/* delete action */
})
There's a few ways of approaching this. As #jmar777 has already said you may attach an altered event to the click handler on the button, stopping propagation.
If you want to do this with the same function as you're applying to the div then you can approach it as such:
if($(event.target).is("input")) {
event.stopPropagation();
}
Another approach is to actually not bind the click event to the button, for any time the browser supports clicks on the containing element. As you will always trigger that, then you don't actually need the button to handle it too! This does require you to handle IE6 etc a little differently from everything else though...
Let your handler function return false
After search and click on result search and click on plus(button add input) next to input "INSERT VALUE HERE" in the example, in new input $('.auto_complete').keyup(function () { ... not work.
I believe that have to bind the events separately and use a closure so that each element has its own set of variables(or change the logic so that only use the value in the field and don't need any state variables),
how is it?
EXAMPLE: see here
Js full code: http://jsfiddle.net/6yPxn/
$.each:
var ac = $(this).text();
var ok = $.grep(data, function (e) {
return e.name == ac;
})[0].units;
$.each(ok, function (bu, key) {
//alert(key.name_units);
$("<div class='mediumCell'/>").hide().fadeIn('slow').append('<b>' + key.name_units + '</b>', $('<div class="column" style="float: left;" />')).appendTo(".list_units");
});
It runs fine, but I don't see anywhere in that code you've provided where you're adding an event handler to the input box.
The issue is in http://www.binboy.gigfa.com/files/js/admin.js, somewhere around the top:
$('.auto_complete').bind('keyup',function () {
/* ... */
});
When the page loads it binds several event handlers to input boxes and the like. When you create a new one this functionality is not added unless you're using jQuery's .live or something similar. As the documentation notes:
This method [.live()] is a variation on the basic .bind() method for attaching event handlers to elements. When .bind() is called, the elements that the jQuery object refers to get the handler attached; elements that get introduced later do not, so they would require another .bind() call.
I don't really want to wade through all the nested click and delegate and bind calls, but I guarantee you that's where your problem lies. To fix it you'll probably need to have either the autocomplete section run on your newly created node, use .live instead, or just .clone the original.
Is it possible to remove than add back click event to specific element? i.e
I have a $("#elem").click(function{//some behaviour});, $(".elem").click(function{//some behaviour});(there are more than 1 element) while my other function getJson is executing I'd like to remove the click event from the #elem, and add it again onsuccess from getJson function, but preserve both mouseenter and mouseleave events the whole time?
Or maybe create overlay to prevent clicking like in modal windows? is that better idea?
edit :
I've seen some really good answers, but there is one detail that I omitted not on purpose. There are more than one element, and I call the click function on the className not on elementId as I stated in the original question
Rather than using unbind(), which means you'll have to rebind the same event handler later, you can use jQuery's data() facility with the ajaxStart and ajaxStop events to have your elements ignore click events during all AJAX requests:
$(".elem").click(function() {
if (!$(this).data("ajaxRequestPending")) {
// some behaviour
}
}).ajaxStart(function() {
$(this).data("ajaxRequestPending", true);
}).ajaxStop(function() {
$(this).removeData("ajaxRequestPending");
});
EDIT: This answer is also id-to-class-proof (see questioner's edit), since everything matching the selector will handle the AJAX events the right way. That's the main selling point of jQuery, and it shows.
You are looking for .unbind(). Pass it 'click' and it will destroy the click event.
I would put it just before your getJSON and re-bind the click event inside the success handler of your ajax call.
You have to do some additional scripting. There is no callback for that. Take a look over here: jQuery - How can I temporarily disable the onclick event listener after the event has been fired?
Rather than unbinding/binding the click event, you could check the state of another variable to see if it should do the action.
var MyObject = {
requestActive = false;
};
function MyJsonFunction() {
// when requesting
MyObject.requestActive = true;
//...
// when request over
MyObject.requestActive = false;
}
$("#elem").click(function{
if (MyObject.requestActive == true) {
//do something
}
});
I have added an onclick event, which opens a new tab/page, to a button which has an action already (I don't know which action cause it isn't displayed in source, or maybe it's a form submit action).
Button originally also opens a page in new tab, what I want to do is fire up some function after my onclick attribute executes which will stop execution and that default page opening will not happen, only my page will load.
Is there something that can be done?
It sounds like the event is bubbling up after you have handled it. To stop this happening, add event.cancelBubble = true at the end of your handler.
Using jQuery, you can do this:
$('button').click(function() {
//do something
return false; // stop the event from propagating
});
Try getting your function that your call on the onclick event to return false. This will cause any existing action by the button to be overridden
If the handler was set using 'onclick=' you can simply rewrite the onclick property.
If a handler was added to an element with addEventHandler or attachEvent, you can remove it with removeEventHandler or detachEvent, using the same parameters that were used to set it.
If you don't know the setter, or if it used an anonymous function, you can't remove it.
You could replace the element with a duplicate that has no events and set your own on the new element.
(cloneNode is not supposed to clone events, but it sometimes does in some browsers.)