When jquery.delegate call the event handler? - javascript

Recently , Ive been using .delegate and .live a lot.They have a subtle difference when it comes to event capturing I guess.
When using live for link clicks like $('a').live("click",... , the links which had an image as their html content, ended up with the click handler getting the target as the image instead of the link.
Whereas with delegation ,it seems that it the link which is passed as the target.
What is the catch here?
Also, when exactly is a click handler called for .delegate, while the capturing phase or the bubbling phase?

The main difference between .live and .delegate is, that .delegate() uses a context. In other words, .delegate() is actually a wrapper for .live(), but instead watching the document root for bubbling events, it'll just watch a given root node.
For instance
$('a').live('click', function() {
});
will create an event handler attached to your document.body. This will catch absolutly all anchors-clicks that occur on your site.
$('#someDiv').delegate('a', 'click', function() {
});
will only "watch" all anchors which are childnodes from #someDiv.
It's unlikely that a delegated event by .live() have another target. Both .live() and .delegate() pass in the event object into the handler. It should make no difference at all, the event.target should always be the node of invocation.
Ref.: .live(), .delegate()

Related

Event stops working after dynamic ul li changes

The Code:
if($('.info-dropdown').length){
setTimeout(function(){
$('li').has('input[type="checkbox"]').on('click', function(){
$(this).find('.brand-checkbox').parent().toggleClass('active');
});
}, 10);
}
The Problem: This code detects event click on element checkbox. After dynamically changing this ul li the event stops working.
Note: These checkboxes are from bootstrap dropdown menu.
To bind event for dynamic HTML, You can follow below code :
$('containerSelector').on('eventName', 'mainElementSelector' function(e){
});
Realtime example
$("ul").on("click", "li:has(:checkbox)", function(){
});
Event handlers added directly to an object are added only to that specific DOM object. If you then add or replace more DOM objects, those DOM object won't have any of these event handlers on them. You will have to either manually add the event handlers after you create or replace the DOM objects or you will have to switch to using delegated event handling.
Delegated event handling attaches the event handler to a common parent object (that is not replaced) and uses the fact that many events bubble up the parent chain in order to process the event from the common parent. This allows you to freely create or replace child elements, but still have one event handler that works for all child objects.
You can read a lot more about how to do delegated event handling in these other answers:
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
As illustrated in those referenced answers, the general idea is like this:
$("#staticParentSelector").on("click", ".selectorForDynamicChildren", function(e) {
// event handler code here
});

What's the right mindset in using jQuery's event.stopPropagation()

I have two approaches in mind on how to apply jQuery's event.stopPropagation():
Optimistic approach - By default, all event handlers are without stopPropagation(). stopPropagation() would be added to a handler only if there's some unnecessary and undesired behaviour caused by event bubbling.
Pessimistic approach - By default, stopPropagation() is added to all event handlers. If there's some desired behaviour missing due to disconnected even bubbling, then the corresponding stopPropagation() would get removed.
I was wondering which one of the two, or neither, is the mindset I should have when utilizing stopPropagation() in my development. I am leaning more towards 2) because it looks like it would have better performance because unnecessary bubbling gets prevented. I would like to hear from experts on the topic.
Calling stopPropagation everywhere isn't good. It breaks event delegation*, is probably less efficient unless you literally add handlers to every single element on the page (and if you're doing that, you should be using event delegation), and you might not remember to do it.
So rule of thumb: Only call stopPropagation when you need to do so.
* That is, .live, .delegate, and the delegate version of .on.
Here's an example of event delegation, which relies on bubbling to listen for events that originate from elements added after the listener was created:
document.documentElement.addEventListener('click', function(e) {
if(e.target.nodeName === 'a') {
console.log('A link was clicked!');
}
});
Here's somebody throwing in jQuery that breaks that:
$('div').click(function(e) {
e.stopPropagation();
});
Any <a> that is a descendant of a <div> will no longer get its clicks handled! Please don't do this.
Call stopPropagation only if you realy need it. And remember, if you use jQuery live function and you stop it, live handler never calls, because live event stored in document element.

Adding event listeners to current and future elements with a particular class

