DEV Community

Discussion on: When to actually use preventDefault(), stopPropagation(), and setTimeout() in Javascript event listeners

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Love the article, indeed it's one of my most common headaches although I "think" or "thought" I understood it lol.

I would ask this question about defaultPrevented though - frequently you are using an library and it's not bothering with that defaultPrevented stuff. So stopPropagation appears to be the only way to handle it.

e.g. React Material UI expansion card header with a button on it. UX is right, user understands the button. If I don't call stopPropagation on the click event of the button then the expansion card will collapse or expand - preventDefault seems to be not enough. Indeed I want the whole above chain to be forgotten, which is why I'm doing it I guess... Do you have an insight I could apply in this case?

Collapse
 
cubiclesocial profile image
cubiclesocial • Edited

Could you post a Fiddle/CodePen example that demonstrates this? It sounds like a tricky problem. As a user of a third-party library, you have little control over the library itself, including its bugs, other than opening an issue on an issue tracker. You could also roll your own expansion card header widget that doesn't have the buggy behavior (e.g. maybe it doesn't collapse on click when the e.target is an 'a', 'input', or 'button' element or has an attribute like data-collapse-on-click="false").

preventDefault() should only be used to tell the browser to not do anything with the button. If the button is in a form element, then that might be enough to trigger the browser to send a form. You would call preventDefault() to stop the browser from sending the form when the button is clicked, not to prevent side-effects upstream in the DOM. As mentioned in the article, preventDefault() can also be used to stop some later events from firing without stopping the event from being sent along the chain so there might be something you could do here to prevent the 'click' event from firing. But as I said, preventDefault() is a suggestion to the browser - it can choose to ignore it and do whatever it wants (i.e. prevent means you want to prevent it but there's no guarantee). Since it is a mechanism for telling the browser to not do something, defaultPrevented shouldn't be the mechanism that widgets rely on.

stopPropagation() completely halts processing of the event along the chain. If there's something later in the event chain, usually in the bubbling phase, listening for an event to react to it, it won't receive it. It's still a judgement call as to what is "modal" and for what input devices is it modal for (keyboard, mouse, touch). Is the button modal? Is the whole card modal (compared to the document/page)? Will anything upstream of the card in the DOM ever need to hear the 'click' event to react to it?

stopPropagation() is generally overkill. The ideal quick-n-dirty solution to handling problematic/buggy widgets without having to dig into their source code would be a way to tell the browser to skip passing an event to specific DOM elements as it passes through our event handlers. For example, a dontPropagateTo(node, capture) function that accepts one or more DOM nodes and capturing/bubbling phase to skip during the processing of an event. That would allow for developers to say to the browser, "I know these particular DOM elements may have previously registered event handlers for the click event but they don't need to know about this particular click event and there might be something important further along the event propagation chain that could be listening for click events that I don't know about." Such functionality would mostly solve your problem without potentially breaking event handling for other listeners. The downside is that all listeners on those specified DOM nodes would not receive the event - not just the buggy one. Still, such a function could be very useful.

Rolling your own widget may actually be the "best" option. That way you have total control over the way events are handled. The downside is that it takes time to build a good, custom widget from the ground-up. However, you might be able to take the existing source code for the widget and slightly modify it to ignore processing certain event targets in its click handler. That way you don't have to call stopPropagation() in your click handler(s).