Open rafaelcastrocouto opened 2 years ago
document.body.addEventListener('click', (event) => {
const tr = event.target.closest('tr');
if (!tr) return;
console.log(tr.textContent);
});
~~but it has the same issue as the one below: what if, for eg, the callback is from a lib and it needs the event.target object to point to the tr element that was clicked?~~
that works great! @jakearchibald found this one here https://www.tech-wiki.online/en/javascript-event-delegation.html seems realy good:
const on = (selector, eventType, childSelector, eventHandler) => {
const elements = document.querySelectorAll(selector)
for (element of elements) {
element.addEventListener(eventType, eventOnElement => {
if (eventOnElement.target.matches(childSelector)) {
eventHandler(eventOnElement)
}
})
}
}
You could construct a new event object, but ideally libraries shouldn't have a tight dependency like this.
Actually i just tested and in your implementation event.target
would point to the tr element.
I agree that libraries shouldn't have a tight dependency (as you mention on you blog post about functions as callbacks)
But it's nice to replicate it as it was very useful! So here's the exact functional equivalent acording to @TheTrueNemo
const on = (selector, eventType, childSelector, eventHandler) => {
const elements = document.querySelectorAll(selector);
for (element of elements) {
element.addEventListener(eventType, eventOnElement => {
const closest = eventOnElement.target.closest(childSelector)
if (!closest || !element.contains(closest)) {
return;
}
eventHandler(eventOnElement);
});
}
}
I usually use composedPath
.
document.body.addEventListener('click', (event) => {
const tr = event.composedPath().find(el=> el.nodeName === 'tr');
if (!tr) return;
console.log(tr.textContent);
});
In most cases I use classes to identify the right elements, but you can use whatever check fits you.
Edit:
I think you can also use event.composedPath()[0].closest('tr')
.
So here's what I ended up with:
myRootElement.addEventListener('click', (event, ...args) {
const matches = event.composedPath()
.filter( (el) => el instanceof HTMLElement )
.find( (el) => el.matches( myTargetElements ) );
if (matches)
eventHandler.call(event.currentTarget, event, matches, ...args);
});
I'm using call
to keep this
pointing to the event currentTarget
as it described here, passing the matches to the callback to cover the shadow-dom case and using the spread operator to pass whatever arguments might appear (just to be sure).
Thank u all, I'm making a pr right now!
A reason you "still might need jquery" is to bind multiple elements, specially if you need delegated events. From jquery docs https://api.jquery.com/on/
It would be really nice to show how to properly do it in vanilla js!