[IMP] blockdom: t-on supports synthetic and native event handler

Synthetic handler is a sort of event delegation that allows placing
only one listener on the document to improve performance. It is an opt-in option.

Native listener places the listener on the node itself.
This commit is contained in:
Lucas Perais (lpe)
2021-10-26 11:01:57 +02:00
committed by Aaron Bohy
parent 3eb63452e7
commit 2ae0149adb
9 changed files with 244 additions and 18 deletions
+53 -13
View File
@@ -1,21 +1,61 @@
import { config } from "./config";
export function createEventHandler(event: string) {
setupSyntheticEvent(event);
const key = `__event__${event}`;
return function setupHandler(this: HTMLElement, data: any) {
(this as any)[key] = data;
};
type EventHandlerSetter = (this: HTMLElement, data: any) => void;
interface EventHandlerCreator {
setup: EventHandlerSetter;
update: EventHandlerSetter;
}
function nativeToSyntheticEvent(event: Event, name: string) {
const eventKey = `__event__${name}`;
export function createEventHandler(rawEvent: string): EventHandlerCreator {
const eventName = rawEvent.split(".")[0];
if (rawEvent.includes(".synthetic")) {
return createSyntheticHandler(eventName);
} else {
return createElementHandler(eventName);
}
}
// Native listener
function createElementHandler(evName: string): EventHandlerCreator {
let eventKey = `__event__${evName}`;
function listener(ev: Event) {
const currentTarget = ev.currentTarget;
if (!currentTarget || !document.contains(currentTarget as HTMLElement)) return;
const data = (currentTarget as any)[eventKey];
if (!data) return;
config.mainEventHandler(data, ev);
}
function setup(this: HTMLElement, data: any) {
(this as any)[eventKey] = data;
this.addEventListener(evName, listener);
}
function update(this: HTMLElement, data: any) {
(this as any)[eventKey] = data;
}
return { setup, update };
}
// Synthetic handler: a form of event delegation that allows placing only one
// listener per event type.
function createSyntheticHandler(evName: string): EventHandlerCreator {
let eventKey = `__event__synthetic_${evName}`;
setupSyntheticEvent(evName, eventKey);
function setup(this: HTMLElement, data: any) {
(this as any)[eventKey] = data;
}
return { setup, update: setup };
}
function nativeToSyntheticEvent(eventKey: string, event: Event) {
let dom = event.target;
while (dom !== null) {
const data = (dom as any)[eventKey];
if (data) {
config.mainEventHandler(data, event);
return;
}
dom = (dom as any).parentNode;
}
@@ -23,10 +63,10 @@ function nativeToSyntheticEvent(event: Event, name: string) {
const CONFIGURED_SYNTHETIC_EVENTS: { [event: string]: boolean } = {};
function setupSyntheticEvent(name: string) {
if (CONFIGURED_SYNTHETIC_EVENTS[name]) {
function setupSyntheticEvent(evName: string, eventKey: string) {
if (CONFIGURED_SYNTHETIC_EVENTS[eventKey]) {
return;
}
document.addEventListener(name, (event) => nativeToSyntheticEvent(event, name));
CONFIGURED_SYNTHETIC_EVENTS[name] = true;
document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event));
CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true;
}