From 9a9aea5bfec9fa3161c6594299cf20340be80518 Mon Sep 17 00:00:00 2001 From: Rene Date: Sat, 17 Apr 2021 21:36:26 +0200 Subject: [PATCH] improve domobserver code and tests --- .../src/observers/domObserver.ts | 53 ++++++++----------- .../src/support/utils/array.ts | 14 ++--- .../observers/domObserver/index.browser.ts | 45 ++++++++++++++++ 3 files changed, 75 insertions(+), 37 deletions(-) diff --git a/packages/overlayscrollbars/src/observers/domObserver.ts b/packages/overlayscrollbars/src/observers/domObserver.ts index f289a8e..ab72f03 100644 --- a/packages/overlayscrollbars/src/observers/domObserver.ts +++ b/packages/overlayscrollbars/src/observers/domObserver.ts @@ -28,7 +28,7 @@ interface DOMObserverOptionsBase { } interface DOMContentObserverOptions extends DOMObserverOptionsBase { - _eventContentChange?: DOMObserverEventContentChange; // [selector, eventname | function returning eventname] + _eventContentChange?: DOMObserverEventContentChange; // [selector, eventname(s) | function returning eventname(s)] -> eventnames divided by whitespaces _nestedTargetSelector?: string; _ignoreContentChange?: DOMObserverIgnoreContentChange; // function which will prevent marking certain dom changes as content change if it returns true _ignoreNestedTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed on nested targets if it returns true @@ -50,7 +50,7 @@ interface DOMContentObserver extends DOMObserverBase { interface DOMTargetObserver extends DOMObserverBase {} export type DOMObserverEventContentChange = - | Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined> + | Array<[StringNullUndefined, ((elms: Node[]) => StringNullUndefined) | StringNullUndefined] | null | undefined> | false | null | undefined; @@ -90,22 +90,6 @@ export type DOMObserver = ContentObserver exten const createEventContentChange = (target: Element, eventContentChange: DOMObserverEventContentChange, callback: (...args: any) => any) => { let map: Map | undefined; let eventContentChangeRef: DOMObserverEventContentChange; - const addEvent = (elm: Node, eventName: string) => { - if (map) { - const entry = map.get(elm); - const newEntry = isUndefined(entry); - const changedExistingEntry = !newEntry && eventName !== entry; - const register = newEntry || changedExistingEntry; - - if (changedExistingEntry) { - off(elm, entry!, callback); - } - if (register) { - map.set(elm, eventName); - on(elm, eventName, callback); - } - } - }; const _destroy = () => { if (map) { map.forEach((eventName: string, elm: Node) => off(elm, eventName, callback)); @@ -113,28 +97,37 @@ const createEventContentChange = (target: Element, eventContentChange: DOMObserv } }; const _updateElements = (getElements?: (selector: string) => Node[]) => { - if (eventContentChangeRef) { + if (map && eventContentChangeRef) { const eventElmList = eventContentChangeRef.reduce>((arr, item) => { if (item) { const selector = item[0]; - const eventName = item[1]; - const elements = eventName && selector && (getElements ? getElements(selector) : find(selector, target)); + const eventNames = item[1]; + const elements = eventNames && selector && (getElements ? getElements(selector) : find(selector, target)); + const parsedEventNames = isFunction(eventNames) ? eventNames(elements) : eventNames; - if (elements) { - push(arr, [elements, isFunction(eventName) ? eventName(elements) : eventName!], true); + if (elements && elements.length && parsedEventNames && isString(parsedEventNames)) { + push(arr, [elements, parsedEventNames.trim()], true); } } return arr; }, []); - each(eventElmList, (item) => { - const elements = item[0]; - const eventName = item[1]; + each(eventElmList, (item) => + each(item[0], (elm) => { + const eventNames = item[1]; + const registredEventNames = map!.get(elm); + const newEntry = isUndefined(registredEventNames); + const changingExistingEntry = !newEntry && eventNames !== registredEventNames; + const finalEventNames = changingExistingEntry ? `${registredEventNames} ${eventNames}` : eventNames; - each(elements, (elm) => { - addEvent(elm, eventName); - }); - }); + if (changingExistingEntry) { + off(elm, registredEventNames!, callback); + } + + map!.set(elm, finalEventNames); + on(elm, finalEventNames, callback); + }) + ); } }; const _updateEventContentChange = (newEventContentChange: DOMObserverEventContentChange) => { diff --git a/packages/overlayscrollbars/src/support/utils/array.ts b/packages/overlayscrollbars/src/support/utils/array.ts index 63a23b6..c3abf57 100644 --- a/packages/overlayscrollbars/src/support/utils/array.ts +++ b/packages/overlayscrollbars/src/support/utils/array.ts @@ -12,28 +12,28 @@ type RunEachItem = ((...args: any) => any | any[]) | null | undefined; */ export function each( array: Array | ReadonlyArray, - callback: (value: T, indexOrKey: number, source: Array) => boolean | void + callback: (value: T, indexOrKey: number, source: Array) => boolean | unknown ): Array | ReadonlyArray; export function each( array: Array | ReadonlyArray | null | undefined, - callback: (value: T, indexOrKey: number, source: Array) => boolean | void + callback: (value: T, indexOrKey: number, source: Array) => boolean | unknown ): Array | ReadonlyArray | null | undefined; export function each( arrayLikeObject: ArrayLike, - callback: (value: T, indexOrKey: number, source: ArrayLike) => boolean | void + callback: (value: T, indexOrKey: number, source: ArrayLike) => boolean | unknown ): ArrayLike; export function each( arrayLikeObject: ArrayLike | null | undefined, - callback: (value: T, indexOrKey: number, source: ArrayLike) => boolean | void + callback: (value: T, indexOrKey: number, source: ArrayLike) => boolean | unknown ): ArrayLike | null | undefined; -export function each(obj: PlainObject, callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | void): PlainObject; +export function each(obj: PlainObject, callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | unknown): PlainObject; export function each( obj: PlainObject | null | undefined, - callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | void + callback: (value: any, indexOrKey: string, source: PlainObject) => boolean | unknown ): PlainObject | null | undefined; export function each( source: ArrayLike | PlainObject | null | undefined, - callback: (value: T, indexOrKey: any, source: any) => boolean | void + callback: (value: T, indexOrKey: any, source: any) => boolean | unknown ): Array | ReadonlyArray | ArrayLike | PlainObject | null | undefined { if (isArrayLike(source)) { for (let i = 0; i < source.length; i++) { diff --git a/packages/overlayscrollbars/tests/browser/observers/domObserver/index.browser.ts b/packages/overlayscrollbars/tests/browser/observers/domObserver/index.browser.ts index 7ea67e0..12f9018 100644 --- a/packages/overlayscrollbars/tests/browser/observers/domObserver/index.browser.ts +++ b/packages/overlayscrollbars/tests/browser/observers/domObserver/index.browser.ts @@ -354,6 +354,8 @@ const addRemoveImgElmsFn = async () => { before(); appendChildren(imgElmsSlot, imgHolder); + await timeout(250); + await waitForOrFailTest(() => { after(); compare(2); @@ -389,6 +391,8 @@ const addRemoveImgElmsFn = async () => { addMultipleItem(); addMultipleItem(); + await timeout(250); + await waitForOrFailTest(() => { after(); compare(2); @@ -403,6 +407,45 @@ const addRemoveImgElmsFn = async () => { await addMultiple(); + // remove load event from image test + const addChanged = async ( + newEventContentChange: Array<[string | null | undefined, (() => string | null | undefined) | string | null | undefined] | null | undefined> + ) => { + contentDomObserver._updateEventContentChange(newEventContentChange); + + const img = new Image(1, 1); + img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; + + const { before, after, compare } = changedThrough(domContentObserverObservations); + const imgHolder = createDiv('img'); + appendChildren(imgHolder, img); + + before(); + appendChildren(imgElmsSlot, imgHolder); + + await timeout(250); + + await waitForOrFailTest(() => { + after(); + compare(1); + }); + + contentDomObserver._updateEventContentChange(contentChangeArr); + }; + + await addChanged([ + ['img', 'something'], + ['img', 'something2'], + ['img', null], + ['img', undefined], + ['img', () => 'hi'], + ['img', () => null], + ['img', () => undefined], + null, + undefined, + ]); + await addChanged([]); + removeElements(document.querySelectorAll('.img')); await timeout(250); @@ -578,11 +621,13 @@ const start = async () => { targetDomObserver._update(); targetDomObserver._destroy(); + targetDomObserver._destroy(); targetDomObserver._update(); contentDomObserver._updateEventContentChange([]); contentDomObserver._update(); contentDomObserver._destroy(); + contentDomObserver._destroy(); contentDomObserver._updateEventContentChange([]); contentDomObserver._update(); };