Skip to content

Commit

Permalink
#960@trivial: Improves performance of children and childNodes logic.
Browse files Browse the repository at this point in the history
  • Loading branch information
capricorn86 committed Aug 22, 2023
1 parent 23267c2 commit 5203972
Show file tree
Hide file tree
Showing 21 changed files with 255 additions and 212 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import INodeList from '../node/INodeList.js';
*/
export default class DocumentFragment extends Node implements IDocumentFragment {
public nodeType = Node.DOCUMENT_FRAGMENT_NODE;
public _children: IHTMLCollection<IElement> = new HTMLCollection();
public readonly _children: IHTMLCollection<IElement> = new HTMLCollection();
public _rootNode: INode = this;

/**
Expand All @@ -30,7 +30,7 @@ export default class DocumentFragment extends Node implements IDocumentFragment
* @returns Element.
*/
public get childElementCount(): number {
return this.children.length;
return this._children.length;
}

/**
Expand All @@ -39,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 @@ -48,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 @@ -58,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 @@ -72,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 @@ -145,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
44 changes: 22 additions & 22 deletions packages/happy-dom/src/nodes/document/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class Document extends Node implements IDocument {
public readonly isConnected: boolean = true;
public readonly defaultView: IWindow;
public readonly _readyStateManager: DocumentReadyStateManager;
public _children: IHTMLCollection<IElement> = new HTMLCollection<IElement>();
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 @@ -279,7 +279,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 @@ -288,7 +288,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 @@ -297,7 +297,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 @@ -342,7 +342,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 @@ -591,22 +591,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 @@ -622,9 +622,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 @@ -668,7 +668,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 @@ -683,7 +683,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 @@ -703,10 +703,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 @@ -715,7 +715,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 @@ -726,7 +726,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 @@ -738,7 +738,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 @@ -761,7 +761,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 5203972

Please sign in to comment.