Keep track of preventDefault/stopBubbling flags on JS side

This commit is contained in:
Steve Sanderson 2019-09-26 13:28:31 +01:00 committed by Artak
parent 3249dce498
commit 3cd4726c76
2 changed files with 59 additions and 2 deletions

View File

@ -10,6 +10,9 @@ const sharedTemplateElemForParsing = document.createElement('template');
const sharedSvgElemForParsing = document.createElementNS('http://www.w3.org/2000/svg', 'g');
const preventDefaultEvents: { [eventType: string]: boolean } = { submit: true };
const rootComponentsPendingFirstRender: { [componentId: number]: LogicalElement } = {};
const internalAttributeNamePrefix = '__internal_';
const eventPreventDefaultAttributeNamePrefix = 'preventDefault_';
const eventStopBubblingAttributeNamePrefix = 'stopBubbling_';
export class BrowserRenderer {
private eventDelegator: EventDelegator;
@ -310,8 +313,30 @@ export class BrowserRenderer {
return this.tryApplyValueProperty(batch, element, attributeFrame);
case 'checked':
return this.tryApplyCheckedProperty(batch, element, attributeFrame);
default:
default: {
if (attributeName.startsWith(internalAttributeNamePrefix)) {
this.applyInternalAttribute(batch, element, attributeName.substring(internalAttributeNamePrefix.length), attributeFrame);
return true;
}
return false;
}
}
}
private applyInternalAttribute(batch: RenderBatch, element: Element, internalAttributeName: string, attributeFrame: RenderTreeFrame | null) {
const attributeValue = attributeFrame ? batch.frameReader.attributeValue(attributeFrame) : null;
if (internalAttributeName.startsWith(eventStopBubblingAttributeNamePrefix)) {
// Stop bubbling
const eventName = internalAttributeName.substring(eventStopBubblingAttributeNamePrefix.length);
this.eventDelegator.setStopBubbling(element, eventName, attributeValue !== null);
} else if (internalAttributeName.startsWith(eventPreventDefaultAttributeNamePrefix)) {
// Prevent default
const eventName = internalAttributeName.substring(eventPreventDefaultAttributeNamePrefix.length);
this.eventDelegator.setPreventDefault(element, eventName, attributeValue !== null);
} else {
// The prefix makes this attribute name reserved, so any other usage is disallowed
throw new Error(`Unsupported internal attribute '${internalAttributeName}'`);
}
}

View File

@ -74,6 +74,16 @@ export class EventDelegator {
}
}
public setStopBubbling(element: Element, eventName: string, value: boolean) {
const infoForElement = this.getEventHandlerInfosForElement(element, true)!;
infoForElement.stopBubbling(eventName, value);
}
public setPreventDefault(element: Element, eventName: string, value: boolean) {
const infoForElement = this.getEventHandlerInfosForElement(element, true)!;
infoForElement.preventDefault(eventName, value);
}
private onGlobalEvent(evt: Event) {
if (!(evt.target instanceof Element)) {
return;
@ -188,6 +198,8 @@ class EventHandlerInfosForElement {
// that name at any one time.
// So to keep things simple, only track one EventHandlerInfo per (element, eventName)
private handlers: { [eventName: string]: EventHandlerInfo } = {};
private preventDefaultFlags: { [eventName: string]: boolean } | null = null;
private stopBubblingFlags: { [eventName: string]: boolean } | null = null;
public getHandler(eventName: string): EventHandlerInfo | null {
return this.handlers.hasOwnProperty(eventName) ? this.handlers[eventName] : null;
@ -201,8 +213,28 @@ class EventHandlerInfosForElement {
delete this.handlers[eventName];
}
public preventDefault(eventName: string, setValue: boolean | null): boolean {
if (setValue !== null) {
this.preventDefaultFlags = this.preventDefaultFlags || {};
this.preventDefaultFlags[eventName] = setValue;
}
return this.preventDefaultFlags ? this.preventDefaultFlags[eventName] : false;
}
public stopBubbling(eventName: string, setValue: boolean | null): boolean {
if (setValue !== null) {
this.stopBubblingFlags = this.stopBubblingFlags || {};
this.stopBubblingFlags[eventName] = setValue;
}
return this.stopBubblingFlags ? this.stopBubblingFlags[eventName] : false;
}
public isEmpty(): boolean {
return Object.getOwnPropertyNames(this.handlers).length === 0;
return Object.getOwnPropertyNames(this.handlers).length === 0
&& (!this.preventDefaultFlags || Object.getOwnPropertyNames(this.preventDefaultFlags).length === 0)
&& (!this.stopBubblingFlags || Object.getOwnPropertyNames(this.stopBubblingFlags).length === 0);
}
}