Skip to content

Commit

Permalink
fix: add removed elements to WeakMap, create getParentElement helper
Browse files Browse the repository at this point in the history
When an element is removed, add it to a WeakMap incase it was a parent element that is still being referenced. Any `node.parentElement` call should use the `getParentElement(node)` utility function.
  • Loading branch information
adamdbradley committed Jul 17, 2021
1 parent c423a27 commit 8b63a2e
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 19 deletions.
35 changes: 19 additions & 16 deletions src/core/injector/element_injector.ts
Expand Up @@ -11,45 +11,48 @@ import type {
ComponentStateOf,
Component,
ComponentConstructor,
} from '../component/component.js';
import { qError, QError } from '../error/error.js';
import { qImport } from '../import/qImport.js';
import type { QRL } from '../import/qrl.js';
import { keyToEntityAttribute, EntityKey } from '../entity/entity_key.js';
} from '../component/component';
import { qError, QError } from '../error/error';
import { qImport } from '../import/qImport';
import type { QRL } from '../import/qrl';
import { keyToEntityAttribute, EntityKey } from '../entity/entity_key';
import type {
Entity,
EntityConstructor,
EntityPromise,
EntityPropsOf,
EntityStateOf,
} from '../entity/entity.js';
import { findAttribute } from '../util/dom_attrs.js';
import { AttributeMarker } from '../util/markers.js';
import '../util/qDev.js';
import { isHtmlElement } from '../util/types.js';
import { BaseInjector } from './base_injector.js';
import type { Injector } from './types.js';
} from '../entity/entity';
import { findAttribute } from '../util/dom_attrs';
import { AttributeMarker } from '../util/markers';
import { isHtmlElement } from '../util/types';
import { BaseInjector } from './base_injector';
import type { Injector } from './types';
import { getParentElement } from '../util/dom';

interface EntityValue {
promise: EntityPromise<Entity<any, any>>;
entity: Entity<any, any> | null;
}

/**
* @public
*/
export class ElementInjector extends BaseInjector {
private component: Component<any, any> | null = null;
private componentPromise: Promise<Component<any, any>> | null = null;
private entities: Map<EntityKey, EntityValue> | null = null;

getParent(): Injector | null {
let element = this.element.parentElement;
let element = getParentElement(this.element);
while (element) {
if (
element.hasAttribute(AttributeMarker.Injector) ||
element.hasAttribute(AttributeMarker.ComponentTemplate)
) {
return getInjector(element);
}
element = element.parentElement;
element = getParentElement(element);
}
return null;
}
Expand Down Expand Up @@ -264,7 +267,7 @@ function toEntityPromise<SERVICE extends Entity<any, any>>(
export function getComponentHost(element: Element): Element {
let cursor: Element | null = element;
while (cursor && !cursor.hasAttribute(AttributeMarker.ComponentTemplate)) {
cursor = cursor.parentElement;
cursor = getParentElement(cursor);
}
if (!cursor) {
throw qError(QError.Injector_noHost_element, element);
Expand Down Expand Up @@ -322,7 +325,7 @@ export function getClosestInjector(
) {
return getInjector(cursor) as ElementInjector;
}
cursor = cursor.parentElement;
cursor = getParentElement(cursor);
}
if (throwIfNotFound) {
throw qError(QError.Injector_notFound_element, element);
Expand Down
12 changes: 11 additions & 1 deletion src/core/util/dom.ts
Expand Up @@ -11,6 +11,7 @@
*/
export function removeNode(parentNode: Node, childNode: Node): Node | null {
const nextSibling = childNode.nextSibling as Node | null;
previousParent.set(childNode, parentNode as Element);
parentNode.removeChild(childNode);
return nextSibling;
}
Expand All @@ -24,6 +25,15 @@ export function replaceNode<T extends Node>(
newNode: T
): T {
parentNode.insertBefore(newNode, existingNode);
existingNode && parentNode.removeChild(existingNode);
if (existingNode) {
previousParent.set(existingNode, parentNode as Element);
parentNode.removeChild(existingNode);
}
return newNode;
}

export function getParentElement(node: Node): Element | null {
return node.parentElement || previousParent.get(node) || null;
}

export const previousParent = new WeakMap<Node, Element>();
5 changes: 3 additions & 2 deletions src/core/util/dom_attrs.ts
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://github.com/BuilderIO/qwik/blob/main/LICENSE
*/

import { qError, QError } from '../error/error.js';
import { qError, QError } from '../error/error';
import { getParentElement } from './dom';

/**
* Read attributes from `Element` and return them as an object literal.
Expand Down Expand Up @@ -61,7 +62,7 @@ export function findAttribute<RET1, RET2>(
return callbackSecondary(cursor, attributeSecondary, attrValueSecondary);
}
}
cursor = cursor.parentElement;
cursor = getParentElement(cursor);
}
throw attributeSecondary
? qError(qNotFoundError, attributePrimary, attributeSecondary, element)
Expand Down

0 comments on commit 8b63a2e

Please sign in to comment.