This technique is also useful for making the click outside of the modal dialogs close the dialog. You look for click events on the document and then use event.stopPropagation() on the dialog to prevent the dialog from closing, what will happen is the event starts at the top element under the cursor and then will bubble that event up to each parent until the event.stopPropagation() is called or it reaches the document root. The best way to use this in a framework, is to have a component that contains the dialog and add/removes the event from the document root and also applies the event.stopPropagation() to a click event on the dialog. This results in not having to track anything globally or on parent components. Just thought I’d share an example of how this is useful in a project.
@nagyszabolcs94513 ай бұрын
Going into the video, I actually thought he would mention this lol. Thanks for mentioning it here!
@ekchills69483 ай бұрын
I've become more confident in web development just watching your videos kyle
@reactoranime3 ай бұрын
For every inner child elements you might need pointer-events:none; otherwise you may think you clicking inside .box but event.target is childnode like href..
@PimiTree3 ай бұрын
Your right, this is the reason wht do event delegation almost usless
@kamoroso94Ай бұрын
The solution to that is checking if event.target matches ".box" or if event.target.closest(".box") exists (if you use parent=document) or parent.contains(event.target.closest(".box")) (otherwise).
@davi485963 ай бұрын
5:01 Event Delegation timestamp
@psycotrompus3 ай бұрын
My brain was flooded with different use cases after watching this. Great stuff!🤟
@dgdev693 ай бұрын
Thanks Kyle. I have been following you part of my software development journey past 2.5 years . You are always concise well structured.
@seanwilliams88333 ай бұрын
OMG this is so timely. I have a an employee directory and when I click on an employee card, a modal pops up. However, when I load new employee cards with AJAX, the modal wouldn't work. Now I know why!!! Forever indebted for this video! Spoke too soon - didn't work in my application for some reason.
@leo8483 ай бұрын
You should really try out a framework like Svelte or Vue, makes something like this completely impossible
@davidlacroix98373 ай бұрын
lol
@shgysk8zer03 ай бұрын
I rarely use event delegation. While i know this is just a quick video on the concept, it does mask the problems and complexity and trade-offs. Suppose you have a button with an SVG or basically any child. There are different phases of an event and how it propagates, but the important thing here is that it could be the child element dispatching the click event, not the button itself. In some cases, it may be better to use `e.target.closest()` that'd match either the element or any parent element... But that just ends up being a trade-off between CPU usage and memory. You're saving a little bit of memory but adding extra computation time to the handler of every click event, even if it isn't the intended target. Personally, i prefer to generally avoid event delegation except when you want a whole bunch of children of a specific container to all call a certain function. Otherwise, I'd target just the desired elements or use a MutationObserver.
@dkznikolaj70133 ай бұрын
How much longer are we talking? Developers have a bad habit of looking at something that takes 200 milliseconds extra and then decide its an unreasonable tradeoff, in places where no one except for a robot would be able to notice it. Obviously with loops this is different, but even then you need some pretty big loops for it to matter to a human
@shgysk8zer03 ай бұрын
@@dkznikolaj7013 time varies, obviously. Any single instance will take far longer on lesser hardware, especially if there's a CPU intensive task running. Also could be affected by how deeply nested the target element is. But sometimes more importantly, they'll run for every one of those events for the lifetime of the document (or until removed). You also can't remove the event listeners without using something like a `map` because it won't be the callback actually used in the listener, even if you're using a named function. Where do you get the idea that 200ms is short? Ever tried scrolling on a page with such a delay in a scroll listener? Or trying to fill out a form with such an input listener. Heck, even just a click would noticeably feel sluggish. And I'd give you a frame rate of 5! That, Vs having listeners on the target elements themselves, taking up only a little RAM. Even if we're only talking about 1ms here, it's still a trade-off, and one where the CPU usage is wasteful and can really accumulate over time. Plus, computation means power consumption, but RAM does not. So which is the better approach is a trade-off, and which is better depends on various factors.
@adambickford87203 ай бұрын
@@dkznikolaj7013 It IS in a loop, the event loop! And its throwing a mind boggling number of events. 200ms is an eternity in UI
@maelstrom573 ай бұрын
Imagine being the kinda dev that considers CPU usage when writing JS.
@SebastianZartner3 ай бұрын
I was also going to note this. If you are oing with `e.target.closest()`, you should also pass the matched element to the callback function to avoid having to call it twice. And regarding CPU usage, this is really neglectable. Even on slow systems, `.closest()` is not even taking a microsecond to execute in most cases. You may try it yourself by wrapping it into `const s = performance.now()` and `console.log(performance.now() - s)`.
@crim-son3 ай бұрын
Nice tutorial, but you're screwed if the target element has nested children, you then now have to be checking with ". closest(...), .querySelector(...)" before you can carry out your actions, this works if the element you're targeting doesn't have much nestedchildren
@JohnDoe-rk7ex3 ай бұрын
Or using "includes" on the parent
@lesalmin3 ай бұрын
Just by watching this one tutorial video and learning everything done in it, you can already create the most important tasks of a beginner javascript coder.
@EricFressange3 ай бұрын
Thank you for this example. I was looking for how to do it in vanilla JS. It was very easy to do delegate with jQuery. I see just a little issue with your code : the event won't trigger if there is another element inside. You need to adjust your code to match also with the parents
@Georgggg3 ай бұрын
You can safely ditch this practice, it doesn't add any value, but add indirection with DOM events, which is painful to debug. When your events stop working due to some element inside boxes, or stopPropagation somewhere.. good luck.
@isaidstream45473 ай бұрын
Not knowing what is a Event Delegation is the reason why I Ieft javascript 5 years ago, and all related to programming. Now i can do a proper To-do app and more... This css property has help me too: pointer-event: none
@AllinOne-ly4xi3 ай бұрын
Thank you sir i am stuck on this problem for many days now i understand how it works 👍👍
@yomajo3 ай бұрын
Python "web" flask dev, working on second project. I used to attach event listeners on newly added elements. Will try this method next time situation presents itself. Thank you!
@user-zj6iz1rl8dАй бұрын
waow ik its simple but waow this dude is amazing at js, i hope he has a js playlist
@S_sword3 ай бұрын
Using the event listeners at the whole document may not be the best idea.... Instead, wrap the elements and add the listener to the parent... If you added it to the document, it will call the listener every time which slows the page down,however, adding the mousemove listener to the document might be a good idea if you need to use the cursor's position for something..
@MrRagday3 ай бұрын
Thank you. From this example I understand that component approach >>> event delegation ;)
@luquillasnano3 ай бұрын
This is such a neat explanation. Thanks, hopefully I'm gonna make use of it right now.
@ModSlash3 ай бұрын
Your videos are awesome, I often point them out to beginners touching on topics that they could use some help to get to grips with. Well done, and keep them coming. For this specific one, I know where you are coming from but this technically is not delegation :) In an older school of programmers that I am part of, this is iteration :)
@isti19863 ай бұрын
It works as long as you dont have complex objects that you want to delegate your events to. In real live applications this unfortunately tend not to work, because e.target will be one of the internal elements within the 'box'.
@moofymoo3 ай бұрын
e.target.closest('.box') will find box element from box element or it's children. null if not clicked in box.
@isti19863 ай бұрын
@@moofymoo That's a good answer but not perfect. If you have other elements in your box, and any calling the event.stopPropagation() method, then your little event listener will never fire. Good luck figuring out in a larger code base when sometimes clicking on the box works and somethimes it doesnt. I think this is an error prone solution, I personally would not use it.
@reactoranime3 ай бұрын
pointer-events:none
@TheSpr0gz3 ай бұрын
Good introductory video on event delegation. It would have been good to cover what happens if the .box has descendents of its own (img, span, etc.) and how to use e.target.closest('.box') to match the .box you want to toggle the class on because this happens a lot in the real world. Head up, at 3:36 (and another place somewhere in the video) you say, "taking an event that happened at a parent such as our document and we're delegating it down to one of the children". This is the wrong way around. You are actually delegating the handling of those child events to a parent you're not delegating anything to the children. Also, technically of course you're delegating a descendent element's events to an ancestor, not necessarily a child / parent relationship.
@yashkhatri20993 ай бұрын
8:33 A simple correction: Actually we don't need parent argument. We can just pass '.grid .box' in parameter to select boxes only inside the grid element.
@fiatlux8053 ай бұрын
I feel like a tiny flower learning to code (self taught) and your videos are like tidal waves lol. Great content, you deserve every like and every sub.
@lann1sterroy3 ай бұрын
I was studying event delegations 1-2 hour ago and Kyle dropped this video. Am i dreaming or something :))
@MatthewNicholsDevGuy3 ай бұрын
Thanks, I didn't know about the .matches method. Good content as always.
@Vptechvelly3 ай бұрын
Kyle best tutor ever.🎉
@kazmuz59162 ай бұрын
Super helpful, thanks!
@josuedelossantos94423 ай бұрын
Thank you, though I haven't used vanilla JS for a while.
@sinvalfelisberto3 ай бұрын
Perfect! Thank you!
@moritzfinke4518Ай бұрын
Thank you for the video, it was really helpful.
@luisenriquecaleroanchelia63693 ай бұрын
Excelente video , saludos desde Perú 💪
@abhishek_236853 ай бұрын
One suggestion - You can move your camera a further back and then zoom in. That will give a natural camera view. Currently it feels like your face is directly on the screen, like you are trying get in to the camera as close as possible, which makes it bit weird to look at.
@JoyfulDarts-fy4gc3 ай бұрын
Great explanation, as always on this channel
@olamidehamed3 ай бұрын
My best tutor 🎉
@chinmayghule82723 ай бұрын
Nice to see a noob level video after a long time. I started from those same videos. I feel like currently the channel lacks content for junior devs who're a little bit experienced. For example, if you're trying to create a whatsapp clone using MERN, then how are you going to design the database? What different collections will you use? What I mean is that things like planning a project and knowing how to create an architecture is something that can teach us a lot. I hope I get to see such a project done in one of these videos.
@darkwater2343 ай бұрын
This is great! Well done!
@anothermouth70773 ай бұрын
Such a neat technique!
@DerPipo3 ай бұрын
This looks and feels like a good old web dev simplified video. ❤ Was this left over?
@wntiv3 ай бұрын
probly want to use .closest() instead of .matches() if your boxes have any children, so that clicking any of the children within the box will still activate the box itself (if that's what you want)
@StephanHaewss3 ай бұрын
Very nicely explained!
@frankoppongkonadu66763 ай бұрын
Well explained, Thank you boss
@tarek-n3g3 ай бұрын
really helpful thanks bro !!
@alirezajafari53823 ай бұрын
this is amazing thank you so much❤❤
@TamsirRilley-x3b9 күн бұрын
dude ur OP. thank you
@HeyNoah3 ай бұрын
Fantastic! Thank you!
@zool2019753 ай бұрын
Only problem here is when you click on any child elements inside the boxes the expected behaviour will not trigger. Use *e.target.closest(".box")* to figure out if you clicked on any of the boxes and/or its child elements. *e.target.closest(".box")* will iterate trough itself and its parents till the query is met. if not so then it results with a NULL.
@kierrboy86133 ай бұрын
This is good
@todd.mitchell3 ай бұрын
Brilliant now I don't have to remove/add listeners every time my virtual scroll renders
@MrRagday3 ай бұрын
Why your components dont do it for you?
@todd.mitchell3 ай бұрын
@MrRagday I'm not using a component just plain vanilla JS.
@MrRagday3 ай бұрын
@@todd.mitchell component is an approach, not an entity.
@todd.mitchell3 ай бұрын
@MrRagday I meant I'm not using a component of a major framework. I wrote my own virtual scroll component using intersection observers. Unless you delegate the events you have to remove/add each one when re-rendering the scroll box. After watching this video I implemented this approach and love how elegant it is. Another commenter warned about using CSS class for logic. Valid concern. I could use the dataset but I understand navigating the DOM by class is more efficient than parsing the dataset; that ship has already sailed in my code. Plus I avoid the many remove/adds, speeding up the virtual scroll. I'm just going to document the design decision.
@KyrylWins3 ай бұрын
1.6 mil soon, keep going!
@karimkrimou3993 ай бұрын
Great video kyle bro
@devwithbrian15343 ай бұрын
Thanks Kyle
@AlThePal783 ай бұрын
Yo thius video is amazing I need this type of learning :)
@cyberprompt3 ай бұрын
Knocked it out the box, Kyle.
@MarcosRVNeves2 ай бұрын
Couldn't I just use '.parent > .child' or '#parent > .child' if they are unique, instead of creating a fourth parameter in the event listener to pass the parent element? If you're adding an event listener to a very specific element or parent, you should consider using an id anyway. Additionally, you could use ':not(.parent) > .child' to select all child elements that are not part of the parent. That being said, good video, well explained.
@abundiko3 ай бұрын
I've always rerun the loop and added all even listeners over again in the past. Thanks for the helpful content!
@ChrisAthanas3 ай бұрын
Both ways have advantages and trade offs I can see cases where one or the other approach is helpful
@StiekemeHenk3 ай бұрын
The original approach is better. Just add the listener you need when you create the item. Instead of running x listeners and x checks. One listener, no checks needed. You can easily remove code duplication with a function.
@froozenalex3 ай бұрын
Fr, the video has a pretty bad example of the problem
@Pluvo2for13 ай бұрын
This reminds me of event bubbling. Could we have a video on that please?
@freerider34343 ай бұрын
Cool stuff, thx! Why bobblehead?
@CarlMiller-p5o3 ай бұрын
🤯 - mind blowing
@iamcookbook3 ай бұрын
if you have child elements inside the boxes the target will be those elements instead of the main box so you might need to use something like if (!e.target.closest(‘.box’)) return; instead of .match(), this will escape if you did not click inside of a box
@davadh3 ай бұрын
Super useful
@RomanWeismanАй бұрын
Thanks.
@strategistaow35203 ай бұрын
People say that you shouldn't make lots of event listeners It's better to make just one listener for document and listen for e.target
@dynamohack3 ай бұрын
better when create element with onclick attribute and pass js function in it with dynamic arguments and it dont even require to add a single event listener
@d.sherman85632 ай бұрын
What do you think setting the onclick attribute does?
@dynamohack2 ай бұрын
@@d.sherman8563 onclick is a attribute which runs js written in onclick attribute either code or some functions in js which present on the document
@loic.bertrand3 ай бұрын
If we had child elements inside the boxes, wouldn't we need to use if (e.target.closest(selector) ) { ... } ? So it bubbles to the parent if we click a child element
@reddevils74243 ай бұрын
is there a more sufficient way to achieve the same thing with jquery?
@GonzaloMassa3 ай бұрын
jQuery includes that behavior out of the box. Just pass an extra argument with the selector to match, like in this example would be $(document).on('click', ((your event handler)), '.box');
@starjuda3633 ай бұрын
thanks kyle
@lilbahr3 ай бұрын
I don't mean to be... uh mean or anything. Is that shake of your head a usual thing or is something the matter? I just realized, is ... that a thing? Maybe I am just noticing something nonissue. Maybe is just a way for the memory to work with explaining the issue. :) I love your videos. I just got to thinking...
@bytebytego-13 ай бұрын
Why is event bubbling is not seen here , as when we click on .box means we click the document too..
@irlshrek3 ай бұрын
Good question! It is actually happening. you are clicking on the element and then it bubbles up to the document where hes handling the bubbled-up event. That event still has as it's target the element that 'captured' the event before bubbling it up to document. This is actually a tiny bit inefficient but it works.
@saefulrahman34783 ай бұрын
thanks bro, 👍
@petrmezera13963 ай бұрын
Is better use eventListener on "document" or on a "window"?
@feesectiontsdps63913 ай бұрын
Superb
@StellarWeb0083 ай бұрын
Its weird to debug because if you have an element & want to check it's event listeners from the Elements tab, you can't do that because you would have to check events of each & every parent as you don't know which one might be delegating the event to it
@JE-cp6zv3 ай бұрын
What’s the best way to reach you , I need help with mirroring a website that has javascript I’m able to get it downloaded locally but it won’t run the JavaScript. I would greatly appreciate any assistance. Thanks 🙂
@tweetoryt3 ай бұрын
Maybe add one example of how to handle the classic "icon on a button" scenario
@keremardicli40133 ай бұрын
Next video: Event propagation in 10 mins
@estebanfelipe39803 ай бұрын
I don't think the "matches" method you used would work if, for example, you had a paragraph inside each box and your clicked such paragraph, because the target would've been the paragraph, not the box, so the event wouldn't have triggered.
@TheSpr0gz3 ай бұрын
I was hoping Kyle would cover this case as it happens a lot in the real world. For that situation you would use e.target.closest('.box') which searches the DOM tree from e.target (inclusive) up the DOM tree and returns the first .box element it finds, or null if the clicked element wasn't a descendent of a .box element.
@Niksorus3 ай бұрын
Typically, that's where I'd make a separate component. A Web Component maybe if you really want no JS framework.
@MrRagday3 ай бұрын
Just simple function/class would be enough
@chudchadanstud3 ай бұрын
Doesn't your method break encapsulation? Also your state is now scattered everywhere. If I make a change I'm now not aware of the effects.
@evilmanua3 ай бұрын
mate, can you continue the event listeners topic, specifically removing event listeners?
@sergewert25603 ай бұрын
What if we replace parent parameter with right selector?
@evalaviniabucur17893 ай бұрын
You can do that: sending a selector for the parent. Then you'd need to do something like: document.querySelector(parentSelector).addEventListener(type, e => { if (!e.target.matches(selector)) return callback(e) })
@OCEMTechZone3 ай бұрын
Great
@oumardicko55933 ай бұрын
Kinda nice, Not sure if this is gonna help our new front-end framework only developers xD
@nhutdev3 ай бұрын
Why can't I find the remove class "clicked" event?
@zeroc72533 ай бұрын
If box is nested with other elements, how do you get the desired click element?
@borstenpinsel3 ай бұрын
The element that was clicked is always known. Nested or not. But every element above it was also clicked and this is where the delegation (or often called bubbling up) comes into play. If you click any element on a website, you also clicked the body. That's rather obvious. But you also clicked every element of the nest as well. And every element gives this event to its parent. Unless you prevent it from doing so.
@zeroc72533 ай бұрын
@@borstenpinsel for example: Document .box{ display: flex; gap: 20px; } .tag{ width: 200px; height: 200px; background: skyblue; } .one{ width: 150px; height: 150px; background: orangered; } .two { width: 100px; height: 100px; background-color: #fff; color: #000; } .span { display: inline-flex; padding: 10px; border: 1px solid #000; } Tag1 Tag2 const box = document.querySelector(".box") box.addEventListener("click", event => { console.log("->> ", event.target) if(event.target.matches(".tag")){ console.log(event.target) } }) I mean, in a lot of development scenarios where elements are nested, like in this example, and you want to get the data-tag, you can't get it by clicking on one, two, and span. So you're looking for the parent element to match。
@zeroc72533 ай бұрын
@@borstenpinselfor example: Document .box { display: flex; gap: 20px; } .tag { width: 200px; height: 200px; background: skyblue; } .one { width: 150px; height: 150px; background: orangered; } .two { width: 100px; height: 100px; background-color: #fff; color: #000; } .span { display: inline-flex; padding: 10px; border: 1px solid #000; } Tag1 Tag2 const box = document.querySelector(".box") box.addEventListener("click", event => { console.log("->> ", event.target) if (event.target.matches(".tag")) { console.log(event.target) } }) delegationEvent(box, (event, dataValue)=>{ console.log("find: ", dataValue) }, "div", "data-tag") function delegationEvent(proxy, handleFunc, matchEl, attrName) { proxy.addEventListener("click", (event) => { let target = event.target; while (target !== proxy) { if (matchEl && attrName) { let attrVal = matchDOM(target, matchEl, attrName) if (attrVal !== null) { return handleFunc(target, attrVal) } } target = target.parentNode } }) } function matchDOM(target, matchEl, attrName) { if (matchEl && attrName) { let attrVal = target.getAttribute(attrName) if (target.nodeName.toUpperCase() === matchEl.toUpperCase() && attrVal != null) { return attrVal } } return null } I mean, in a lot of development scenarios where elements are nested, like in this example, and you want to get the data-tag, you can't get it by clicking on one, two, and span. So you're looking for the parent element to match. I wrote a function that is more in line with the development scenario, so that whatever element is clicked under the proxy will get the desired object.
@BurritoBrooks3 ай бұрын
🔥🔥🔥
@veenitchauhan61313 ай бұрын
Exelent
@uplink-on-yt3 ай бұрын
jQuery did that selector trick back when it was cool.
@paxdriver3 ай бұрын
Correction: "linearly" adds more complexity, not "exponentially" Sorry... It's not you, it's me lol
@aram56423 ай бұрын
The more serious problem that delegation solves is when you remove boxes. It frees you from removing individual event listeners for which a reference you'd have to store
@torquebiker99593 ай бұрын
When you remove boxes from the DOM, their event listeners are also removed, no?
@TheGryphon143 ай бұрын
no, garbage collector handles that for you
@TheSpr0gz3 ай бұрын
@@torquebiker9959 Garbage collection will clean up, provided there are no other references to the event listener function or the DOM element.
@MrRagday3 ай бұрын
@@torquebiker9959 no. It will be removed if nothing will point to box from global/running code. Not only dom but some vaiables/properties too.
@hovhadovahАй бұрын
"Event delegation is essentially taking an event at a top level and passing it down to other elements" This is incorrect. Event delegation is just a fancy term for setting an event listener on a parent element rather than on each individual child. It doesn't change how the event is captured or how it bubbles. It simply delegates the responsibility of event handling to a common ancestor.
@ChandrashekarCN3 ай бұрын
💖💖💖💖
@studytime80783 ай бұрын
Can’t we just set attribute (class) in box , to execute event listener
@NaifIT113 ай бұрын
I love DOM and native thing i dont know i hate js frameworks😂
@MrRagday3 ай бұрын
Dont use frameworks . Use react-js instead
@moofymoo3 ай бұрын
Hello jQuery development patterns! nice to see how upset some modern framework people are in comment section :D
@MrRagday3 ай бұрын
Modern frameworks also can use this pattern
@abhishek_4223 ай бұрын
why he shakes his head so much, I am an indian I we are known for the head gestures but he is on next level 😂
@MrLinuxFreak3 ай бұрын
Or an more easy way is to just bind the function on box creation instead of listening and searching the whole doom every click..
@injSrc3 ай бұрын
In this scenario adding the event listener right when it was created would have been a sensible solution too
@evalaviniabucur17893 ай бұрын
I actually think it would still have been a horrible idea, as you could have a thousand such boxes and then a thousand such event listeners would have been added - which seems far from ideal.
@MrRagday3 ай бұрын
@@evalaviniabucur1789 actually its great idea. If you also encapsulate box and listener in some function or clas you'll get so-called component. Which used in 99% of web apps. And only if your users really create thousands of boxes and they are upset about perfomance drop you'll think how to fix it with smth like event delegation.
@TomasMisura3 ай бұрын
i use event delegation as much as possible. But i do not overuse it ...
@whitecreek3 ай бұрын
Niiiiiceee
@skeleton_craftGaming3 ай бұрын
Jquery solved this a long time ago, there is no reason for using raw JavaScript to do this in a web setting