diff --git a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts index 075b20743..8911a01da 100644 --- a/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts +++ b/packages/happy-dom/src/nodes/html-template-element/HTMLTemplateElement.ts @@ -3,6 +3,9 @@ import HTMLElement from '../html-element/HTMLElement'; import IDocumentFragment from '../document-fragment/IDocumentFragment'; import INode from '../node/INode'; import IHTMLTemplateElement from './IHTMLTemplateElement'; +import XMLParser from '../../xml-parser/XMLParser'; +import XMLSerializer from '../../xml-serializer/XMLSerializer'; +import DOMException from '../../exception/DOMException'; /** * HTML Template Element. @@ -11,105 +14,118 @@ import IHTMLTemplateElement from './IHTMLTemplateElement'; * https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement. */ export default class HTMLTemplateElement extends HTMLElement implements IHTMLTemplateElement { - private _contentElement: IDocumentFragment = null; + public readonly content: IDocumentFragment = this.ownerDocument.createDocumentFragment(); /** - * Returns the content. - * - * @returns Content. + * @override + */ + public get innerHTML(): string { + return this.getInnerHTML(); + } + + /** + * @override */ - public get content(): IDocumentFragment { - if (!this._contentElement) { - this._contentElement = this.ownerDocument.createDocumentFragment(); + public set innerHTML(html: string) { + for (const child of this.content.childNodes.slice()) { + this.content.removeChild(child); + } + + for (const node of XMLParser.parse(this.ownerDocument, html).childNodes.slice()) { + this.content.appendChild(node); } - return this._contentElement; } /** - * Previous sibling. - * - * @returns Node. + * @override + */ + public get outerHTML(): string { + return new XMLSerializer().serializeToString(this.content); + } + + /** + * @override + */ + public set outerHTML(_html: string) { + throw new DOMException( + `Failed to set the 'outerHTML' property on 'Element': This element has no parent node.` + ); + } + + /** + * @override */ public get previousSibling(): INode { return this.content.previousSibling; } /** - * Next sibling. - * - * @returns Node. + * @override */ public get nextSibling(): INode { return this.content.nextSibling; } /** - * First child. - * - * @returns Node. + * @override */ public get firstChild(): INode { return this.content.firstChild; } /** - * Last child. - * - * @returns Node. + * @override */ public get lastChild(): INode { return this.content.lastChild; } /** - * Append a child node to childNodes. - * - * @param node Node to append. - * @returns Appended node. + * @override + */ + public getInnerHTML(options?: { includeShadowRoots?: boolean }): string { + const xmlSerializer = new XMLSerializer(); + let xml = ''; + for (const node of this.content.childNodes) { + xml += xmlSerializer.serializeToString(node, options); + } + return xml; + } + + /** + * @override */ public appendChild(node: INode): INode { return this.content.appendChild(node); } /** - * Remove Child element from childNodes array. - * - * @param node Node to remove. + * @override */ public removeChild(node: Node): INode { return this.content.removeChild(node); } /** - * Inserts a node before another. - * - * @param newNode Node to insert. - * @param referenceNode Node to insert before. - * @returns Inserted node. + * @override */ public insertBefore(newNode: INode, referenceNode: INode): INode { return this.content.insertBefore(newNode, referenceNode); } /** - * Replaces a node with another. - * - * @param newChild New child. - * @param oldChild Old child. - * @returns Replaced node. + * @override */ public replaceChild(newChild: INode, oldChild: INode): INode { return this.content.replaceChild(newChild, oldChild); } /** - * Clones a node. - * * @override - * @param [deep=false] "true" to clone deep. - * @returns Cloned node. */ public cloneNode(deep = false): IHTMLTemplateElement { - return super.cloneNode(deep); + const clone = super.cloneNode(deep); + (clone.content) = this.content.cloneNode(deep); + return clone; } } diff --git a/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts new file mode 100644 index 000000000..22d3879c3 --- /dev/null +++ b/packages/happy-dom/test/nodes/html-template-element/HTMLTemplateElement.test.ts @@ -0,0 +1,29 @@ +import Window from '../../../src/window/Window'; +import Document from '../../../src/nodes/document/Document'; +import HTMLTemplateElement from '../../../src/nodes/html-template-element/HTMLTemplateElement'; + +describe('HTMLTemplateElement', () => { + let window: Window; + let document: Document; + let element: HTMLTemplateElement; + + beforeEach(() => { + window = new Window(); + document = window.document; + element = document.createElement('template'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('InnerHTML', () => { + const div = '
happy-dom is cool!
'; + expect(element.content.childNodes.length).toBe(0); + element.innerHTML = div; + expect(element.innerHTML).toBe(div); + expect(element.content.childNodes.length).toBe(1); + element.innerHTML = ''; + expect(element.content.childNodes.length).toBe(0); + }); +});