Repository: capricorn86/happy-dom
Pull request #844 (2023-04-07 11:20:09)

Task/840 v9 not support name attr consisting of pure numbers


Patch:
diff --git a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
index 0ec0fe3f..95cfe0e7 100644
--- a/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
+++ b/packages/happy-dom/src/nodes/document-fragment/DocumentFragment.ts
@@ -14,7 +14,7 @@ import INodeList from '../node/INodeList';
  */
 export default class DocumentFragment extends Node implements IDocumentFragment {
 	public nodeType = Node.DOCUMENT_FRAGMENT_NODE;
-	public readonly children: IHTMLCollection<IElement, IElement> = new HTMLCollection();
+	public readonly children: IHTMLCollection<IElement> = new HTMLCollection();
 	public _rootNode: INode = this;
 
 	/**
diff --git a/packages/happy-dom/src/nodes/document/Document.ts b/packages/happy-dom/src/nodes/document/Document.ts
index 1b6809c6..df1b717b 100644
--- a/packages/happy-dom/src/nodes/document/Document.ts
+++ b/packages/happy-dom/src/nodes/document/Document.ts
@@ -57,10 +57,7 @@ export default class Document extends Node implements IDocument {
 	public nodeType = Node.DOCUMENT_NODE;
 	public adoptedStyleSheets: CSSStyleSheet[] = [];
 	public implementation: DOMImplementation;
-	public readonly children: IHTMLCollection<IElement, IElement> = new HTMLCollection<
-		IElement,
-		IElement
-	>();
+	public readonly children: IHTMLCollection<IElement> = new HTMLCollection<IElement>();
 	public readonly readyState = DocumentReadyStateEnum.interactive;
 	public readonly isConnected: boolean = true;
 	public readonly defaultView: IWindow;
@@ -421,10 +418,8 @@ export default class Document extends Node implements IDocument {
 	 *
 	 * @returns Scripts.
 	 */
-	public get scripts(): IHTMLCollection<IHTMLScriptElement, IHTMLScriptElement> {
-		return <IHTMLCollection<IHTMLScriptElement, IHTMLScriptElement>>(
-			this.getElementsByTagName('script')
-		);
+	public get scripts(): IHTMLCollection<IHTMLScriptElement> {
+		return <IHTMLCollection<IHTMLScriptElement>>this.getElementsByTagName('script');
 	}
 
 	/**
@@ -538,7 +533,7 @@ export default class Document extends Node implements IDocument {
 	 * @param className Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByClassName(className: string): IHTMLCollection<IElement, IElement> {
+	public getElementsByClassName(className: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByClassName(this, className);
 	}
 
@@ -548,7 +543,7 @@ export default class Document extends Node implements IDocument {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByTagName(tagName: string): IHTMLCollection<IElement, IElement> {
+	public getElementsByTagName(tagName: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByTagName(this, tagName);
 	}
 
@@ -559,10 +554,7 @@ export default class Document extends Node implements IDocument {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByTagNameNS(
-		namespaceURI: string,
-		tagName: string
-	): IHTMLCollection<IElement, IElement> {
+	public getElementsByTagNameNS(namespaceURI: string, tagName: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByTagNameNS(this, namespaceURI, tagName);
 	}
 
diff --git a/packages/happy-dom/src/nodes/document/IDocument.ts b/packages/happy-dom/src/nodes/document/IDocument.ts
index 39aa48ac..b345a341 100644
--- a/packages/happy-dom/src/nodes/document/IDocument.ts
+++ b/packages/happy-dom/src/nodes/document/IDocument.ts
@@ -32,7 +32,7 @@ export default interface IDocument extends IParentNode {
 	readonly doctype: IDocumentType;
 	readonly body: IHTMLElement;
 	readonly head: IHTMLElement;
-	readonly scripts: IHTMLCollection<IHTMLScriptElement, IHTMLScriptElement>;
+	readonly scripts: IHTMLCollection<IHTMLScriptElement>;
 	readonly activeElement: IHTMLElement;
 	readonly styleSheets: CSSStyleSheet[];
 	readonly scrollingElement: IHTMLElement;
diff --git a/packages/happy-dom/src/nodes/element/Element.ts b/packages/happy-dom/src/nodes/element/Element.ts
index 392d708e..65d0159c 100644
--- a/packages/happy-dom/src/nodes/element/Element.ts
+++ b/packages/happy-dom/src/nodes/element/Element.ts
@@ -46,7 +46,7 @@ export default class Element extends Node implements IElement {
 
 	public scrollTop = 0;
 	public scrollLeft = 0;
-	public children: IHTMLCollection<IElement, IElement> = new HTMLCollection<IElement, IElement>();
+	public children: IHTMLCollection<IElement> = new HTMLCollection<IElement>();
 	public readonly namespaceURI: string = null;
 
 	// Events
@@ -778,7 +778,7 @@ export default class Element extends Node implements IElement {
 	 * @param className Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByClassName(className: string): IHTMLCollection<IElement, IElement> {
+	public getElementsByClassName(className: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByClassName(this, className);
 	}
 
@@ -788,7 +788,7 @@ export default class Element extends Node implements IElement {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByTagName(tagName: string): IHTMLCollection<IElement, IElement> {
+	public getElementsByTagName(tagName: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByTagName(this, tagName);
 	}
 
@@ -799,10 +799,7 @@ export default class Element extends Node implements IElement {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	public getElementsByTagNameNS(
-		namespaceURI: string,
-		tagName: string
-	): IHTMLCollection<IElement, IElement> {
+	public getElementsByTagNameNS(namespaceURI: string, tagName: string): IHTMLCollection<IElement> {
 		return ParentNodeUtility.getElementsByTagNameNS(this, namespaceURI, tagName);
 	}
 
@@ -834,14 +831,16 @@ export default class Element extends Node implements IElement {
 		if (attribute.name === 'id' || attribute.name === 'name') {
 			if (this.parentNode && (<IElement>this.parentNode).children && attribute.value !== oldValue) {
 				if (oldValue) {
-					(<HTMLCollection<IElement, IElement>>(
-						(<IElement>this.parentNode).children
-					))._removeNamedItem(this, oldValue);
+					(<HTMLCollection<IElement>>(<IElement>this.parentNode).children)._removeNamedItem(
+						this,
+						oldValue
+					);
 				}
 				if (attribute.value) {
-					(<HTMLCollection<IElement, IElement>>(
-						(<IElement>this.parentNode).children
-					))._appendNamedItem(this, attribute.value);
+					(<HTMLCollection<IElement>>(<IElement>this.parentNode).children)._appendNamedItem(
+						this,
+						attribute.value
+					);
 				}
 			}
 		}
@@ -946,7 +945,7 @@ export default class Element extends Node implements IElement {
 
 		if (attribute.name === 'id' || attribute.name === 'name') {
 			if (this.parentNode && (<IElement>this.parentNode).children && attribute.value) {
-				(<HTMLCollection<IElement, IElement>>(<IElement>this.parentNode).children)._removeNamedItem(
+				(<HTMLCollection<IElement>>(<IElement>this.parentNode).children)._removeNamedItem(
 					this,
 					attribute.value
 				);
diff --git a/packages/happy-dom/src/nodes/element/ElementUtility.ts b/packages/happy-dom/src/nodes/element/ElementUtility.ts
index a30ce7c1..92395425 100644
--- a/packages/happy-dom/src/nodes/element/ElementUtility.ts
+++ b/packages/happy-dom/src/nodes/element/ElementUtility.ts
@@ -29,9 +29,10 @@ export default class ElementUtility {
 				if (index !== -1) {
 					for (const attribute of NAMED_ITEM_ATTRIBUTES) {
 						if ((<Element>node)._attributes[attribute]) {
-							(<HTMLCollection<IHTMLElement, IHTMLElement>>(
-								(<IElement>node.parentNode).children
-							))._removeNamedItem(<IHTMLElement>node, (<Element>node)._attributes[attribute].value);
+							(<HTMLCollection<IHTMLElement>>(<IElement>node.parentNode).children)._removeNamedItem(
+								<IHTMLElement>node,
+								(<Element>node)._attributes[attribute].value
+							);
 						}
 					}
 					(<IHTMLElement>node.parentNode).children.splice(index, 1);
@@ -40,7 +41,7 @@ export default class ElementUtility {
 
 			for (const attribute of NAMED_ITEM_ATTRIBUTES) {
 				if ((<Element>node)._attributes[attribute]) {
-					(<HTMLCollection<IHTMLElement, IHTMLElement>>parentElement.children)._appendNamedItem(
+					(<HTMLCollection<IHTMLElement>>parentElement.children)._appendNamedItem(
 						<IHTMLElement>node,
 						(<Element>node)._attributes[attribute].value
 					);
@@ -66,7 +67,7 @@ export default class ElementUtility {
 			if (index !== -1) {
 				for (const attribute of NAMED_ITEM_ATTRIBUTES) {
 					if ((<Element>node)._attributes[attribute]) {
-						(<HTMLCollection<IHTMLElement, IHTMLElement>>parentElement.children)._removeNamedItem(
+						(<HTMLCollection<IHTMLElement>>parentElement.children)._removeNamedItem(
 							<IHTMLElement>node,
 							(<Element>node)._attributes[attribute].value
 						);
@@ -96,7 +97,7 @@ export default class ElementUtility {
 				if (index !== -1) {
 					for (const attribute of NAMED_ITEM_ATTRIBUTES) {
 						if ((<Element>newNode)._attributes[attribute]) {
-							(<HTMLCollection<IHTMLElement, IHTMLElement>>(
+							(<HTMLCollection<IHTMLElement>>(
 								(<IElement>newNode.parentNode).children
 							))._removeNamedItem(
 								<IHTMLElement>newNode,
@@ -132,7 +133,7 @@ export default class ElementUtility {
 			if (referenceNode || referenceNode === null) {
 				for (const attribute of NAMED_ITEM_ATTRIBUTES) {
 					if ((<Element>newNode)._attributes[attribute]) {
-						(<HTMLCollection<IHTMLElement, IHTMLElement>>parentElement.children)._appendNamedItem(
+						(<HTMLCollection<IHTMLElement>>parentElement.children)._appendNamedItem(
 							<IHTMLElement>newNode,
 							(<Element>newNode)._attributes[attribute].value
 						);
diff --git a/packages/happy-dom/src/nodes/element/HTMLCollection.ts b/packages/happy-dom/src/nodes/element/HTMLCollection.ts
index 96afc317..f2670a80 100644
--- a/packages/happy-dom/src/nodes/element/HTMLCollection.ts
+++ b/packages/happy-dom/src/nodes/element/HTMLCollection.ts
@@ -1,12 +1,9 @@
 import IHTMLCollection from './IHTMLCollection';
 
 /**
- * Class list.
+ * HTML collection.
  */
-export default class HTMLCollection<T, NamedItem>
-	extends Array
-	implements IHTMLCollection<T, NamedItem>
-{
+export default class HTMLCollection<T> extends Array implements IHTMLCollection<T> {
 	protected _namedItems: { [k: string]: T[] } = {};
 
 	/**
@@ -24,8 +21,10 @@ export default class HTMLCollection<T, NamedItem>
 	 * @param name Name.
 	 * @returns Node.
 	 */
-	public namedItem(name: string): NamedItem | null {
-		return this[name] || null;
+	public namedItem(name: string): T | null {
+		return this._namedItems[name] && this._namedItems[name].length
+			? this._namedItems[name][0]
+			: null;
 	}
 
 	/**
@@ -42,7 +41,7 @@ export default class HTMLCollection<T, NamedItem>
 				this._namedItems[name].push(node);
 			}
 
-			if (!this[name]) {
+			if (!this[name] && this._isValidPropertyName(name)) {
 				this[name] = this._namedItems[name][0];
 			}
 		}
@@ -63,11 +62,23 @@ export default class HTMLCollection<T, NamedItem>
 
 				if (this._namedItems[name].length === 0) {
 					delete this._namedItems[name];
-					delete this[name];
-				} else {
+					if (this._isValidPropertyName(name)) {
+						delete this[name];
+					}
+				} else if (this._isValidPropertyName(name)) {
 					this[name] = this._namedItems[name][0];
 				}
 			}
 		}
 	}
+
+	/**
+	 * Returns "true" if the property name is valid.
+	 *
+	 * @param name Name.
+	 * @returns True if the property name is valid.
+	 */
+	protected _isValidPropertyName(name: string): boolean {
+		return isNaN(Number(name)) || name.includes('.');
+	}
 }
diff --git a/packages/happy-dom/src/nodes/element/IHTMLCollection.ts b/packages/happy-dom/src/nodes/element/IHTMLCollection.ts
index d7c3520e..fe8ab1e7 100644
--- a/packages/happy-dom/src/nodes/element/IHTMLCollection.ts
+++ b/packages/happy-dom/src/nodes/element/IHTMLCollection.ts
@@ -1,7 +1,7 @@
 /**
  * HTMLCollection.
  */
-export default interface IHTMLCollection<T, NamedItem> extends Array<T> {
+export default interface IHTMLCollection<T> extends Array<T> {
 	/**
 	 * Returns item by index.
 	 *
@@ -15,5 +15,5 @@ export default interface IHTMLCollection<T, NamedItem> extends Array<T> {
 	 * @param name Name.
 	 * @returns Node.
 	 */
-	namedItem(name: string): NamedItem | null;
+	namedItem(name: string): T | null;
 }
diff --git a/packages/happy-dom/src/nodes/html-form-element/HTMLFormControlsCollection.ts b/packages/happy-dom/src/nodes/html-form-element/HTMLFormControlsCollection.ts
index ad6631aa..39b9a281 100644
--- a/packages/happy-dom/src/nodes/html-form-element/HTMLFormControlsCollection.ts
+++ b/packages/happy-dom/src/nodes/html-form-element/HTMLFormControlsCollection.ts
@@ -2,10 +2,8 @@ import IHTMLFormControlsCollection from './IHTMLFormControlsCollection';
 import IHTMLInputElement from '../html-input-element/IHTMLInputElement';
 import IHTMLTextAreaElement from '../html-text-area-element/IHTMLTextAreaElement';
 import IHTMLSelectElement from '../html-select-element/IHTMLSelectElement';
-import HTMLCollection from '../element/HTMLCollection';
 import RadioNodeList from './RadioNodeList';
 import IHTMLButtonElement from '../html-button-element/IHTMLButtonElement';
-import IRadioNodeList from './IRadioNodeList';
 
 /**
  * HTMLFormControlsCollection.
@@ -13,17 +11,45 @@ import IRadioNodeList from './IRadioNodeList';
  * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection
  */
 export default class HTMLFormControlsCollection
-	extends HTMLCollection<
-		IHTMLInputElement | IHTMLTextAreaElement | IHTMLSelectElement | IHTMLButtonElement,
+	extends Array<IHTMLInputElement | IHTMLTextAreaElement | IHTMLSelectElement | IHTMLButtonElement>
+	implements IHTMLFormControlsCollection
+{
+	public _namedItems: { [k: string]: RadioNodeList } = {};
+
+	/**
+	 * Returns item by index.
+	 *
+	 * @param index Index.
+	 */
+	public item(
+		index: number
+	): IHTMLInputElement | IHTMLTextAreaElement | IHTMLSelectElement | IHTMLButtonElement | null {
+		return index >= 0 && this[index] ? this[index] : null;
+	}
+
+	/**
+	 * Returns named item.
+	 *
+	 * @param name Name.
+	 * @returns Node.
+	 */
+	public namedItem(
+		name: string
+	):
 		| IHTMLInputElement
 		| IHTMLTextAreaElement
 		| IHTMLSelectElement
 		| IHTMLButtonElement
-		| IRadioNodeList
-	>
-	implements IHTMLFormControlsCollection
-{
-	public _namedItems: { [k: string]: RadioNodeList } = {};
+		| RadioNodeList
+		| null {
+		if (this._namedItems[name] && this._namedItems[name].length) {
+			if (this._namedItems[name].length === 1) {
+				return this._namedItems[name][0];
+			}
+			return this._namedItems[name];
+		}
+		return null;
+	}
 
 	/**
 	 * Appends named item.
@@ -42,8 +68,10 @@ export default class HTMLFormControlsCollection
 				this._namedItems[name].push(node);
 			}
 
-			this[name] =
-				this._namedItems[name].length > 1 ? this._namedItems[name] : this._namedItems[name][0];
+			if (this._isValidPropertyName(name)) {
+				this[name] =
+					this._namedItems[name].length > 1 ? this._namedItems[name] : this._namedItems[name][0];
+			}
 		}
 	}
 
@@ -65,12 +93,24 @@ export default class HTMLFormControlsCollection
 
 				if (this._namedItems[name].length === 0) {
 					delete this._namedItems[name];
-					delete this[name];
-				} else {
+					if (this._isValidPropertyName(name)) {
+						delete this[name];
+					}
+				} else if (this._isValidPropertyName(name)) {
 					this[name] =
 						this._namedItems[name].length > 1 ? this._namedItems[name] : this._namedItems[name][0];
 				}
 			}
 		}
 	}
+
+	/**
+	 * Returns "true" if the property name is valid.
+	 *
+	 * @param name Name.
+	 * @returns True if the property name is valid.
+	 */
+	protected _isValidPropertyName(name: string): boolean {
+		return isNaN(Number(name)) || name.includes('.');
+	}
 }
diff --git a/packages/happy-dom/src/nodes/html-form-element/IHTMLFormControlsCollection.ts b/packages/happy-dom/src/nodes/html-form-element/IHTMLFormControlsCollection.ts
index 00f4f880..4cb9fc2d 100644
--- a/packages/happy-dom/src/nodes/html-form-element/IHTMLFormControlsCollection.ts
+++ b/packages/happy-dom/src/nodes/html-form-element/IHTMLFormControlsCollection.ts
@@ -1,4 +1,3 @@
-import IHTMLCollection from '../element/IHTMLCollection';
 import IHTMLButtonElement from '../html-button-element/IHTMLButtonElement';
 import IHTMLInputElement from '../html-input-element/IHTMLInputElement';
 import IHTMLSelectElement from '../html-select-element/IHTMLSelectElement';
@@ -11,13 +10,8 @@ import IRadioNodeList from './IRadioNodeList';
  * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormControlsCollection
  */
 export default interface IHTMLFormControlsCollection
-	extends IHTMLCollection<
-		IHTMLInputElement | IHTMLTextAreaElement | IHTMLSelectElement | IHTMLButtonElement,
-		| IHTMLInputElement
-		| IHTMLTextAreaElement
-		| IHTMLSelectElement
-		| IHTMLButtonElement
-		| IRadioNodeList
+	extends Array<
+		IHTMLInputElement | IHTMLTextAreaElement | IHTMLSelectElement | IHTMLButtonElement
 	> {
 	/**
 	 * Returns item by index.
diff --git a/packages/happy-dom/src/nodes/html-select-element/HTMLOptionsCollection.ts b/packages/happy-dom/src/nodes/html-select-element/HTMLOptionsCollection.ts
index 36c99ba6..8805fe73 100644
--- a/packages/happy-dom/src/nodes/html-select-element/HTMLOptionsCollection.ts
+++ b/packages/happy-dom/src/nodes/html-select-element/HTMLOptionsCollection.ts
@@ -11,7 +11,7 @@ import IHTMLOptionsCollection from './IHTMLOptionsCollection';
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionsCollection.
  */
 export default class HTMLOptionsCollection
-	extends HTMLCollection<IHTMLOptionElement, IHTMLOptionElement>
+	extends HTMLCollection<IHTMLOptionElement>
 	implements IHTMLOptionsCollection
 {
 	private _selectElement: IHTMLSelectElement;
diff --git a/packages/happy-dom/src/nodes/html-select-element/HTMLSelectElement.ts b/packages/happy-dom/src/nodes/html-select-element/HTMLSelectElement.ts
index 7195e4d6..f3291ee3 100644
--- a/packages/happy-dom/src/nodes/html-select-element/HTMLSelectElement.ts
+++ b/packages/happy-dom/src/nodes/html-select-element/HTMLSelectElement.ts
@@ -342,9 +342,7 @@ export default class HTMLSelectElement extends HTMLElement implements IHTMLSelec
 	 * @param [selectedOption] Selected option.
 	 */
 	public _updateOptionItems(selectedOption?: IHTMLOptionElement): void {
-		const optionElements = <IHTMLCollection<IHTMLOptionElement, IHTMLOptionElement>>(
-			this.getElementsByTagName('option')
-		);
+		const optionElements = <IHTMLCollection<IHTMLOptionElement>>this.getElementsByTagName('option');
 
 		if (optionElements.length < this.options.length) {
 			this.options.splice(this.options.length - 1, this.options.length - optionElements.length);
diff --git a/packages/happy-dom/src/nodes/html-select-element/IHTMLOptionsCollection.ts b/packages/happy-dom/src/nodes/html-select-element/IHTMLOptionsCollection.ts
index e14f72a1..d2a7251a 100644
--- a/packages/happy-dom/src/nodes/html-select-element/IHTMLOptionsCollection.ts
+++ b/packages/happy-dom/src/nodes/html-select-element/IHTMLOptionsCollection.ts
@@ -7,8 +7,7 @@ import IHTMLOptionElement from '../html-option-element/IHTMLOptionElement';
  * Reference:
  * https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionsCollection.
  */
-export default interface IHTMLOptionsCollection
-	extends IHTMLCollection<IHTMLOptionElement, IHTMLOptionElement> {
+export default interface IHTMLOptionsCollection extends IHTMLCollection<IHTMLOptionElement> {
 	selectedIndex: number;
 	length: number;
 
diff --git a/packages/happy-dom/src/nodes/parent-node/IParentNode.ts b/packages/happy-dom/src/nodes/parent-node/IParentNode.ts
index ed7f453b..ef6cd3b8 100644
--- a/packages/happy-dom/src/nodes/parent-node/IParentNode.ts
+++ b/packages/happy-dom/src/nodes/parent-node/IParentNode.ts
@@ -7,7 +7,7 @@ export default interface IParentNode extends INode {
 	readonly childElementCount: number;
 	readonly firstElementChild: IElement;
 	readonly lastElementChild: IElement;
-	readonly children: IHTMLCollection<IElement, IElement>;
+	readonly children: IHTMLCollection<IElement>;
 
 	/**
 	 * Inserts a set of Node objects or DOMString objects after the last child of the ParentNode. DOMString objects are inserted as equivalent Text nodes.
@@ -45,7 +45,7 @@ export default interface IParentNode extends INode {
 	 * @param className Tag name.
 	 * @returns Matching element.
 	 */
-	getElementsByClassName(className: string): IHTMLCollection<IElement, IElement>;
+	getElementsByClassName(className: string): IHTMLCollection<IElement>;
 
 	/**
 	 * Returns an elements by tag name.
@@ -53,7 +53,7 @@ export default interface IParentNode extends INode {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	getElementsByTagName(tagName: string): IHTMLCollection<IElement, IElement>;
+	getElementsByTagName(tagName: string): IHTMLCollection<IElement>;
 
 	/**
 	 * Returns an elements by tag name and namespace.
@@ -62,10 +62,7 @@ export default interface IParentNode extends INode {
 	 * @param tagName Tag name.
 	 * @returns Matching element.
 	 */
-	getElementsByTagNameNS(
-		namespaceURI: string,
-		tagName: string
-	): IHTMLCollection<IElement, IElement>;
+	getElementsByTagNameNS(namespaceURI: string, tagName: string): IHTMLCollection<IElement>;
 
 	/**
 	 * Replaces the existing children of a node with a specified new set of children.
diff --git a/packages/happy-dom/src/nodes/parent-node/ParentNodeUtility.ts b/packages/happy-dom/src/nodes/parent-node/ParentNodeUtility.ts
index 461811c9..b3bc54b8 100644
--- a/packages/happy-dom/src/nodes/parent-node/ParentNodeUtility.ts
+++ b/packages/happy-dom/src/nodes/parent-node/ParentNodeUtility.ts
@@ -79,14 +79,14 @@ export default class ParentNodeUtility {
 	public static getElementsByClassName(
 		parentNode: IElement | IDocumentFragment | IDocument,
 		className: string
-	): IHTMLCollection<IElement, IElement> {
-		let matches = new HTMLCollection<IElement, IElement>();
+	): IHTMLCollection<IElement> {
+		let matches = new HTMLCollection<IElement>();
 
 		for (const child of parentNode.children) {
 			if (child.className.split(' ').includes(className)) {
 				matches.push(child);
 			}
-			matches = <HTMLCollection<IElement, IElement>>(
+			matches = <HTMLCollection<IElement>>(
 				matches.concat(this.getElementsByClassName(<IElement>child, className))
 			);
 		}
@@ -104,16 +104,16 @@ export default class ParentNodeUtility {
 	public static getElementsByTagName(
 		parentNode: IElement | IDocumentFragment | IDocument,
 		tagName: string
-	): IHTMLCollection<IElement, IElement> {
+	): IHTMLCollection<IElement> {
 		const upperTagName = tagName.toUpperCase();
 		const includeAll = tagName === '*';
-		let matches = new HTMLCollection<IElement, IElement>();
+		let matches = new HTMLCollection<IElement>();
 
 		for (const child of parentNode.children) {
 			if (includeAll || child.tagName === upperTagName) {
 				matches.push(child);
 			}
-			matches = <HTMLCollection<IElement, IElement>>(
+			matches = <HTMLCollection<IElement>>(
 				matches.concat(this.getElementsByTagName(<IElement>child, tagName))
 			);
 		}
@@ -133,16 +133,16 @@ export default class ParentNodeUtility {
 		parentNode: IElement | IDocumentFragment | IDocument,
 		namespaceURI: string,
 		tagName: string
-	): IHTMLCollection<IElement, IElement> {
+	): IHTMLCollection<IElement> {
 		const upperTagName = tagName.toUpperCase();
 		const includeAll = tagName === '*';
-		let matches = new HTMLCollection<IElement, IElement>();
+		let matches = new HTMLCollection<IElement>();
 
 		for (const child of parentNode.children) {
 			if ((includeAll || child.tagName === upperTagName) && child.namespaceURI === namespaceURI) {
 				matches.push(child);
 			}
-			matches = <HTMLCollection<IElement, IElement>>(
+			matches = <HTMLCollection<IElement>>(
 				matches.concat(this.getElementsByTagNameNS(<IElement>child, namespaceURI, tagName))
 			);
 		}


Test patch:
diff --git a/packages/happy-dom/test/nodes/document/Document.test.ts b/packages/happy-dom/test/nodes/document/Document.test.ts
index 1d4301f9..d893cc3d 100644
--- a/packages/happy-dom/test/nodes/document/Document.test.ts
+++ b/packages/happy-dom/test/nodes/document/Document.test.ts
@@ -557,7 +557,7 @@ describe('Document', () => {
 				.mockImplementation((parentNode, requestedClassName) => {
 					expect(parentNode === document).toBe(true);
 					expect(requestedClassName).toEqual(className);
-					return <IHTMLCollection<IElement, IElement>>[element];
+					return <IHTMLCollection<IElement>>[element];
 				});
 
 			const result = document.getElementsByClassName(className);
@@ -576,7 +576,7 @@ describe('Document', () => {
 				.mockImplementation((parentNode, requestedTagName) => {
 					expect(parentNode === document).toBe(true);
 					expect(requestedTagName).toEqual(tagName);
-					return <IHTMLCollection<IElement, IElement>>[element];
+					return <IHTMLCollection<IElement>>[element];
 				});
 
 			const result = document.getElementsByTagName(tagName);
@@ -597,7 +597,7 @@ describe('Document', () => {
 					expect(parentNode === document).toBe(true);
 					expect(requestedNamespaceURI).toEqual(namespaceURI);
 					expect(requestedTagName).toEqual(tagName);
-					return <IHTMLCollection<IElement, IElement>>[element];
+					return <IHTMLCollection<IElement>>[element];
 				});
 
 			const result = document.getElementsByTagNameNS(namespaceURI, tagName);
diff --git a/packages/happy-dom/test/nodes/element/Element.test.ts b/packages/happy-dom/test/nodes/element/Element.test.ts
index 335aa3e4..a6226188 100644
--- a/packages/happy-dom/test/nodes/element/Element.test.ts
+++ b/packages/happy-dom/test/nodes/element/Element.test.ts
@@ -837,7 +837,7 @@ describe('Element', () => {
 				.mockImplementation((parentNode, requestedClassName) => {
 					expect(parentNode).toBe(element);
 					expect(requestedClassName).toEqual(className);
-					return <IHTMLCollection<IElement, IElement>>[child];
+					return <IHTMLCollection<IElement>>[child];
 				});
 
 			const result = element.getElementsByClassName(className);
@@ -856,7 +856,7 @@ describe('Element', () => {
 				.mockImplementation((parentNode, requestedTagName) => {
 					expect(parentNode).toBe(element);
 					expect(requestedTagName).toEqual(tagName);
-					return <IHTMLCollection<IElement, IElement>>[child];
+					return <IHTMLCollection<IElement>>[child];
 				});
 
 			const result = element.getElementsByTagName(tagName);
@@ -877,7 +877,7 @@ describe('Element', () => {
 					expect(parentNode).toBe(element);
 					expect(requestedNamespaceURI).toEqual(namespaceURI);
 					expect(requestedTagName).toEqual(tagName);
-					return <IHTMLCollection<IElement, IElement>>[child];
+					return <IHTMLCollection<IElement>>[child];
 				});
 
 			const result = element.getElementsByTagNameNS(namespaceURI, tagName);
diff --git a/packages/happy-dom/test/nodes/element/HTMLCollection.test.ts b/packages/happy-dom/test/nodes/element/HTMLCollection.test.ts
index 8501c32b..fb150ece 100644
--- a/packages/happy-dom/test/nodes/element/HTMLCollection.test.ts
+++ b/packages/happy-dom/test/nodes/element/HTMLCollection.test.ts
@@ -75,5 +75,47 @@ describe('HTMLCollection', () => {
 			expect(document.body.children.namedItem('div3') === null).toBe(true);
 			expect(document.body.children.namedItem('div5') === div5).toBe(true);
 		});
+
+		it('Supports attributes only consisting of numbers.', () => {
+			const div = document.createElement('div');
+			div.innerHTML = `<div name="container1" class="container1"></div><div name="container2" class="container2"></div><div name="0" class="container3"></div><div name="1" class="container4"></div>`;
+			const container1 = div.querySelector('.container1');
+			const container2 = div.querySelector('.container2');
+			const container3 = div.querySelector('.container3');
+			const container4 = div.querySelector('.container4');
+
+			expect(div.children.length).toBe(4);
+			expect(div.children[0] === container1).toBe(true);
+			expect(div.children[1] === container2).toBe(true);
+			expect(div.children[2] === container3).toBe(true);
+			expect(div.children[3] === container4).toBe(true);
+			expect(div.children.namedItem('container1') === container1).toBe(true);
+			expect(div.children.namedItem('container2') === container2).toBe(true);
+			expect(div.children.namedItem('0') === container3).toBe(true);
+			expect(div.children.namedItem('1') === container4).toBe(true);
+
+			container3.remove();
+
+			expect(div.children.length).toBe(3);
+			expect(div.children[0] === container1).toBe(true);
+			expect(div.children[1] === container2).toBe(true);
+			expect(div.children[2] === container4).toBe(true);
+			expect(div.children.namedItem('container1') === container1).toBe(true);
+			expect(div.children.namedItem('container2') === container2).toBe(true);
+			expect(div.children.namedItem('0') === null).toBe(true);
+			expect(div.children.namedItem('1') === container4).toBe(true);
+
+			div.insertBefore(container3, container4);
+
+			expect(div.children.length).toBe(4);
+			expect(div.children[0] === container1).toBe(true);
+			expect(div.children[1] === container2).toBe(true);
+			expect(div.children[2] === container3).toBe(true);
+			expect(div.children[3] === container4).toBe(true);
+			expect(div.children.namedItem('container1') === container1).toBe(true);
+			expect(div.children.namedItem('container2') === container2).toBe(true);
+			expect(div.children.namedItem('0') === container3).toBe(true);
+			expect(div.children.namedItem('1') === container4).toBe(true);
+		});
 	});
 });
diff --git a/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts b/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts
index 2901d8f8..6d301052 100644
--- a/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-form-element/HTMLFormElement.test.ts
@@ -91,13 +91,14 @@ describe('HTMLFormElement', () => {
                     <input type="radio" name="radio1" value="value1">
                     <input type="radio" name="radio1" value="value2" checked>
                     <input type="radio" name="radio1" value="value3">
+                    <input type="hidden" name="1" value="value1">
                 </div>
             `;
 			const elements = element.elements;
 			const root = element.children[0];
 
-			expect(element.length).toBe(8);
-			expect(elements.length).toBe(8);
+			expect(element.length).toBe(9);
+			expect(elements.length).toBe(9);
 
 			expect(element[0] === root.children[0]).toBe(true);
 			expect(element[1] === root.children[1]).toBe(true);
@@ -107,6 +108,7 @@ describe('HTMLFormElement', () => {
 			expect(element[5] === root.children[5]).toBe(true);
 			expect(element[6] === root.children[6]).toBe(true);
 			expect(element[7] === root.children[7]).toBe(true);
+			expect(element[8] === root.children[8]).toBe(true);
 
 			expect(elements[0] === root.children[0]).toBe(true);
 			expect(elements[1] === root.children[1]).toBe(true);
@@ -116,6 +118,7 @@ describe('HTMLFormElement', () => {
 			expect(elements[5] === root.children[5]).toBe(true);
 			expect(elements[6] === root.children[6]).toBe(true);
 			expect(elements[7] === root.children[7]).toBe(true);
+			expect(elements[8] === root.children[8]).toBe(true);
 
 			expect(elements.item(0) === root.children[0]).toBe(true);
 			expect(elements.item(1) === root.children[1]).toBe(true);
@@ -125,6 +128,7 @@ describe('HTMLFormElement', () => {
 			expect(elements.item(5) === root.children[5]).toBe(true);
 			expect(elements.item(6) === root.children[6]).toBe(true);
 			expect(elements.item(7) === root.children[7]).toBe(true);
+			expect(elements.item(8) === root.children[8]).toBe(true);
 
 			const radioNodeList1 = new RadioNodeList();
 			const radioNodeList2 = new RadioNodeList();
@@ -153,6 +157,7 @@ describe('HTMLFormElement', () => {
 			expect(elements.namedItem('checkbox1').value).toBe('value2');
 			expect(elements.namedItem('radio1')).toEqual(radioNodeList2);
 			expect(elements.namedItem('radio1').value).toBe('value2');
+			expect(elements.namedItem('1').value).toBe('value1');
 
 			(<IHTMLInputElement>elements.namedItem('text1')).name = 'text2';
 			(<IHTMLInputElement>elements.namedItem('text2')).id = 'text3';
@@ -181,26 +186,26 @@ describe('HTMLFormElement', () => {
 
 			root.appendChild(anotherElement);
 
-			expect(element.length).toBe(16);
-			expect(elements.length).toBe(16);
-
-			expect(element[8] === anotherRoot.children[0]).toBe(true);
-			expect(element[9] === anotherRoot.children[1]).toBe(true);
-			expect(element[10] === anotherRoot.children[2]).toBe(true);
-			expect(element[11] === anotherRoot.children[3]).toBe(true);
-			expect(element[12] === anotherRoot.children[4]).toBe(true);
-			expect(element[13] === anotherRoot.children[5]).toBe(true);
-			expect(element[14] === anotherRoot.children[6]).toBe(true);
-			expect(element[15] === anotherRoot.children[7]).toBe(true);
-
-			expect(elements[8] === anotherRoot.children[0]).toBe(true);
-			expect(elements[9] === anotherRoot.children[1]).toBe(true);
-			expect(elements[10] === anotherRoot.children[2]).toBe(true);
-			expect(elements[11] === anotherRoot.children[3]).toBe(true);
-			expect(elements[12] === anotherRoot.children[4]).toBe(true);
-			expect(elements[13] === anotherRoot.children[5]).toBe(true);
-			expect(elements[14] === anotherRoot.children[6]).toBe(true);
-			expect(elements[15] === anotherRoot.children[7]).toBe(true);
+			expect(element.length).toBe(17);
+			expect(elements.length).toBe(17);
+
+			expect(element[9] === anotherRoot.children[0]).toBe(true);
+			expect(element[10] === anotherRoot.children[1]).toBe(true);
+			expect(element[11] === anotherRoot.children[2]).toBe(true);
+			expect(element[12] === anotherRoot.children[3]).toBe(true);
+			expect(element[13] === anotherRoot.children[4]).toBe(true);
+			expect(element[14] === anotherRoot.children[5]).toBe(true);
+			expect(element[15] === anotherRoot.children[6]).toBe(true);
+			expect(element[16] === anotherRoot.children[7]).toBe(true);
+
+			expect(elements[9] === anotherRoot.children[0]).toBe(true);
+			expect(elements[10] === anotherRoot.children[1]).toBe(true);
+			expect(elements[11] === anotherRoot.children[2]).toBe(true);
+			expect(elements[12] === anotherRoot.children[3]).toBe(true);
+			expect(elements[13] === anotherRoot.children[4]).toBe(true);
+			expect(elements[14] === anotherRoot.children[5]).toBe(true);
+			expect(elements[15] === anotherRoot.children[6]).toBe(true);
+			expect(elements[16] === anotherRoot.children[7]).toBe(true);
 
 			const anotherRadioNodeList1 = new RadioNodeList();
 			const anotherRadioNodeList2 = new RadioNodeList();