Skip to content

Commit

Permalink
Add support for events triggered on text nodes (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante authored Nov 9, 2020
1 parent bbaf654 commit 60bfecb
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 12 deletions.
31 changes: 19 additions & 12 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ function isEventTarget(
return typeof (elements as EventTarget).addEventListener === 'function';
}

function safeClosest(event: Event, selector: string): Element | void {
let target = event.target;
if (target instanceof Text) {
target = target.parentElement;
}

if (target instanceof Element && event.currentTarget instanceof Element) {
// `.closest()` may match ancestors of `currentTarget` but we only need its children
const closest = target.closest(selector);
if (closest && event.currentTarget.contains(closest)) {
return closest;
}
}
}

/**
* Delegates event to a selector.
*/
Expand Down Expand Up @@ -99,18 +114,10 @@ function delegate<

// Handle the regular Element usage
const capture = Boolean(typeof options === 'object' ? options.capture : options);
const listenerFn: EventListener = (event: Partial<delegate.Event>): void => {
const delegateTarget = (event.target as Element).closest(selector) as TElement;

if (!delegateTarget) {
return;
}

event.delegateTarget = delegateTarget;

// Closest may match elements outside of the currentTarget
// so it needs to be limited to elements inside it
if ((event.currentTarget as Element).contains(event.delegateTarget)) {
const listenerFn: EventListener = (event: Event): void => {
const delegateTarget = safeClosest(event, selector);
if (delegateTarget) {
(event as any).delegateTarget = delegateTarget;
callback.call(baseElement, event as delegate.Event<TEvent, TElement>);
}
};
Expand Down
7 changes: 7 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ const {window} = new JSDOM(`
</ul>
`);

global.Text = window.Text;
global.Event = window.Event;
global.Element = window.Element;
global.Document = window.Document;
global.MouseEvent = window.MouseEvent;
global.document = window.document;
const container = window.document.querySelector('ul');
const anchor = window.document.querySelector('a');
Expand Down Expand Up @@ -46,6 +48,11 @@ test.serial('should add an event listener only once', t => {
anchor.click();
});

test.serial('should handle events on text nodes', t => {
delegate(container, 'a', 'click', t.pass);
anchor.firstChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});

test.serial('should remove an event listener', t => {
const spy = sinon.spy(container, 'removeEventListener');

Expand Down

0 comments on commit 60bfecb

Please sign in to comment.