How to use jQuery this with .on function and .each function - javascript

I am using jQuery each function and on click on current i.e $(this) I am executing JS code, It will be more clear if you look on following code
$('.switch-cal').each(function(){
$(this).on( 'click', function( e ){
.... CODE HERE ....
Please tell me correct way of use "this" with ".on" inside ".each" function.
Thank you.

1. Short answer: There is no need for the each:
Do not use click inside of each... you do not need to with jQuery. It handlers collections automatically for most operations (including event handlers):
e.g.
$('.switch-cal').click(function(){
//$(this) is the calendar clicked
.... CODE HERE ....
2. Try a delegated handler:
It is often more convenient to use a single delegated event handler (attached to the nearest non-changing ancestor element, or document if none is handy). The best feature of delegated events is that they can work on elements that do not even exist yet! :)
The run-time overhead is quite low as they only need to apply the selector to the elements in the bubble-chain. Unless you are processing 50,000 mouse operations per second the speed difference is negligible, so for mouse events, don't be put of by ridiculous claims of inefficiency (like the down-voter's ranting below). The benefits usually out-way any extra overhead. Nobody clicks 50,000 times per second!:
e.g.
$(document).on('click', '.switch-cal', function(){
//$(this) is the calendar clicked
.... CODE HERE ....
Delegated events work by listening for events (in this case click) bubbling up the DOM to a non-changing ancestor element. It then applies the selector. It then calls the function on each matching element that caused the event.
The closer the non-changing element is to the elements in question the better, to reduce the number of tests required, but document is your fall-back if nothing else is convenient. Do not use body as it has problems relating to styling that means events may not fire.

#TrueBlueAussie had the correct answer. You should use a single delegated event.
$(document).on('click', '.switch-cal', function(){
//$(this) is the calendar clicked
.... CODE HERE ....
Still if you want to keep your "this" scope, you may
$('.switch-cal').each(function(){
$(this).on( 'click', function( e ){
.... CODE HERE ....
}.bind(this))
});

Related

jQuery best way to apply eventhandlers

I'm working on a grid with 20 columns and 100+ rows. Every column has an input field. Now I want to put eventHandlers on the input fields like change and keyup, focus, and much more. So there can be 20*100 events = 2000!
What is the best way to do this? I've read something about eventHandlers and memory problems.
This is how I think I should do it:
$("#grid input").each(function() {
$(this).keyup(function() {
//
});
$(this).change(function() {
//
});
});
Or is this the best way to do it?
$("#grid").keyup(function() {
//
});
You're looking for event delegation.
$("#grid").on("change", "input", function() {
// Handle the event here, `this` refers to the input where it occurred
});
That attaches one handler (on #grid), which jQuery then fires only if the event passed through an element matching the second selector. It calls the handler as though the handler were attached to the actual input. Even events like focus that don't natively bubble are supported through jQuery's mechanations.
More in the on documentation.
I'd suggest using Event Delegation, like so:
$("#grid").on("keyup", "input", function() {
...
});
Rather than adding a listener to each and every input, you're only adding one to #grid.
As per this great answer: What is DOM event delegation?
Another benefit to event delegation is that the total memory footprint used by event listeners goes down (since the number of event bindings go down). It may not make much of a difference to small pages that unload often (i.e. user's navigate to different pages often). But for long-lived applications it can be significant.
There are some really difficult-to-track-down situations when elements removed from the DOM still claim memory (i.e. they leak), and often this leaked memory is tied to an event binding. With event delegation you're free to destroy child elements without risk of forgetting to "unbind" their event listeners (since the listener is on the ancestor). These types of memory leaks can then be contained (if not eliminated, which is freaking hard to do sometimes. IE I'm looking at you).

how to attach keyup event from within jQuery plugin

I have a jQuery plugin, and inside of it I have an init function. Inside of this init-function I attach some events:
(function ( $ ) {
$.fn.gallery = function(options) {
var init = function(self) {
var main += '<input id="gallery-search">';
//click event for the filter checkboxes
$("body").on('click', self.selector+" .gallery-filter-checkbox",function(event) {
self.data(filter( self ));
});
//capture input in the search box
$('#gallery-search').keyup(function(){
console.log('test');
});
self.html(output);
}
}( jQuery ));
}
The first one works just fine, but the second one doesn't work at all. I have tested it outside of the plugin scope and it works just fine so there is no syntax error, but probably an error in the way I try and attach the event?
Since #gallery-search is created dynamically, you can use delegated event handler:
$(document).on('keyup', '#gallery-search', function() { ... });
If self represents static HTML element at page, you can use a little better (for performance) version:
self.on('keyup', '#gallery-search', function() { ... });
Or you can place event handler in code after element's insertion, if HTML will not be modified later:
self.html(output);
$('#gallery-search').keyup(function()
{
console.log('test');
});
keyup() is a shortcut for bind('keyup',callback) which will register some event handler on the elements that are already present in the DOM (not necessarily rendered). It won't work if the element is not present in DOM when it's defined. To do that you need to use delegate() which will attach some handler on an element which is currently available or in might be available in future.
From jQuery 1.7 onwards, it's recommended to use on() as it combines the functionality of bind / delegate / live.
on() can be used in two ways
$(selector).on('event',callback);
$(parentSelector).on('event','someChildSelector', callback);
First one is direct handler and second one is called delegated handler.
If you're using first one to register event handlers, you've to make sure that element is present in the DOM at the time of registering just like bind(). So if you're adding new elements, you have to attach that handler again.
If you're using the second way, you don't have to worry about registering the handler again
$(document).on('click','.row',callback);
As document is always available, you callback will be registered as click handler for all the existing rows and any new row that you might add in the future.
I strongly recommend you read the Direct and delegated events section here. They even explain about the performance benefits.
Now that you know how it works, you can fix it using on() either as a direct handler or as a delegated handler.
EDIT : It's better to use closest static parent than document/body when using on() to register delegated handlers. Thanks to Regent for suggesting that :)
$('closestStaticParent').on('keyup','#gallery-search',function(){
console.log('test');
});

Javascript Delegation Best Practice [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I am developing/have developed a few systems now, I have come across a querk in my programming, I seem to delegation all my JQuery functions to the document like so:
$(document).on('click', '.modal-editor', function () {
//code
});
$(document).on('click', '.another-class', function () {
//code
});
$(document).on('click', '#another-id', function () {
//code
});
Is there anything wrong with this?
UPDATE:
Ok so in essence there is nothing wrong with this until:
The applications reaches a scale where delegation needs to be more localised to prevent slowing down UI. "Every event that takes place needs to run every selector against every element between the e.target and the document"
Delegation like this will increase the chances of unexpected behaviour like propagation if nested functions are used.
Referring back to my question (best practice), best practice would be to delegate the event to the closest static element as possible to attempt to avoid the above.
"Is there anything wrong with this?"
Yes. Every event that takes place needs to run every selector against every element between the e.target and the document. That's unnecessarily expensive.
Basically like this simplified pseudo code:
// start on the e.target
var node = e.target;
// For the target and all its ancestors until the bound element...
while (node && node !== this) {
// ...test the current node against every click selector it was given
for (var i = 0; i < click_selectors.length; i++) {
// If a selector matches...
if ($(node).is(selector)) {
// ...run the handler for that selector
}
}
node = node.parentNode;
}
Also, if your code or some other code you loaded binds a handler between the e.target and the document, and calls e.stopPropagation(), the event will never reach the document, and so yours will not fire.
It's much better to keep your delegation as close to the intended element(s) as possible. And use it primarily for dynamically loaded elements because of the extra overhead it adds, especially in the case of events that fire rapidly.
Yes. In itself there isn't, nto really, but delegating all events from the document (root of the DOM) does meant that all click events, including those you're not interested in will be handled at least partially:
$(document).on('click', '$another-id', function(){});
is a particularly bad idea, in that respect: you're after a single element, but if I click anywhere in the body, jQ will do something like:
if (event.target.id == 'another-id')
{
return callback.call(event.target, $(event));//where callback is the anonymous function you passed to on
}
return event;
So all click events result in a function call. This can slow your UI down.
By no means should you stop delegating events, but bind the listeners wisely. If all the DOM elements you want to use are contained within the #container div, for example, then bind your listeners there. If you want to handle navigation events, bind the listener to the node that contains all the navigation elements your after
Add to that that, if you cancel an event, but failed to stopPropagation, the event will still end up invoking all your other listeners that might be queued. Returnign false can also cause trouble, seeing as, in jQ, return false is translated to e.preventDefault(); e.stopPropagation(). So be careful when dealing with nested elements, if you want to handle a click on links in your page, but also on elements like <a class='navigation'>, both handlers might be called, depending on the selectors used:
$(document).on('click', 'a', function(){});//is called for all links
$(document).on('click', 'a.navigation', function(){});//is called for navigation
Which will be invoked first? Which would you want to use in a given situation? There's room for error here, that shouldn't be there:
$('#navContainer').on('click', 'a.navigation', function(){});//is called for all links
At least makes things a tad safer, clearer and lighter, too.
If you want to delegate an event, using an ID selector, and the element already exists in the DOM, don't delegate:
$('#another-id').on('click', function(){});
is shorter, and will likely even be faster anyway
Delegation is great when you want a particular event to apply to multiple elements, without each element having its own event handler. In the instance above this is likely for the first two methods.
Where there will only ever be one element with that matches the criteria in theory it might have a performance implication, depending on the size of your document, due to every event having to be tested against the handler filter to see if it matches.
Also, if every method is added as a delegate, and you are frequently loading and uploading sections of the page those events are going to hang around longer than the elements on the page that they belong to.
You can delegate your handlers to something other than the document element, such as a surrounding div or something instead perhaps in this kind of scenario. Or use event namespacing to make sure events aren't being added multiple times.
There is nothing wrong with your code,
$(document) is not necessary to be place always in event delegation, that selector refers to the closest parent element to which your elements are added dynamically:
Syntax:
$(closest parent selector).on('event','target selector', function(){
});
You can also use document.body
$(document.body).on('event','target selecor',function(){
});
Side Note: For existing DOM elements you can use normal click event.
$('selector').click(function(){
});
OR
$('selector').on('click',function(){
});
For more information refer to .on() API documentation.

Use jquery inside or outside document ready

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.

Native addEventListener with selector like .on() in jQuery

Does anyone know how jQuery's .on() method can be implemented in native JS? The addEventListener method does not take a child/selector element as a way to filter, and I don't think I have the proper bubbling/capturing knowledge to completely understand what is happening in there. I did consult the source in event.js, where it appears that eventually addEventListener does get used just as it normally does, but I'm not sure I quite grok the source.
If the native method does not provide a mechanism for taking advantage of bubbling and capturing, then does the jQuery .on() function really even have any benefit, or does it just make it look that way? I was under the impression that
.on('parent', '.child', fn(){});
is more efficient than attaching an event to all children individually, but from my interpretation of the source, it's difficult to tell if jQuery is somehow managing this in a way to leads to performance improvement, or if it's just for readability.
Is there a standard methodology for implementing events on a parent that take advantage of bubbling/capture phases for it's child elements, rather than having to attach an event to each individual child?
To perform event delegation natively:
parent.addEventListener('click', function(e) {
if(e.target.classList.contains('myclass')) {
// this code will be executed only when elements with class
// 'myclass' are clicked on
}
});
The efficiency you are referring to has to do with how many event handlers you add. Imagine a table with 100 rows. It is much more efficient to attach a single event handler to the table element then 'delegate' to each row than attach 100 event handlers, 1 to each row.
The reason event delegation works is because a click event actually fires on both the child and the parent (because you're clicking over a region within the parent). The above code snippet fires on the parent's click event, but only executes when the condition returns true for the event target, thus simulating a directly attached event handler.
Bubbling/capturing is a related issue, but you only need to worry about it if the order of multiple event handlers firing matters. I recommend reading further on event order if you are interested in understanding bubbling vs capturing.
The most common benefit of event delegation is that it handles new elements that are added to the DOM after the event handler is attached. Take the above example of a table of 100 rows with click handlers. If we use direct event handler attachment (100 event handlers), then new rows that are added will need event handlers added manually. If we use delegated events, then new rows will automatically 'have' the event handler, because it's technically been added to the parent which will pick up all future events. Read What is DOM Event Delegation, as Felix Kling suggested, for more information.
Adding to the accepted answer: since often the actual event target will be nested within the element you want to bind the listener to it would be better to query the parents (Element.closest() includes the element itself). Also this works with complex CSS selectors instead of a single class only.
<button><span>button with</span><span>multiple click targets</span></button>
function addListener(el, type, callbackFn, selector) {
el.addEventListener(type, e => {
const target = e.target.closest(selector);
if (target) callbackFn.call(target, e);
}, true);
}
addListener(document, "click", e => console.log("clickediclick"), "button");
The answer by #Raine Revere, while concise, does not handle all cases. For example, if .child element contains children of its own, then click on the grandchildren will not trigger the handler. Also, jQuery sets the this execution context to the matched element.
The following snippet handles it correctly.
function on(event, elem, selector, handler) {
elem.addEventListener(event, ev => {
const target = ev.target.closest(selector);
if (target) {
handler.apply(target, arguments)
}
})
}
on('click', document.querySelector('.parent'), '.child', function () { console.log('Clicked ' + this.tagName);});
<div class="parent">
<button class="child">Click Me <i class="icon icon-something">→</i></button>
</div>

Categories