Avoid These Mistakes With Svelte Events

Joy of Code| 00:13:06|Mar 13, 2026
Chapters7
This chapter introduces how Swelt handles events, highlighting issues with manual event listeners and showing how to fix them using the on function from Swelt Events, along with a peek at the source code and event delegation basics.

Joy of Code’s guide shows why Svelte events should use delegation and how to fix manual listeners with the on function and bubbles option.

Summary

Joy of Code’s video with Dan (host) dives into how Svelte handles events, focusing on the pitfalls of manual event listeners and custom events. He demonstrates that attaching listeners to every button is memory-inefficient and explains event delegation by bubbling the event to a common ancestor. Dan then demonstrates the limitations of custom events (they don’t bubble by default) and how to enable bubbling with a bubbles: true option. He compares declarative events with manual listeners, showing how swelt’s on function can order event handling to maintain predictable propagation. The walkthrough includes practical code: using attachments for outside-click logic, replacing manual listeners with the on function, and returning cleanup functions. Finally, he pulls back the curtain on Swell’s source code, illustrating how event delegation is implemented in render, with the event delegation wiring to both container and document for robust handling. If you want to understand why event delegation matters in Svelte-like ecosystems and how to keep event handling predictable, this video is a concise, hands-on resource.

Key Takeaways

  • Using event delegation (one listener on a root element) avoids memory waste when you have multiple buttons or elements that would each need a listener.
  • Custom events don’t bubble by default; enabling bubbling with bubbles: true on the event makes them usable from parent elements.
  • The swelt on function returns a cleanup function and can simplify adding and removing event listeners, reducing boilerplate.
  • Manual event listeners can fire before declarative events, potentially breaking propagation order; using on to centralize handling fixes ordering issues.
  • Swel’s internal render logic delegates events to the container and document, ensuring events bubble correctly even when inner content stops propagation.

Who Is This For?

Front-end developers curious about Svelte-like event handling, especially those who work with custom events, event delegation, and keeping event listeners efficient. Great for developers moving from declarative events to manual listeners and wanting a mental model of Swelt’s internals.

Notable Quotes

"And this is how most JavaScript frameworks implement this."
Illustrates the standard approach of event delegation used by many frameworks.
"Custom events don't bubble."
Explains a core pitfall when using custom events without enabling bubbling.
"The on function from swelt events can simplify adding and taking care of this event listener."
Shows a practical alternative to manual listeners with built-in cleanup.
"This is actually the cool part. This is the event delegation part."
Leads into how Swell wires events to container and document for robust handling.

Questions This Video Answers

  • How does event delegation work in Svelte-like frameworks and why is it more memory efficient?
  • Why don't custom events bubble by default and how can you enable bubbling in Swell?
  • What does the swelt on function do and how does it simplify cleanup of event listeners?
  • How does Swell implement event delegation in render and what’s the role of container vs document listeners?
  • What are practical tips for avoiding race conditions between manual and declarative events in modern front-end frameworks?