With JQuery, is it possible to add an event listener to any element that currently, or will in the future, have a particular class?
I'm working on a project that makes heavy use of contentEditable, so the DOM is changing, and elements can have classes added and removed as a result of user input.
I would like to be able to say "elements of class X should do Y when clicked", but if I understand correctly, $(".X").click(Y) will only add the event listener to elements that currently have class X.
Furthermore, if an element is no-longer part of class X, then it will still have the click event listener.
How can I do this?
Yep. What you're talking about is called event delegation. Here's an example:
$('#container').on('click', '.innerElement', function(){
/// Do stuff
});
In your case, #container would be an element that is known to exist on page load which will contain the child elements you care about (either now or in the future). This approach takes advantage of event bubbling in the DOM.
As another poster mentioned, the live method will also work -- but it has been deprecated in jQuery 1.7, and is generally not as performant as using more selective delegation (such as the example above).
you'll want to use event delegation. jquery 1.7 has made this more abstract than previous versions, but it looks something like this:
$("#myWrappingElement").on("click", ".myclass", function(event){
alert($(this).text());
});
this basically adds a click event listener to the #myWrappingElement element, and jquery will automagically look to see what the original event target was and fire the proper function. this means you can add or remove .myclass elements and still have events fire on them.
the jQuery live() method swill allow to have a "live" action listener - so if new DOM elements match the selector, they will be attached to the action listener. For example:
$(".X").live("click", function(){
alert('some action');
});
See the documentation here for more info: http://api.jquery.com/live/
I'm not sure that the second part of your question about keeping the action listener attached after removing the class os possible - someone else might have a solution though.

Global jQuery `.click()`

I would like to fire an event when anything on the page is clicked, and then process normally. For example a click would be fired, I would see if the target matched something, alert if it did, and then have the click event continue (no preventDefault()).
$(document).click(function(e) {
// e.target is the element which has been clicked.
});
This will handle all click events unless a handler prevents the event from bubbling up (by calling the stopPropagation() method of the event object).
$("body").click(function (event) {
// Your stuff here
}
3 options for you:
This is how .live() in jquery works. Everything bubbles to the top, and it matches the selector you set.
http://api.jquery.com/live/
A more efficient way to do it is using .delegate, or providing a context to .live() so you don't have to bubble to the top.
http://api.jquery.com/delegate/
If you want to do it manually, bind 'click' to the document, and use .closest() to find the closest matching selector:
http://api.jquery.com/closest/
It's all the same concept, event delegation as mentioned already.

jQuery equivalent of JavaScript's addEventListener method

I'm trying to find the jQuery equivalent of this JavaScript method call:
document.addEventListener('click', select_element, true);
I've gotten as far as:
$(document).click(select_element);
but that doesn't achieve the same result, as the last parameter of the JavaScript method - a boolean that indicates whether the event handler should be executed in the capturing or bubbling phase (per my understanding from http://www.quirksmode.org/js/events_advanced.html) - is left out.
How do I specify that parameter, or otherwise achieve the same functionality, using jQuery?
Not all browsers support event capturing (for example, Internet Explorer versions less than 9 don't) but all do support event bubbling, which is why it is the phase used to bind handlers to events in all cross-browser abstractions, jQuery's included.
The nearest to what you are looking for in jQuery is using bind() (superseded by on() in jQuery 1.7+) or the event-specific jQuery methods (in this case, click(), which calls bind() internally anyway). All use the bubbling phase of a raised event.
As of jQuery 1.7, .on() is now the preferred method of binding events, rather than .bind():
From http://api.jquery.com/bind/:
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document. For earlier versions, the
.bind() method is used for attaching an event handler directly to
elements. Handlers are attached to the currently selected elements in
the jQuery object, so those elements must exist at the point the call
to .bind() occurs. For more flexible event binding, see the discussion
of event delegation in .on() or .delegate().
The documentation page is located at
http://api.jquery.com/on/
The closest thing would be the bind function:
http://api.jquery.com/bind/
$('#foo').bind('click', function() {
alert('User clicked on "foo."');
});
One thing to note is that jQuery event methods do not fire/trap load on embed tags that contain SVG DOM which loads as a separate document in the embed tag. The only way I found to trap a load event on these were to use raw JavaScript.
This will not work (I've tried on/bind/load methods):
$img.on('load', function () {
console.log('FOO!');
});
However, this works:
$img[0].addEventListener('load', function () {
console.log('FOO!');
}, false);
You should now use the .on() function to bind events.
$( "button" ).on( "click", function(event) {
alert( $( this ).html() );
console.log( event.target );
} );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button>test 1</button>
<button>test 2</button>
Here is an excellent treatment on the Mozilla Development Network (MDN) of this issue for standard JavaScript (if you do not wish to rely on jQuery or understand it better in general):
https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener
Here is a discussion of event flow from a link in the above treatment:
http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
Some key points are:
It allows adding more than a single handler for an event
It gives you finer-grained control of the phase when the listener gets activated (capturing vs. bubbling)
It works on any DOM element, not just HTML elements
The value of "this" passed to the event is not the global object (window), but the element from which the element is fired. This is very convenient.
Code for legacy IE browsers is simple and included under the heading "Legacy Internet Explorer and attachEvent"
You can include parameters if you enclose the handler in an anonymous function

Categories

Resources