- From: Andrea Giammarchi <notifications@github.com>
- Date: Thu, 14 Nov 2024 08:17:09 -0800
- To: whatwg/dom <dom@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/dom/issues/736/2476840911@github.com>
OK, I went ahead and created this implementation which leaves just a few details to discuss but it works already great: **persistent-fragment.js** ```js import native from 'https://esm.run/custom-function/factory'; const OWNERSHIP = 'ownerFragment'; const { defineProperty, getPrototypeOf, hasOwn } = Object; const children = ({ nodeType }) => nodeType === 1; const isOwned = (self, node) => !hasOwn(node, OWNERSHIP) || node[OWNERSHIP] == self; const asValueOf = node => node instanceof PersistentFragment ? node.valueOf() : node; export default class PersistentFragment extends native(DocumentFragment) { #childNodes; constructor(...childNodes) { super(document.createDocumentFragment()); this.#childNodes = childNodes.map(asOwnedNode, this); super.append(...childNodes); } append(...childNodes) { childNodes = childNodes.map(asOwnedNode, this); if (this.#childNodes.length) this.#childNodes.at(-1).after(...childNodes); else super.append(...childNodes); this.#childNodes.push(...childNodes); } appendChild(childNode) { const node = asOwnedNode.call(this, childNode); if (this.#childNodes.length) this.#childNodes.at(-1).after(node); else super.appendChild(node); this.#childNodes.push(node); } insertBefore(childNode, ownedNode) { if (ownedNode) { if (isOwned(this, ownedNode)) { ownedNode.before(asOwnedNode.call(this, childNode)); const i = this.#childNodes.indexOf(ownedNode); this.#childNodes.splice(i, 0, childNode); } else throw new Error('Illegal operation'); } else this.appendChild(childNode); } prepend(...childNodes) { childNodes = childNodes.map(asOwnedNode, this); if (this.#childNodes.length) this.#childNodes.at(0).before(...childNodes); else super.prepend(...childNodes); this.#childNodes.unshift(...childNodes); } removeChild(childNode) { const i = this.#childNodes.indexOf(ownedNode); if (i < 0) throw new Error('Illegal operation'); childNode.remove(); this.#childNodes.splice(i, 1); } replaceChildren(...childNodes) { this.#childNodes.forEach(notOwnedAnymore, this); this.#childNodes = childNodes.map(asOwnedNode, this); super.replaceChildren(...this.#childNodes); } valueOf() { if (this.#childNodes.at(0)?.parentNode !== this) super.append(...this.#childNodes); return this; } } function asOwnedNode(childNode) { if (!isOwned(this, childNode)) throw new Error('Illegal operation'); return defineProperty(childNode, OWNERSHIP, { value: this, configurable: true }); } function notOwnedAnymore(childNode) { delete childNode[OWNERSHIP]; } // patch globals const [ { prototype: E }, { prototype: N }, { prototype: PF }, { prototype: CD }, ] = [ Element, Node, PersistentFragment, getPrototypeOf(Text), ]; for (const key of Reflect.ownKeys(PF)) { if (key === 'constructor') continue; const { [key]: value } = PF; if (typeof value !== 'function') continue; for (const proto of [N, E]) { if (hasOwn(proto, key)) { const native = proto[key]; defineProperty(proto, key, { value(...args) { return native.apply(this, args.map(asValueOf)); } }); } } } for (const key of ['after', 'before']) { for (const proto of [CD, E]) { const native = proto[key]; defineProperty(proto, key, { value(...args) { return native.apply(this, args.map(asValueOf)); } }); } } ``` **index.html** ```html <!doctype html> <script type="module"> import PersistentFragment from "./persistent-fragment.js"; const text = value => document.createTextNode(value); const a = text('a'); const c = text('c'); const pf = new PersistentFragment(a, c); const hr = document.createElement('hr'); document.body.append(pf, hr); setTimeout( () => { pf.insertBefore(text('b'), c); setTimeout( () => { hr.after(pf); }, 1000 ); }, 1000 ); </script> ``` You would see `ac`, then `abc`, both before the `hr`, then you'll see the fragment moved *after* that `hr` with `abc` as resulting DOM content. I think this simplification is superior to my original proposal as it answers tons of questions and simplifies everything that needs simplification around this topic and I believe it would be an awesome feature to have for any library author out there. -- Reply to this email directly or view it on GitHub: https://github.com/whatwg/dom/issues/736#issuecomment-2476840911 You are receiving this because you are subscribed to this thread. Message ID: <whatwg/dom/issues/736/2476840911@github.com>
Received on Thursday, 14 November 2024 16:17:13 UTC