This question already has answers here:
jQuery .on() method doesn't see new elements
(5 answers)
Closed 8 years ago.
I am using Jquery 2.1.0. I have injected some elements to the DOM (containing label) into a div element. I am using the following JavaScript to handle click event on all labels but it is completely dead and not responding. I checked and Jquery is loaded.
$('label').on("click", function () {
var ul = $(this).parent().children("ul:first");
if (ul.is(':visible')) {
ul.css({ "display": "none" });
}
else {
ul.css({ "display": "block" });
}
});
I used developer tools in IE 10 and debugged the code. When I hit F5 it goes to my break point (on first line of my code) but when I click a label nothing happens and no errors.
This will only assign the event handler to label elements which exist initially on the page:
$('label').on("click", function () {
// ...
});
To catch elements which are added later, you need to assign the event handler to a common parent element (which isn't added/removed during the life of the page) and filter the source elements, like this:
$(document).on('click', 'label', function () {
// ...
});
Note that there are two selectors:
document
'label'
The first, which is the target of the event handler, is only evaluated once when the page loads (or when this line of code is evaluated, which is generally when the page loads). This attaches the handler to the document object. Since all events "bubble up" to parent elements, this will catch all click events on the page (unless propagation is explicitly stopped, of course).
The second, which is the "filter" for the .on() method's handler, is evaluated any time an event is caught by this handler. It filters out the elements which originated the click event so that the handler is executed only for those which match the filter.
I've actually recently blogged about this very subject.
You need to use event delegation since your label has been dynamically created element:
Event delegation allows us to attach a single event listener, to a
parent element, that will fire for all children matching a selector,
whether those children exist now or are added in the future.
$('body').on('click','label', function() {
// Your code here
});
Basically, event delegation will helps you to attach click event to newly added label elements in your case
you should use event delegation for that
$(document).on("click","label",function(){
});
Event delegation allows you to attach a single event listener, to a parent element, that will fire for all children matching a selector, whether those children exist now or are added in the future.
From this answer to another question here: https://stackoverflow.com/a/8337382/2407870
It seems that the method doesn't work for some inline elements in Chrome. If your element is inline, try changing the display to block.
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
In jQuery I have experienced a difference in how my event handlers work, depending on whether I split the selector.
Selector not split (#myId .someClass):
$('#myId .someClass').on('click', function (e) {
alert('x');
});
Selector "split" (#myId ........ .someClass):
$('#myId').on('click', '.someClass', function (e) {
alert('x');
});
When I use the latter, I will get the same event multiple times from same click, whereas the first only give me the click event once (however I sometimes experience that the first one does not even work).
Can someone explain why there is this difference?
The difference is e.g. that the first version
$('#myId .someClass').on('click', function (e) { ...
binds the click event to all .someClass elements that are descendants of the element with the id #myId and are already in the DOM when the page is loaded, while the second version
$('#myId').on('click', '.someClass', function (e) { ..
will delegate the click event from the #myId element to all descendant elements with the class .someClass, even if they are dynamically added later.
For reference: http://api.jquery.com/on/
As one essential quote from there, section "Direct and delegated events":
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().
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.
Below two scenario give me the same behavior. But What is the difference technically? (I put the below code in the last section of script tags in the body.)
$(document).ready(function() {
$('.collapse').collapse({toggle: false});
$(document).on('click', '#expandAllLessons', function() {
$('div.accordion-body').collapse('show');
});
$(document).on('click', '#collapseAllLessons', function() {
$('div.accordion-body.collapse').collapse('hide');
});
});
or
$(document).ready(function() {
$('.collapse').collapse({toggle: false});
});
$(document).on('click', '#expandAllLessons', function() {
$('div.accordion-body').collapse('show');
});
$(document).on('click', '#collapseAllLessons', function() {
$('div.accordion-body.collapse').collapse('hide');
});
Thanks.
More or less, it's doing the same thing.
With the use of .on() with a child selector, you're using event delegation to bind any future events to any elements that match that selector. document is the very tippy top of the DOM tree (and available upon script execution), so your event delegation works.
.ready() waits until the DOM has assembled, so you can, more reliably, directly bind events using methods like .click(), .hover(), etc.
So your first example is just waiting for the DOM to assemble, then delegating the event. The second example is just delegating the event immediately upon script execution.
From jQuery's documentation regarding .on():
Direct and delegated events
The majority of browser events bubble, or
propagate, from the deepest, innermost element (the event target) in
the document where they occur all the way up to the body and the
document element. In Internet Explorer 8 and lower, a few events such
as change and submit do not natively bubble but jQuery patches these
to bubble and create consistent cross-browser behavior.
If selector is omitted or is null, the event handler is referred to as
direct or directly-bound. The handler is called every time an event
occurs on the selected elements, whether it occurs directly on the
element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as
delegated. The handler is not called when the event occurs directly on
the bound element, but only for descendants (inner elements) that
match the selector. jQuery bubbles the event from the event target up
to the element where the handler is attached (i.e., innermost to
outermost element) and runs the handler for any elements along that
path matching the selector.
Whenever you do a function, regardless of whether it's $(document).ready(function(){}); or something else, all the contents inside that function can only read stuff that's at its level or above it (unless you're using return functions).
The top paragraph means that all your code won't be executed until it's loaded, but it also means that it's nested code. Nested code means certain variables and functions won't be readable from outside. Example:
function bob(){
function sandy(){
function joe(){
alert("I can access anything written by sandy, bob or ray!");
}
}
}
function ray(){
alert("I can't see anything from anybody but bob!");
}
Look at the comments first. After jQuery 1.7 on can delegate events as well:
"The .on() method attaches event handlers to the currently selected
set of elements in the jQuery object. As of jQuery 1.7, the .on()
method provides all functionality required for attaching event
handlers. For help in converting from older jQuery event methods, see
.bind(), .delegate(), and .live()."
So before jQuery 1.7 this is the correct answer:
First is better because, document ready event is triggered when HTML document is fully loaded to DOM. And then you're sure you have all elements in place, and you can bind events to them.
But if you bind event before loading '#expandAllLessons' element to DOM, then it will simply not work, as jQuery selector will not find any elements, and will not bind this event anywhere.
After 1.7 both will work almost in the same way. Almost, because in first case, when you trigger event before document will be ready, it will not be executed. In second example it will be executed, because it was attached when script was loaded.
I'm using jQuery's .on() event handler and it's only working when I use $(document).
This works:
$(function() {
$(document).on("click", ".search .remove", function(e) {
console.log("clicked");
});
});
This does not work:
$(function() {
$(".search .remove").on("click", function(e) {
console.log("clicked");
});
});
Nothing happens on that second one...no errors or anything. It just doesn't fire.
You are using two different syntaxes of .on which have two very different outcomes.
Your first is:
$(context).on("event","targetselector",handler)
This binds the event to context, and any events of type event that gets to the context that has an e.target that can be selected with targetselector will trigger the handler with e.target as the context. this is commonly known as event delegation.
Your second syntax is
$(targetselector).on("event",handler)
In this case, the event is bound directly to the elements currently on the page that match targetselector, not future elements. This is essentially the same as the old .bind.
Your second example doesn't work because your elements are created dynamically. When using .on() with dynamically inserted elements, you have to bind it via an element that isn't inserted dynamically, i.e. one that exists on the page at load time.
You can continue to use document as an ancestor element but in terms of performance you might want to find an element closer in the DOM to ".search .remove".
From the jQuery docs on .on():
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. Or, use delegated events to attach an event
handler, as described next.
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time. By
picking an element that is guaranteed to be present at the time the
delegated event handler is attached, you can use delegated events to
avoid the need to frequently attach and remove event handlers. This
element could be the container element of a view in a
Model-View-Controller design, for example, or document if the event
handler wants to monitor all bubbling events in the document. The
document element is available in the head of the document before
loading any other HTML, so it is safe to attach events there without
waiting for the document to be ready.
Your first method is the on() equivalent for the deprecated method live(). Probably your elements get inserted dynamically after the page loading has finished.
You could rewrite your code like following and it should work:
$(function() {
$(".search").on("click", ".remove", function(e) {
console.log("clicked");
});
});