Swell eventsevent delegationon function (Swell)custom events and bubblingmanual event listenersattachment (Swell)event propagation orderSwel source codememory efficiency in event handling
Full Transcript
Hey friends, today I want to talk to you about how swelt handles events and why is it important? Well, sometimes in case where you're using manual event listeners. All right, so in the first part I'm going to show you the issue when using custom event handlers and manual event listeners and how to fix it using the on function from swelt events. And in the second part, if you're interested, we're going to jump into the source code to understand how swelt uses event delegation so you have a better mental model how swelt works. All right, friends. And so here I have a simple button. So when we click on it, we're going to get the message click. So imagine if we had more than one button. It would be really memory inefficient if we had an event handler for every button. And then we would also need to take care of the cleanup. Well, most JavaScript frameworks don't attach directly event listeners on the elements themselves. They use something that's called event delegation. So let me show you that. So if we inspect this button, we go here and let's go to event listeners. We have to uncheck ancestors. So you're going to see if we click on this button, it has no event listeners. The event listener is actually on the root element. So if we go here, we're going to see here is the click event itself. And then using something that's called bubbling, we're going to bubble this event. When we click on this button up to the parent element itself. So here's how it looks like. So this is the bubbling phase. So when we click on the button, it's going to invoke its event handlers and all the event handlers on its ancestors. So instead of having a bunch of event listeners on multiple buttons, we can just use bubbling to bubble the event up and then we can see which button was clicked and perform the appropriate action. And this is how most JavaScript frameworks implement this. And you can read the SW documentation to know which type of events get delegated. And because we're using event delegation, we're going to run into some problems. Usually, you don't have to worry about this in swelt when you're using declarative events because swelt is going to sort them for you. But the problem arises when you're using manual event listeners if for whatever reason you have to use things like custom events or manual event listeners. All right, so let me show you the issues you're going to run into and how you can fix it. All right, friends. In the first example, we're going to look at custom events. So here I have this attachment called clicked outside. And if you don't know what attachments are, I'm going to link to a video I made. But basically, attachments are an upgraded version of swelt actions. And we can use them to attach behavior to any element. So here we get a reference to the node. And then we have this handler here. So we're checking, hey, if this isn't the same element that we clicked on, then we probably clicked outside. And then we can say note.patche event. And then we can fire off a new custom event called clicked outside. And then we can listen for this event. So here we're adding an event listener on the window click using this handler because we need to reference it here where it's remove event listener. So you need to take care of it. All right. So here is how it works. So to use attachment we can use the attach keyword and we can say clicked outside and now we can listen for this event. So we can say on clicked outside equals and now let's say console log and then we can say clicked outside. All right. So, let me save it and it's going to format it. All right. So, if we click on the button, you're going to see it's going to log click. But if we click outside, it's going to log clicked outside and everything works as expected. All right. So, what is the problem? Well, the problem is that custom events don't bubble. So, what if for example, we want to listen for oncllicked outside on the parent? Well, it's not going to work. Let me show you. So, let me just move this to a section. All right. Let's clear this. So, now you can see we can click. But if I clicked outside, nothing is happening. And this is a really easy fix. So we just need to go to the custom event and specify an option where we say bubbles and then we can say true. All right. So now this custom event is going to bubble. All right. So let's click on the button we get click. But now when you click outside, we get clicked outside. All right. So let me show you the on function from swelt. In this example, we're only going to use it to simplify adding and taking care of this event listener. So let's import it. We can import on from swel events and we can just take this function here. We can yink all of this code. All right. So we can use the on function. We can specify what we want to attach the event listener to. So in this case it's going to be window. The type of event is going to be click. And now we can add the event handler. All right. So we can now copy paste our code and this also returns a cleanup function. So it's way easier. So we can say off and then here where we have a cleanup function we can return this. All right. So let's see if it works. We're going to log click and we're going to log clicked outside. But of course this is redundant here. We can make this even simpler. So we can remove this and instead of defining con stop we can just return this function. As you can see this simplifies things quite a bit. And of course, this can also be inside of an effect, so you can just return it. All right, let's look at the next example. All right, we're back to factory settings. In this example, we're going to look at manual events. So, let's say, for example, that you need to create a manual event listener. So again, here we have this handler, which is just a console log that says manual click. And we're going to add this event listener to the node. And then we're also going to have this cleanup here. All right. So, let's go to the section and we're going to use an attachment. So, we're just going to use this event. Let's save this. And you're going to notice the first problem when we click on this button. All right. So, do you notice it? The first thing that ran is our manual event and then the declarative event ran, which might not be something that you want if the order is important to you. And let me show you another downside. So, let's say for example that we go here to this declarative event and we want to stop the propagation. So if you say event stop propagation, if you save this, let's click on this button, you're going to see it's not going to work. And that is because manual events are always going to fire first because this event here is bubbling up and then it doesn't work. And to fix this problem, it's really simple. So again, we're going to take this function and we're going to use on from swelt. And this is actually going to sort the events in the appropriate order. So let's just yink all of this code and then we can say on the node type of event click and we can just copy and paste our function and of course let's return it for cleanup. All right so let's clear this. So now when you click on the button you're going to see that it works as expected. The propagation is going to stop and this is not going to run and it's also going to fix the issue with the order. So if we remove stop propagation and save. Let's click on the button. You're going to see the first thing that's going to log is click. And then it's going to log manual click. And that's everything that you have to know. But if you're curious, in the next section, we're going to dive into the SWEL source code to understand how event delegation works and how the on function is implemented. All right. So this is the developer tools debugger. So let's look at the compile source code. So in this example, we have our component and nothing here is important besides maybe this button here where we're creating it and then we're saying delegated here. But this is actually a red herring. What selt actually does it just creates this property on an element with all of its events. And I don't know what it uses for basically for reference or something. I don't know. It looks something like this. So we have button and then it creates something like events and then it has this object basically with the events and that's basically it. But it doesn't use it for anything of course because it uses event delegation. The actual code that we're interested in is actually inside of render. So here where we're actually going to create the event handler and then inside of events where we handle event propagation when you click on a button for example or whatever. But what's also interesting here in the source code the only thing that's important here is this line where it says delegate click. So which type of events is going to delegate and turn into events. All right so let me show you for example if we go to render let me just restart this page. So we're going to hit the break point. All right. So now you can see we just defined this function and we're going to look at what it does. But we can just step through it because we're just creating this function. So here we have this function. We're invoking it event handler. We're creating an array from all the registered events. And if you remember this is this click that we see in here. So here it is delegate click. All right. So let's go here or actually it's the render. It always switches up on me. All right. So let's actually see what's going on. So this is really interesting. All right. So now it's going to loop through all of the events, but we only have click in this example. All right. So let's actually just step through it. So we can see that the event name is click. Then it looks at all the registered events, which is just click. And this really isn't important. So this is actually the cool part. This is the event delegation part. So it's going to loop over these nodes and it's going to add the events to this target. This is going to be the target element that we've seen before, but it's also going to add it to the document. As it says here, add event listener to both the container and the document. The container listener ensures we catch events from within in case the outer content stops propagation of the event. All right, so we can actually step through this code and we can see that we get counts here from listeners. It's going to be undefined. Let's just step through this really not important. All right, so here is the interesting part. So here we have count undefined. So we're actually going to add an event listener to our parent with the event name and this thing which is going to call handle event propagation. So we're going to look at this function when we click on the button and that's basically it. So now we can actually step for this code. We're going to see it's going to add the event listener on the parent and it's now going to loop again to the document. And that's basically it. So now it should be done. And the only thing that it does here is has this return. So you're going to see it also returns this entire cleanup for the events when we unmount the component. And what's also cool about this, you can see this is the entire component. So all of your components, etc. Your app is wrapped inside of a root effect. All right, but we can now actually press play because we're not interested anymore in the rendering. We're interesting in what happens when we press on click. So we can go to events and now we have this function. Remember, handle event propagation. Where was it? Here we go. Handle event propagation. So this is what happens when you click on the button. All right. So let's do this and it should trip the debugger. Awesome. So now we get the event passed in. So let's just step through it. We can see this really isn't important besides the current target. So we can just jump through this. All right. So here is the interesting part. So here we have this current target. So we can see that the current target, if it's harder to see here, we can actually see that is here this button. So that's the current target. And now it does this thing where it proxies the current target to the correct target. So it defines a property on this object. Yeah, it passes this event and then it creates current target or overrides it. I'm not sure. So it says configurable true and here is a getter. So it returns the current target which is going to be the button. All right, so let's step through this code and we can actually skip this because this is just for effects and etc. We don't really care about this. All right, so let's go here. So it's going to loop over all the targets parent element. So in this case, if we can't see it here, this is going to be the section. And then we're going to see now we have this delegated which is going to be from the current target the event symbol. So which event was it and then the event name. So if we step through it more we can actually see the delegated is going to be the onclick function. So let me actually show you. So now when we step through this code more now we're going to actually call this delegated function and this is a special method in JavaScript. So we can supply the this argument and then we can pass it arguments. So I can open the console here. And now when we step through this line, this should show us back into our script where the console log is. So if we step through it, you're going to see now we have click and now it's going to log click. And that's basically it. So that is how SWEL implements event delegation. But let me also show you one last cool thing. So remember our on function, how does that work? So I actually don't remember is this in events. Let's actually just look for it. Yeah, here it is. So here we have this on function. So we can pass an element type, a handler, and we also have some options. But as you can see here, we have this thing target handler which invokes this create event function and then it just returns a cleanup. And basically that's how simple that is. But let's actually see what create event is. So I think it's just below here. Let's search for it. Create event. All right, here it is. So you can see that create event accepts an event name, the element handler, and options. And the only thing it does it wraps it inside of this target handler. And by default if there is no options captured it's going to invoke handle event propagation but it's going to call it with this as being the element and the event. So that is how swelt ensures that it runs in the correct order. And then here below we have some shenanigans here but this is the important part. So here is where it adds the event listener. So it says dom add event listener the event name and the wrapped target handler. And that is basically what the on function is. All right friends. I hope that you learned something and I'll catch you in the next one.

Get daily recaps from
Joy of Code

AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.