Refactor EventHandlerInfosForElement storage to avoid ad-hoc assumptions about storage formats
This commit is contained in:
parent
e6f183e39e
commit
3249dce498
|
|
@ -31,7 +31,7 @@ export interface OnEventCallback {
|
|||
export class EventDelegator {
|
||||
private static nextEventDelegatorId = 0;
|
||||
|
||||
private eventsCollectionKey: string;
|
||||
private readonly eventsCollectionKey: string;
|
||||
|
||||
private eventInfoStore: EventInfoStore;
|
||||
|
||||
|
|
@ -42,21 +42,18 @@ export class EventDelegator {
|
|||
}
|
||||
|
||||
public setListener(element: Element, eventName: string, eventHandlerId: number, renderingComponentId: number) {
|
||||
// Ensure we have a place to store event info for this element
|
||||
let infoForElement: EventHandlerInfosForElement = element[this.eventsCollectionKey];
|
||||
if (!infoForElement) {
|
||||
infoForElement = element[this.eventsCollectionKey] = {};
|
||||
}
|
||||
const infoForElement = this.getEventHandlerInfosForElement(element, true)!;
|
||||
const existingHandler = infoForElement.getHandler(eventName);
|
||||
|
||||
if (infoForElement.hasOwnProperty(eventName)) {
|
||||
if (existingHandler) {
|
||||
// We can cheaply update the info on the existing object and don't need any other housekeeping
|
||||
const oldInfo = infoForElement[eventName];
|
||||
this.eventInfoStore.update(oldInfo.eventHandlerId, eventHandlerId);
|
||||
// Note that this also takes care of updating the eventHandlerId on the existing handler object
|
||||
this.eventInfoStore.update(existingHandler.eventHandlerId, eventHandlerId);
|
||||
} else {
|
||||
// Go through the whole flow which might involve registering a new global handler
|
||||
const newInfo = { element, eventName, eventHandlerId, renderingComponentId };
|
||||
this.eventInfoStore.add(newInfo);
|
||||
infoForElement[eventName] = newInfo;
|
||||
infoForElement.setHandler(eventName, newInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,12 +66,10 @@ export class EventDelegator {
|
|||
// Looks like this event handler wasn't already disposed
|
||||
// Remove the associated data from the DOM element
|
||||
const element = info.element;
|
||||
if (element.hasOwnProperty(this.eventsCollectionKey)) {
|
||||
const elementEventInfos: EventHandlerInfosForElement = element[this.eventsCollectionKey];
|
||||
delete elementEventInfos[info.eventName];
|
||||
if (Object.getOwnPropertyNames(elementEventInfos).length === 0) {
|
||||
delete element[this.eventsCollectionKey];
|
||||
}
|
||||
const elementEventInfos = this.getEventHandlerInfosForElement(element, false);
|
||||
if (elementEventInfos) {
|
||||
elementEventInfos.removeHandler(info.eventName);
|
||||
this.cleanEventHandlerInfosForElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,15 +84,15 @@ export class EventDelegator {
|
|||
let eventArgs: EventForDotNet<UIEventArgs> | null = null; // Populate lazily
|
||||
const eventIsNonBubbling = nonBubblingEvents.hasOwnProperty(evt.type);
|
||||
while (candidateElement) {
|
||||
if (candidateElement.hasOwnProperty(this.eventsCollectionKey)) {
|
||||
const handlerInfos: EventHandlerInfosForElement = candidateElement[this.eventsCollectionKey];
|
||||
if (handlerInfos.hasOwnProperty(evt.type)) {
|
||||
const handlerInfos = this.getEventHandlerInfosForElement(candidateElement, false);
|
||||
if (handlerInfos) {
|
||||
const handlerInfo = handlerInfos.getHandler(evt.type);
|
||||
if (handlerInfo) {
|
||||
// We are going to raise an event for this element, so prepare info needed by the .NET code
|
||||
if (!eventArgs) {
|
||||
eventArgs = EventForDotNet.fromDOMEvent(evt);
|
||||
}
|
||||
|
||||
const handlerInfo = handlerInfos[evt.type];
|
||||
const eventFieldInfo = EventFieldInfo.fromEvent(handlerInfo.renderingComponentId, evt);
|
||||
this.onEvent(evt, handlerInfo.eventHandlerId, eventArgs, eventFieldInfo);
|
||||
}
|
||||
|
|
@ -106,6 +101,23 @@ export class EventDelegator {
|
|||
candidateElement = eventIsNonBubbling ? null : candidateElement.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
private getEventHandlerInfosForElement(element: Element, createIfNeeded: boolean): EventHandlerInfosForElement | null {
|
||||
if (element.hasOwnProperty(this.eventsCollectionKey)) {
|
||||
return element[this.eventsCollectionKey];
|
||||
} else if (createIfNeeded) {
|
||||
return (element[this.eventsCollectionKey] = new EventHandlerInfosForElement());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private cleanEventHandlerInfosForElement(element: Element) {
|
||||
const existingValue = this.getEventHandlerInfosForElement(element, false);
|
||||
if (existingValue && existingValue.isEmpty()) {
|
||||
delete element[this.eventsCollectionKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsible for adding and removing the global listener when the number of listeners
|
||||
|
|
@ -168,14 +180,30 @@ class EventInfoStore {
|
|||
}
|
||||
}
|
||||
|
||||
interface EventHandlerInfosForElement {
|
||||
class EventHandlerInfosForElement {
|
||||
// Although we *could* track multiple event handlers per (element, eventName) pair
|
||||
// (since they have distinct eventHandlerId values), there's no point doing so because
|
||||
// our programming model is that you declare event handlers as attributes. An element
|
||||
// can only have one attribute with a given name, hence only one event handler with
|
||||
// that name at any one time.
|
||||
// So to keep things simple, only track one EventHandlerInfo per (element, eventName)
|
||||
[eventName: string]: EventHandlerInfo;
|
||||
private handlers: { [eventName: string]: EventHandlerInfo } = {};
|
||||
|
||||
public getHandler(eventName: string): EventHandlerInfo | null {
|
||||
return this.handlers.hasOwnProperty(eventName) ? this.handlers[eventName] : null;
|
||||
}
|
||||
|
||||
public setHandler(eventName: string, handler: EventHandlerInfo) {
|
||||
this.handlers[eventName] = handler;
|
||||
}
|
||||
|
||||
public removeHandler(eventName: string) {
|
||||
delete this.handlers[eventName];
|
||||
}
|
||||
|
||||
public isEmpty(): boolean {
|
||||
return Object.getOwnPropertyNames(this.handlers).length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface EventHandlerInfo {
|
||||
|
|
|
|||
Loading…
Reference in New Issue