Skip to content

Commit

Permalink
HTMLElement: Implement before, after, append, prepend
Browse files Browse the repository at this point in the history
  • Loading branch information
Maluscat committed Sep 29, 2024
1 parent 0959b6b commit 04856f0
Showing 1 changed file with 48 additions and 19 deletions.
67 changes: 48 additions & 19 deletions src/nodes/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export interface RawAttributes {
}

export type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend';
export type NodeInsertable = Node | string;

// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
const Htags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup'];
Expand Down Expand Up @@ -643,10 +644,7 @@ export default class HTMLElement extends Node {
* @return {Node} node appended
*/
public appendChild<T extends Node = Node>(node: T) {
// remove the node from it's parent
node.remove();
this.childNodes.push(node);
node.parentNode = this;
this.append(node);
return node;
}

Expand Down Expand Up @@ -818,24 +816,13 @@ export default class HTMLElement extends Node {
}
const p = parse(html, this._parseOptions);
if (where === 'afterend') {
const idx = this.parentNode.childNodes.findIndex((child) => {
return child === this;
});
resetParent(p.childNodes, this.parentNode);
this.parentNode.childNodes.splice(idx + 1, 0, ...p.childNodes);
this.after(...p.childNodes);
} else if (where === 'afterbegin') {
resetParent(p.childNodes, this);
this.childNodes.unshift(...p.childNodes);
this.prepend(...p.childNodes);
} else if (where === 'beforeend') {
p.childNodes.forEach((n) => {
this.appendChild(n);
});
this.append(...p.childNodes);
} else if (where === 'beforebegin') {
const idx = this.parentNode.childNodes.findIndex((child) => {
return child === this;
});
resetParent(p.childNodes, this.parentNode);
this.parentNode.childNodes.splice(idx, 0, ...p.childNodes);
this.before(...p.childNodes);
} else {
throw new Error(
`The value provided ('${where as string}') is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'`
Expand All @@ -847,6 +834,33 @@ export default class HTMLElement extends Node {
// }
}

/** Prepend nodes or strings to this node's children. */
public prepend(...insertable: NodeInsertable[]) {
const nodes = resolveInsertable(insertable);
resetParent(nodes, this);
this.childNodes.unshift(...nodes);
}
/** Append nodes or strings to this node's children. */
public append(...insertable: NodeInsertable[]) {
const nodes = resolveInsertable(insertable);
resetParent(nodes, this);
this.childNodes.push(...nodes);
}
/** Insert nodes or strings before this node. */
public before(...insertable: NodeInsertable[]) {
const nodes = resolveInsertable(insertable);
const siblings = this.parentNode.childNodes;
resetParent(nodes, this.parentNode);
siblings.splice(siblings.indexOf(this), 0, ...nodes);
}
/** Insert nodes or strings after this node. */
public after(...insertable: NodeInsertable[]) {
const nodes = resolveInsertable(insertable);
const siblings = this.parentNode.childNodes;
resetParent(nodes, this.parentNode);
siblings.splice(siblings.indexOf(this) + 1, 0, ...nodes);
}

public get nextSibling(): Node | null {
if (this.parentNode) {
const children = this.parentNode.childNodes;
Expand Down Expand Up @@ -1225,6 +1239,21 @@ export function parse(data: string, options = {} as Partial<Options>) {
return root;
}

/**
* Resolves a list of {@link NodeInsertable} to a list of nodes,
* and removes nodes from any potential parent.
*/
function resolveInsertable(insertable: NodeInsertable[]): Node[] {
return insertable.map(val => {
if (typeof val === 'string') {
return new TextNode(val);
} else {
val.remove();
return val;
}
});
}

function resetParent(nodes: Node[], parent: HTMLElement) {
return nodes.map((node) => {
node.parentNode = parent;
Expand Down

0 comments on commit 04856f0

Please sign in to comment.