Skip to content

Commit

Permalink
Merge pull request #961 from edoardocavazza/master
Browse files Browse the repository at this point in the history
#960@minor: Implement childNodes and children as getters.
  • Loading branch information
capricorn86 committed Aug 22, 2023
2 parents 9c92c1c + 28f9b72 commit 1f59807
Show file tree
Hide file tree
Showing 25 changed files with 326 additions and 222 deletions.
16 changes: 9 additions & 7 deletions packages/happy-dom/src/dom-parser/DOMParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import HTMLDocument from '../nodes/html-document/HTMLDocument.js';
import XMLDocument from '../nodes/xml-document/XMLDocument.js';
import SVGDocument from '../nodes/svg-document/SVGDocument.js';
import IWindow from '../window/IWindow.js';
import Document from '../nodes/document/Document.js';
import DocumentFragment from '../nodes/document-fragment/DocumentFragment.js';

/**
* DOM parser.
Expand Down Expand Up @@ -38,17 +40,17 @@ export default class DOMParser {
}

const ownerDocument = this._ownerDocument;
const newDocument = this._createDocument(mimeType);
const newDocument = <Document>this._createDocument(mimeType);

(<IWindow>newDocument.defaultView) = ownerDocument.defaultView;
newDocument.childNodes.length = 0;
newDocument.children.length = 0;
newDocument._childNodes.length = 0;
newDocument._children.length = 0;

const root = XMLParser.parse(newDocument, string, { evaluateScripts: true });
const root = <DocumentFragment>XMLParser.parse(newDocument, string, { evaluateScripts: true });
let documentElement = null;
let documentTypeNode = null;

for (const node of root.childNodes) {
for (const node of root._childNodes) {
if (node['tagName'] === 'HTML') {
documentElement = node;
} else if (node.nodeType === Node.DOCUMENT_TYPE_NODE) {
Expand All @@ -67,7 +69,7 @@ export default class DOMParser {
newDocument.appendChild(documentElement);
const body = newDocument.body;
if (body) {
for (const child of root.childNodes.slice()) {
for (const child of root._childNodes.slice()) {
body.appendChild(child);
}
}
Expand All @@ -80,7 +82,7 @@ export default class DOMParser {
documentElement.appendChild(bodyElement);
newDocument.appendChild(documentElement);

for (const node of root.childNodes.slice()) {
for (const node of root._childNodes.slice()) {
bodyElement.appendChild(node);
}
}
Expand Down
22 changes: 10 additions & 12 deletions packages/happy-dom/src/nodes/child-node/ChildNodeUtility.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DOMException from '../../exception/DOMException.js';
import XMLParser from '../../xml-parser/XMLParser.js';
import DocumentFragment from '../document-fragment/DocumentFragment.js';
import Document from '../document/Document.js';
import INode from '../node/INode.js';
import IParentNode from '../parent-node/IParentNode.js';
Expand Down Expand Up @@ -35,10 +36,9 @@ export default class ChildNodeUtility {

for (const node of nodes) {
if (typeof node === 'string') {
const newChildNodes = XMLParser.parse(
<Document>childNode.ownerDocument,
node
).childNodes.slice();
const newChildNodes = (<DocumentFragment>(
XMLParser.parse(<Document>childNode.ownerDocument, node)
))._childNodes.slice();
for (const newChildNode of newChildNodes) {
parent.insertBefore(newChildNode, childNode);
}
Expand All @@ -65,10 +65,9 @@ export default class ChildNodeUtility {

for (const node of nodes) {
if (typeof node === 'string') {
const newChildNodes = XMLParser.parse(
<Document>childNode.ownerDocument,
node
).childNodes.slice();
const newChildNodes = (<DocumentFragment>(
XMLParser.parse(<Document>childNode.ownerDocument, node)
))._childNodes.slice();
for (const newChildNode of newChildNodes) {
parent.insertBefore(newChildNode, childNode);
}
Expand All @@ -95,10 +94,9 @@ export default class ChildNodeUtility {

for (const node of nodes) {
if (typeof node === 'string') {
const newChildNodes = XMLParser.parse(
<Document>childNode.ownerDocument,
node
).childNodes.slice();
const newChildNodes = (<DocumentFragment>(
XMLParser.parse(<Document>childNode.ownerDocument, node)
))._childNodes.slice();
for (const newChildNode of newChildNodes) {
if (!nextSibling) {
parent.appendChild(newChildNode);
Expand Down
25 changes: 16 additions & 9 deletions packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ import INodeList from '../node/INodeList.js';
*/
export default class DocumentFragment extends Node implements IDocumentFragment {
public nodeType = Node.DOCUMENT_FRAGMENT_NODE;
public readonly children: IHTMLCollection<IElement> = new HTMLCollection();
public readonly _children: IHTMLCollection<IElement> = new HTMLCollection();
public _rootNode: INode = this;

/**
* Returns the document fragment children.
*/
public get children(): IHTMLCollection<IElement> {
return this._children;
}

/**
* Last element child.
*
* @returns Element.
*/
public get childElementCount(): number {
return this.children.length;
return this._children.length;
}

/**
Expand All @@ -32,7 +39,7 @@ export default class DocumentFragment extends Node implements IDocumentFragment
* @returns Element.
*/
public get firstElementChild(): IElement {
return this.children ? this.children[0] || null : null;
return this._children[0] ?? null;
}

/**
Expand All @@ -41,7 +48,7 @@ export default class DocumentFragment extends Node implements IDocumentFragment
* @returns Element.
*/
public get lastElementChild(): IElement {
return this.children ? this.children[this.children.length - 1] || null : null;
return this._children[this._children.length - 1] ?? null;
}

/**
Expand All @@ -51,7 +58,7 @@ export default class DocumentFragment extends Node implements IDocumentFragment
*/
public get textContent(): string {
let result = '';
for (const childNode of this.childNodes) {
for (const childNode of this._childNodes) {
if (childNode.nodeType === Node.ELEMENT_NODE || childNode.nodeType === Node.TEXT_NODE) {
result += childNode.textContent;
}
Expand All @@ -65,7 +72,7 @@ export default class DocumentFragment extends Node implements IDocumentFragment
* @param textContent Text content.
*/
public set textContent(textContent: string) {
for (const child of this.childNodes.slice()) {
for (const child of this._childNodes.slice()) {
this.removeChild(child);
}
if (textContent) {
Expand Down Expand Up @@ -138,12 +145,12 @@ export default class DocumentFragment extends Node implements IDocumentFragment
* @returns Cloned node.
*/
public cloneNode(deep = false): IDocumentFragment {
const clone = <IDocumentFragment>super.cloneNode(deep);
const clone = <DocumentFragment>super.cloneNode(deep);

if (deep) {
for (const node of clone.childNodes) {
for (const node of clone._childNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
clone.children.push(<IElement>node);
clone._children.push(<IElement>node);
}
}
}
Expand Down
51 changes: 29 additions & 22 deletions packages/happy-dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ export default class Document extends Node implements IDocument {
public nodeType = Node.DOCUMENT_NODE;
public adoptedStyleSheets: CSSStyleSheet[] = [];
public implementation: DOMImplementation;
public readonly children: IHTMLCollection<IElement> = new HTMLCollection<IElement>();
public readonly readyState = DocumentReadyStateEnum.interactive;
public readonly isConnected: boolean = true;
public readonly defaultView: IWindow;
public readonly _windowClass: {} | null = null;
public readonly _readyStateManager: DocumentReadyStateManager;
public readonly _children: IHTMLCollection<IElement> = new HTMLCollection<IElement>();
public _activeElement: IHTMLElement = null;

// Used as an unique identifier which is updated whenever the DOM gets modified.
Expand Down Expand Up @@ -215,6 +215,13 @@ export default class Document extends Node implements IDocument {
documentElement.appendChild(bodyElement);
}

/**
* Returns document children.
*/
public get children(): IHTMLCollection<IElement> {
return this._children;
}

/**
* Returns character set.
*
Expand Down Expand Up @@ -276,7 +283,7 @@ export default class Document extends Node implements IDocument {
* @returns Element.
*/
public get childElementCount(): number {
return this.children.length;
return this._children.length;
}

/**
Expand All @@ -285,7 +292,7 @@ export default class Document extends Node implements IDocument {
* @returns Element.
*/
public get firstElementChild(): IElement {
return this.children ? this.children[0] || null : null;
return this._children[0] ?? null;
}

/**
Expand All @@ -294,7 +301,7 @@ export default class Document extends Node implements IDocument {
* @returns Element.
*/
public get lastElementChild(): IElement {
return this.children ? this.children[this.children.length - 1] || null : null;
return this._children[this._children.length - 1] ?? null;
}

/**
Expand Down Expand Up @@ -339,7 +346,7 @@ export default class Document extends Node implements IDocument {
* @returns Document type.
*/
public get doctype(): IDocumentType {
for (const node of this.childNodes) {
for (const node of this._childNodes) {
if (node instanceof DocumentType) {
return node;
}
Expand Down Expand Up @@ -588,22 +595,22 @@ export default class Document extends Node implements IDocument {
* @param name
*/
public getElementsByName(name: string): INodeList<IElement> {
const _getElementsByName = (
_parentNode: IElement | IDocumentFragment | IDocument,
_name: string
const getElementsByName = (
parentNode: IElement | IDocumentFragment | IDocument,
name: string
): INodeList<IElement> => {
const matches = new NodeList<IElement>();
for (const child of _parentNode.children) {
if (child.getAttributeNS(null, 'name') === _name) {
for (const child of (<Element | Document>parentNode)._children) {
if (child.getAttributeNS(null, 'name') === name) {
matches.push(child);
}
for (const match of _getElementsByName(<IElement>child, _name)) {
for (const match of getElementsByName(<IElement>child, name)) {
matches.push(match);
}
}
return matches;
};
return _getElementsByName(this, name);
return getElementsByName(this, name);
}

/**
Expand All @@ -619,9 +626,9 @@ export default class Document extends Node implements IDocument {
const clone = <Document>super.cloneNode(deep);

if (deep) {
for (const node of clone.childNodes) {
for (const node of clone._childNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
clone.children.push(<IElement>node);
clone._children.push(<IElement>node);
}
}
}
Expand Down Expand Up @@ -665,7 +672,7 @@ export default class Document extends Node implements IDocument {
* @param html HTML.
*/
public write(html: string): void {
const root = XMLParser.parse(this, html, { evaluateScripts: true });
const root = <DocumentFragment>XMLParser.parse(this, html, { evaluateScripts: true });

if (this._isFirstWrite || this._isFirstWriteAfterOpen) {
if (this._isFirstWrite) {
Expand All @@ -680,7 +687,7 @@ export default class Document extends Node implements IDocument {
let documentElement = null;
let documentTypeNode = null;

for (const node of root.childNodes) {
for (const node of root._childNodes) {
if (node['tagName'] === 'HTML') {
documentElement = node;
} else if (node.nodeType === NodeTypeEnum.documentTypeNode) {
Expand All @@ -700,10 +707,10 @@ export default class Document extends Node implements IDocument {

this.appendChild(documentElement);
} else {
const rootBody = ParentNodeUtility.getElementByTagName(root, 'body');
const rootBody = <Element>ParentNodeUtility.getElementByTagName(root, 'body');
const body = ParentNodeUtility.getElementByTagName(this, 'body');
if (rootBody && body) {
for (const child of rootBody.childNodes.slice()) {
for (const child of rootBody._childNodes.slice()) {
body.appendChild(child);
}
}
Expand All @@ -712,7 +719,7 @@ export default class Document extends Node implements IDocument {
// Remaining nodes outside the <html> element are added to the <body> element.
const body = ParentNodeUtility.getElementByTagName(this, 'body');
if (body) {
for (const child of root.childNodes.slice()) {
for (const child of root._childNodes.slice()) {
if (child['tagName'] !== 'HTML' && child.nodeType !== NodeTypeEnum.documentTypeNode) {
body.appendChild(child);
}
Expand All @@ -723,7 +730,7 @@ export default class Document extends Node implements IDocument {
const bodyElement = this.createElement('body');
const headElement = this.createElement('head');

for (const child of root.childNodes.slice()) {
for (const child of root._childNodes.slice()) {
bodyElement.appendChild(child);
}

Expand All @@ -735,7 +742,7 @@ export default class Document extends Node implements IDocument {
} else {
const bodyNode = ParentNodeUtility.getElementByTagName(root, 'body');
const body = ParentNodeUtility.getElementByTagName(this, 'body');
for (const child of (bodyNode || root).childNodes.slice()) {
for (const child of (<Element>(bodyNode || root))._childNodes.slice()) {
body.appendChild(child);
}
}
Expand All @@ -758,7 +765,7 @@ export default class Document extends Node implements IDocument {
}
}

for (const child of this.childNodes.slice()) {
for (const child of this._childNodes.slice()) {
this.removeChild(child);
}

Expand Down

0 comments on commit 1f59807

Please sign in to comment.