Skip to content

Commit f337e60

Browse files
committed
Reverse event optimisation PR that breaks Safari
1 parent 96e68a9 commit f337e60

File tree

1 file changed

+22
-75
lines changed

1 file changed

+22
-75
lines changed

src/elements.ts

Lines changed: 22 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,12 @@ interface EventListenerOptions {
4343
export abstract class BaseView<T extends HTMLElement|SVGElement> {
4444
readonly _data: Obj<unknown> = {};
4545
readonly _events: Obj<EventCallback[]> = {};
46-
_mutationObserver: MutationObserver|undefined;
47-
private readonly _mutationObserverCallbacks: Obj<EventCallback[]> = {};
4846
readonly type: string = 'default';
4947
model?: Observable;
50-
isDeleted?: boolean;
5148

52-
constructor(readonly __el: T) {
49+
constructor(readonly _el: T) {
5350
// Store a reference to this element within the native browser DOM.
54-
__el._view = this;
55-
}
56-
57-
get _el() {
58-
if (this.isDeleted) console.error('Trying to access a deleted element:', this);
59-
return this.__el;
51+
_el._view = this;
6052
}
6153

6254
get id() {
@@ -606,33 +598,12 @@ export abstract class BaseView<T extends HTMLElement|SVGElement> {
606598
}
607599
}
608600

609-
/** Removes listeners and model data from el */
610-
private unsubscribe() {
611-
this.isDeleted = true;
612-
this.model?.clear();
613-
this._mutationObserver?.disconnect();
614-
this.offAll();
615-
616-
for (const child of this.children) {
617-
child.unsubscribe();
618-
}
619-
620-
/* Disabled temporarily
621-
// hack to avoid TS notification
622-
// about setting undefined value to readonly properties
623-
delete (this as any)._el;
624-
delete (this as any)._data;
625-
delete (this as any)._events;
626-
627-
// remove mutation observer instances
628-
delete (this as any)._mutationObserver;
629-
delete (this as any)._mutationObserverCallbacks; */
630-
}
631-
632601
/** Removes this element. */
633602
remove() {
634603
this.detach();
635-
this.unsubscribe();
604+
// TODO Remove event listeners (including children)
605+
// TODO Remove model bindings (including children)
606+
// this._el = this._data = this._events = undefined;
636607
}
637608

638609
/** Removes all children of this element. */
@@ -678,24 +649,13 @@ export abstract class BaseView<T extends HTMLElement|SVGElement> {
678649
*/
679650
off(events: string, callback?: EventCallback) {
680651
for (const e of words(events)) {
681-
if (callback) {
682-
this._events[e] = this._events[e].filter(fn => fn !== callback);
683-
unbindEvent(this, e, callback);
684-
continue;
652+
if (e in this._events) {
653+
this._events[e] = callback ? this._events[e].filter(fn => fn !== callback) : [];
685654
}
686-
for (const eventsCallback of this._events[e]) unbindEvent(this, e, eventsCallback);
655+
unbindEvent(this, e, callback);
687656
}
688657
}
689658

690-
/**
691-
* Removes all event listeners from this element
692-
*/
693-
offAll() {
694-
Object.entries(this._events).forEach(([eventName, callbacks]) => {
695-
callbacks.forEach((callback) => this.off(eventName, callback));
696-
});
697-
}
698-
699659
/** Triggers a specific event on this element. */
700660
trigger(events: string, args: unknown = {}) {
701661
for (const e of words(events)) {
@@ -713,41 +673,28 @@ export abstract class BaseView<T extends HTMLElement|SVGElement> {
713673
const keyNames = new Set(words(keys));
714674
const event = options?.up ? 'keyup' : 'keydown';
715675

716-
const eventFunction = (e: KeyboardEvent) => {
676+
const target = (this._el === document.body ? document : this._el) as HTMLElement;
677+
target.addEventListener(event, (e: KeyboardEvent) => {
717678
const key = keyCode(e);
718679
if (options?.meta ? !e.ctrlKey && !e.metaKey : e.ctrlKey || e.metaKey) return;
719680
if (!key || !keyNames.has(key)) return;
720681
if (document.activeElement !== this._el && document.activeElement?.shadowRoot?.activeElement !== this._el && Browser.formIsActive) return;
721682
callback(e as KeyboardEvent, key);
722-
};
723-
724-
const target = (this._el === document.body ? document : this._el) as HTMLElement;
725-
target.addEventListener(event, eventFunction);
726-
727-
if (!(event in this._events)) this._events[event] = [];
728-
this._events[event].push(eventFunction);
683+
});
729684
}
730685

731-
/**
732-
* Bind an listener when element attribute changed
733-
*/
734686
onAttr(name: string, callback: (value: string, initial?: boolean) => void) {
735-
if (!this._mutationObserver) {
736-
this._mutationObserver = new MutationObserver((mutations) => {
737-
for (const m of mutations) {
738-
if (m.type === 'attributes' && m.attributeName === name) {
739-
for (const attributeCallback of this._mutationObserverCallbacks[name]) {
740-
attributeCallback(this.attr(name));
741-
}
742-
}
743-
}
744-
});
745-
this._mutationObserver.observe(this._el, {attributes: true});
746-
}
687+
// TODO Reuse existing observers, remove events, disconnect when deleting.
747688

748-
if (!(name in this._mutationObserverCallbacks)) this._mutationObserverCallbacks[name] = [];
749-
this._mutationObserverCallbacks[name].push(callback);
689+
const observer = new MutationObserver((mutations) => {
690+
for (const m of mutations) {
691+
if (m.type === 'attributes' && m.attributeName === name) {
692+
callback(this.attr(name));
693+
}
694+
}
695+
});
750696

697+
observer.observe(this._el, {attributes: true});
751698
callback(this.attr(name), true);
752699
}
753700

@@ -1199,7 +1146,7 @@ export class WindowView extends HTMLBaseView<HTMLHtmlElement|HTMLBodyElement> {
11991146
}
12001147

12011148
get scrollTop() {
1202-
return window.scrollY;
1149+
return window.pageYOffset;
12031150
}
12041151

12051152
set scrollTop(y) {
@@ -1208,7 +1155,7 @@ export class WindowView extends HTMLBaseView<HTMLHtmlElement|HTMLBodyElement> {
12081155
}
12091156

12101157
get scrollLeft() {
1211-
return window.scrollX;
1158+
return window.pageXOffset;
12121159
}
12131160

12141161
set scrollLeft(x) {

0 commit comments

Comments
 (0)