I have a touchscreen page with an element <div id="x"> with several sub-elements in the form of:
<div id="x_1" style="certain_class" pid="1723464"></div>
<div id="x_2" style="certain_class" pid="1723465"></div>
<div id="x_3" style="certain_class" pid="1723466"></div>
<div id="x_4" style="certain_class" pid="1723467"></div>
These elements have a jQuery touchstart bind event set on them... Now if I alter these sub-elements using:
$("#x").html("<div id="x_1"></div><div id="x_2"></div><div id="x_3"></div><div id="x_4"></div>");
which changes these sub-elements to:
<div id="x_1"></div>
<div id="x_2"></div>
<div id="x_3"></div>
<div id="x_4"></div>
Why is it I seem to lose the touchstart bind set to these elements? Is it because I'm rendering the sub-elements within the parent element null and void, and thus the binding event?
Should I be changing the extra parameters on these elements bit by bit through the individual sub-elements within the parent node, rather than using the .html() method on the parent node?
Any help is greatly appreciated...
When you replace child elements like you are doing with the .html() jQuery method, then the old DOM elements that had event handlers bound to them are completely removed and no longer in the DOM. Thus, your event handlers that were on those elements are then gone.
You have several options:
Stop replacing the elements. Instead, just modify them so that the same elements stay in the page and thus their event handlers stay intact.
Reattach the event handlers after you set .html() to install new event handlers to the new elements.
Use delegated event handling that is attached to a parent object that is not destroyed/recreated.
My first choice is to stop replacing the elements if that is practical. I don't know exactly what type of change you're trying to have take place so I don't know how simple this would be.
Oftentimes, delegated event handling is the most elegant solution. You attach an event handler once to a parent and it will stay in effect even though you destroy and recreate the children. In this case, here's one way to do that:
$("#x").on("touchstart", "[id^='x_']", function(e) {
// event handler code here
// the this pointer will point to the origional DOM object that caused the event
});
This attaches a delegated event handler to the #x object and that event handler fires anytime the touchstart event happens on any child object whose id attribute starts with "x_".
Here are some other references on delegated event handling:
jQuery .live() vs .on() method for adding a click event after loading dynamic html
Does jQuery.on() work for elements that are added after the event handler is created?
JQuery Event Handlers - What's the "Best" method
Pretty much you are deleting the existing elements (and their bindings), and creating new ones with identical ids (but without bindings). One solution would be to use jQuery.fn.on to bind events. This way the binding will be part of x which is kept.
$('#x').on('touchstart', '#x_1, #x_2, #x3, #x4', yourFunction)
Related
I am trying to understand this particular difference between the direct and delegated event handlers using the jQuery .on() method. Specifically, the last sentence in this paragraph:
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.
What does it mean by "runs the handler for any elements"? I made a test page to experiment with the concept. But both following constructs lead to the same behavior:
$("div#target span.green").on("click", function() {
alert($(this).attr("class") + " is clicked");
});
or,
$("div#target").on("click", "span.green", function() {
alert($(this).attr("class") + " is clicked");
});
Maybe someone could refer to a different example to clarify this point? Thanks.
Case 1 (direct):
$("div#target span.green").on("click", function() {...});
== Hey! I want every span.green inside div#target to listen up: when you get clicked on, do X.
Case 2 (delegated):
$("div#target").on("click", "span.green", function() {...});
== Hey, div#target! When any of your child elements which are "span.green" get clicked, do X with them.
In other words...
In case 1, each of those spans has been individually given instructions. If new spans get created, they won't have heard the instruction and won't respond to clicks. Each span is directly responsible for its own events.
In case 2, only the container has been given the instruction; it is responsible for noticing clicks on behalf of its child elements. The work of catching events has been delegated. This also means that the instruction will be carried out for child elements that are created in future.
The first way, $("div#target span.green").on(), binds a click handler directly to the span(s) that match the selector at the moment that code is executed. This means if other spans are added later (or have their class changed to match) they have missed out and will not have a click handler. It also means if you later remove the "green" class from one of the spans its click handler will continue to run - jQuery doesn't keep track of how the handler was assigned and check to see if the selector still matches.
The second way, $("div#target").on(), binds a click handler to the div(s) that match (again, this is against those that match at that moment), but when a click occurs somewhere in the div the handler function will only be run if the click occurred not just in the div but in a child element matching the selector in the second parameter to .on(), "span.green". Done this way it doesn't matter when those child spans were created, clicking upon them will still run the handler.
So for a page that isn't dynamically adding or changing its contents you won't notice a difference between the two methods. If you are dynamically adding extra child elements the second syntax means you don't have to worry about assigning click handlers to them because you've already done it once on the parent.
The explanation of N3dst4 is perfect. Based on this, we can assume that all child elements are inside body, therefore we need use only this:
$('body').on('click', '.element', function(){
alert('It works!')
});
It works with direct or delegate event.
Tangential to the OP, but the concept that helped me unravel confusion with this feature is that the bound elements must be parents of the selected elements.
Bound refers to what is left of the .on.
Selected refers to the 2nd argument of .on().
Delegation does not work like .find(), selecting a subset of the bound elements. The selector only applies to strict child elements.
$("span.green").on("click", ...
is very different from
$("span").on("click", ".green", ...
In particular, to gain the advantages #N3dst4 hints at with "elements that are created in future" the bound element must be a permanent parent. Then the selected children can come and go.
EDIT
Checklist of why delegated .on doesn't work
Tricky reasons why $('.bound').on('event', '.selected', some_function) may not work:
Bound element is not permanent. It was created after calling .on()
Selected element is not a proper child of a bound element. It's the same element.
Selected element prevented bubbling of an event to the bound element by calling .stopPropagation().
(Omitting less tricky reasons, such as a misspelled selector.)
I wro te a post with a comparison of direct events and delegated. I compare pure js but it has the same meaning for jquery which only encapsulate it.
Conclusion is that delegated event handling is for dynamic DOM structure where binded elements can be created while user interact with page ( no need again bindings ), and direct event handling is for static DOM elements, when we know that structure will not change.
For more information and full comparison -
http://maciejsikora.com/standard-events-vs-event-delegation/
Using always delegated handlers, which I see is current very trendy is not right way, many programmers use it because "it should be used", but truth is that direct event handlers are better for some situation and the choice which method use should be supported by knowledge of differences.
Case 3 (delegated):
$("div#target").delegate("span.green", "click", function() {...});
I need to bind the change of all the textboxes(having class .box) under Box2 column who have the blue button(having class .blue which is dynamic in some cases) in their row.
Below is the screenshot of the HTML I have which is just a HTML table which has 3 columns(Box1,Box2 & for the button):
Class .blue could be present on some buttons on the page load & also could be removed/added to the buttons as result of another Ajax operation.
jQuery .on() has
.on( events [, selector ] [, data ], handler )
is it possible to use .filter() in the selector part where I can use a function like
jQuery('.box').filter(function(){
return jQuery(this).closest('tr').find('.blue').length
});
to filter out the needed textboxes & then bind there change event?
Considering the #parent is the parent div of the HTML I was trying
jQuery('#parent').on('change','.box',..); but this will listen to the change of all the box2 elements whereas I want it to listen to the specific ones only.
Ideas please?
Thanks.
In event delegation, we are binding events on the parent element and listening events on it based on the target(selector). The only thing we are certain about is the specific-selector of the target-element. Well, in the cases link specific-child or specific-sibling of the dynamically appended element, doing things in the handler(callback) is the only way!
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.
Note: Attaching many delegated event handlers near the top of the document tree can degrade performance. Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path from the event target up to the top of the document. For best performance, attach delegated events at a document location as close as possible to the target elements. Avoid excessive use of document or document.body for delegated events on large documents. [Reference]
use a if to test if the button(.box) has a blue class
$('.parent').on('change','.box2 input',function(){
if($(this).closest('tr').find('.box').is('.blue')) {
//do code here
}
});
Include a separate check for the button. Without any code it's not possible to give you the correct code
jQuery('#parent').on('change','.box',..);
if ($(this).next().hasClass('blue')) {
// Do other stuff
}
I always wondered which is the better way of handling events in terms of code manageability, cleanliness and code reuse.
If you use the former then say a list of 10 anchor tags with click handler will have something like:
Click Me
Click Me
Click Me
... 10 times
which looks kind of odd.
With the latter method, using anonymous function, it'd be like:
$('a').on('click', function(e){});
At the end of the day, every event is bound to some element in the DOM. In the case of .bind, you're binding directly to the element (or elements) in your jQuery object. If, for example, your jQuery object contained 100 elements, you'd be binding 100 event listeners.
In the case of .live, .delegate, and .on, a single event listener is bound, generally on one of the topmost nodes in the DOM tree: document, document.documentElement (the element), or document.body.
Because DOM events bubble up through the tree, an event handler attached to the body element can actually receive click events originating from any element on the page. So, rather than binding 100 events you could bind just one.
For a small number of elements (fewer than five, say), binding the event handlers directly is likely to be faster (although performance is unlikely to be an issue). For a larger number of elements, always use .on.
The other advantage of .on is that if you add elements to the DOM you don't need to worry about binding event handlers to these new elements. Take, for example, an HTML list:
<ul id="todo">
<li>buy milk</li>
<li>arrange haircut</li>
<li>pay credit card bill</li>
</ul>
Next, some jQuery:
// Remove the todo item when clicked.
$('#todo').children().click(function () {
$(this).remove()
})
Now, what if we add a todo?
$('#todo').append('<li>answer all the questions on SO</li>')
Clicking this todo item will not remove it from the list, since it doesn't have any event handlers bound. If instead we'd used .on, the new item would work without any extra effort on our part. Here's how the .on version would look:
$('#todo').on('click', 'li', function (event) {
$(event.target).remove()
})
Second method is preferrable, since we should not be mixing our JavaScript with the HTML. (Separation of Concerns) . This way your code is kept clean.
This also works well with dynamically inserted HTML code.
`$('a').on('click', function(e){});` // Using jQuery.
Using Vanilla JS:
document.getElementById("idName").addEventListener("click", function(){}); // Bind events to specific element.
document.addEventListener("click", function(){}); // Bind events to the document. Take care to handle event bubbling all the way upto the document level.
Just wondering how is this managed from the memory point of view.
Let's say I have this HTML page.
<div id="container">
<div id="someID"></div>
<div>
and the following jQuery code:
$("#someID").click(function(){
//do something
});
Now somewhere in my script I need to empty (clear) all the content in #container:
$("#container").empty();
Does this automatically remove/unbind the click event, or do I have to do it myself?
Is this something browser specific?
Yes, the .empty() method unbinds handlers, and clears all other data stored in jQuery.cache for all elements nested within #container.
jQuery only binds a single (generic) handler to an element. All other handlers and data are stored in jQuery.cache. The data for each element is cross-referenced by a serial number that jQuery puts directly on the DOM node.
So this is a jQuery specific system. The only browser specific concern is how jQuery binds the generic handler, and jQuery takes care of that unbinding as well.
From the docs:
"To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves."
I think what you're looking for, is..
$('something').remove(); /removes element and children
$('something').empty(); // clears children
Both would remove all data and events associated with the removed elements.
jQuery.empty():
To avoid memory leaks, jQuery removes other constructs such as data
and event handlers from the child elements before removing the
elements themselves.`
If you want to remove elements without destroying their data or event
handlers (so they can be re-added later), use .detach() instead.
$("#container").unbind(); // Remove a previously-attached event handler from the elements.
$('something').empty(); // Clear children.
$('something').remove(); // Removes element and children.
I've a table generated dynamically by jQuery, using
this.html("<table><tr><td><div>Click Me</div></td></tr></table>");
within the table, I've a few divs (my sample shows only one to keep things simple), which I want to add click event handler to. I'd like to keep html clean and use as much of jQuery power as I can, but since I'm doing an 'eval' type of things I can't quite figure out how to do that.
I know, that I can use $("div[some attribute selector]").on("click", {}, clickHandler);, but is it a good idea in my case?
You need delegated events. To do that, simply use jQuerys on() method like this:
$(document.body).on('click', 'div', function( event ) {
// do something
});
Ref.: .delegate(), .on()
What is that? Almost all events do what we call 'bubble'. That means, if you click on a nested element, your browser looks if there is any click-event handler ascociated on that node. If so, it executes them and then the parent of that element is also asked if there is any click-event handler. This continues until either some handler prevents the event from further bubbling or we have reached the document.documentElement (html).
So, you should change the above document.body into the closest node relative to your dynamically added elements.
You can use either use live or delegate to do that