Repository: capricorn86/happy-dom
Pull request #159 (2020-11-21 13:28:27)

Task/158 not possible to clone checkbox


Patch:
diff --git a/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts b/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts
index 25e97f8f..9dbcc894 100644
--- a/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts
+++ b/packages/happy-dom/src/nodes/html-form-element/HTMLFormElement.ts
@@ -1,20 +1,171 @@
 import HTMLElement from '../html-element/HTMLElement';
 import Element from '../element/Element';
-import Attr from '../../attribute/Attr';
 
 /**
  * HTMLFormElement.
  */
 export default class HTMLFormElement extends HTMLElement {
-	public name = '';
-	public method = 'get';
-	public target = '';
-	public action = '';
-	public encoding = '';
-	public enctype = '';
-	public acceptCharset = '';
-	public autocomplete = '';
-	public noValidate = '';
+	/**
+	 * Returns name.
+	 *
+	 * @return Name.
+	 */
+	public get name(): string {
+		return this.getAttributeNS(null, 'name') || '';
+	}
+
+	/**
+	 * Sets name.
+	 *
+	 * @param name Name.
+	 */
+	public set name(name: string) {
+		this.setAttributeNS(null, 'name', name);
+	}
+
+	/**
+	 * Returns method.
+	 *
+	 * @return Method.
+	 */
+	public get method(): string {
+		return this.getAttributeNS(null, 'method') || 'get';
+	}
+
+	/**
+	 * Sets method.
+	 *
+	 * @param method Method.
+	 */
+	public set method(method: string) {
+		this.setAttributeNS(null, 'method', method);
+	}
+
+	/**
+	 * Returns target.
+	 *
+	 * @return Target.
+	 */
+	public get target(): string {
+		return this.getAttributeNS(null, 'target') || '';
+	}
+
+	/**
+	 * Sets target.
+	 *
+	 * @param target Target.
+	 */
+	public set target(target: string) {
+		this.setAttributeNS(null, 'target', target);
+	}
+
+	/**
+	 * Returns action.
+	 *
+	 * @return Action.
+	 */
+	public get action(): string {
+		return this.getAttributeNS(null, 'action') || '';
+	}
+
+	/**
+	 * Sets action.
+	 *
+	 * @param action Action.
+	 */
+	public set action(action: string) {
+		this.setAttributeNS(null, 'action', action);
+	}
+
+	/**
+	 * Returns encoding.
+	 *
+	 * @return Encoding.
+	 */
+	public get encoding(): string {
+		return this.getAttributeNS(null, 'encoding') || '';
+	}
+
+	/**
+	 * Sets encoding.
+	 *
+	 * @param encoding Encoding.
+	 */
+	public set encoding(encoding: string) {
+		this.setAttributeNS(null, 'encoding', encoding);
+	}
+
+	/**
+	 * Returns enctype.
+	 *
+	 * @return Enctype.
+	 */
+	public get enctype(): string {
+		return this.getAttributeNS(null, 'enctype') || '';
+	}
+
+	/**
+	 * Sets enctype.
+	 *
+	 * @param enctype Enctype.
+	 */
+	public set enctype(enctype: string) {
+		this.setAttributeNS(null, 'enctype', enctype);
+	}
+
+	/**
+	 * Returns autocomplete.
+	 *
+	 * @return Autocomplete.
+	 */
+	public get autocomplete(): string {
+		return this.getAttributeNS(null, 'autocomplete') || '';
+	}
+
+	/**
+	 * Sets autocomplete.
+	 *
+	 * @param autocomplete Autocomplete.
+	 */
+	public set autocomplete(autocomplete: string) {
+		this.setAttributeNS(null, 'autocomplete', autocomplete);
+	}
+
+	/**
+	 * Returns accept charset.
+	 *
+	 * @return Accept charset.
+	 */
+	public get acceptCharset(): string {
+		return this.getAttributeNS(null, 'acceptcharset') || '';
+	}
+
+	/**
+	 * Sets accept charset.
+	 *
+	 * @param acceptCharset Accept charset.
+	 */
+	public set acceptCharset(acceptCharset: string) {
+		this.setAttributeNS(null, 'acceptcharset', acceptCharset);
+	}
+
+	/**
+	 * Returns no validate.
+	 *
+	 * @return No validate.
+	 */
+	public get noValidate(): string {
+		return this.getAttributeNS(null, 'novalidate') || '';
+	}
+
+	/**
+	 * Sets no validate.
+	 *
+	 * @param noValidate No validate.
+	 */
+	public set noValidate(noValidate: string) {
+		this.setAttributeNS(null, 'novalidate', noValidate);
+	}
 
 	/**
 	 * Returns input elements.
@@ -57,86 +208,4 @@ export default class HTMLFormElement extends HTMLElement {
 	public checkValidity(): boolean {
 		return true;
 	}
-
-	/**
-	 * Removes an Attr node.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 */
-	public removeAttributeNode(attribute: Attr): void {
-		super.removeAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'method':
-				this[attribute.name] = 'get';
-				break;
-			case 'name':
-			case 'target':
-			case 'action':
-			case 'encoding':
-			case 'enctype':
-			case 'autocomplete':
-				this[attribute.name] = '';
-				break;
-			case 'acceptcharset':
-				this.acceptCharset = '';
-				break;
-			case 'novalidate':
-				this.noValidate = '';
-				break;
-		}
-	}
-
-	/**
-	 * The setAttributeNode() method adds a new Attr node to the specified element.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 * @returns Replaced attribute.
-	 */
-	public setAttributeNode(attribute: Attr): Attr {
-		const replacedAttribute = super.setAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'name':
-			case 'method':
-			case 'target':
-			case 'action':
-			case 'encoding':
-			case 'enctype':
-			case 'autocomplete':
-				this[attribute.name] = attribute.value;
-				break;
-			case 'acceptcharset':
-				this.acceptCharset = attribute.value;
-				break;
-			case 'novalidate':
-				this.noValidate = attribute.value;
-				break;
-		}
-
-		return replacedAttribute;
-	}
-
-	/**
-	 * Clones a node.
-	 *
-	 * @override
-	 * @param [deep=false] "true" to clone deep.
-	 * @return Cloned node.
-	 */
-	public cloneNode(deep = false): HTMLFormElement {
-		const clone = <HTMLFormElement>super.cloneNode(deep);
-		clone.name = this.name;
-		clone.method = this.method;
-		clone.target = this.target;
-		clone.action = this.action;
-		clone.encoding = this.encoding;
-		clone.enctype = this.enctype;
-		clone.acceptCharset = this.acceptCharset;
-		clone.autocomplete = this.autocomplete;
-		clone.noValidate = this.noValidate;
-		return clone;
-	}
 }
diff --git a/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
index 488ba880..7138a2cc 100644
--- a/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
+++ b/packages/happy-dom/src/nodes/html-input-element/HTMLInputElement.ts
@@ -1,5 +1,4 @@
 import File from '../../file/File';
-import Attr from '../../attribute/Attr';
 import HTMLElement from '../html-element/HTMLElement';
 import HTMLFormElement from '../html-form-element/HTMLFormElement';
 import ValidityState from './ValidityState';
@@ -26,52 +25,504 @@ export default class HTMLInputElement extends HTMLElement {
 	public formNoValidate = false;
 
 	// Any type of input
-	public name = '';
-	public _type = 'text';
-	public disabled = false;
-	public autofocus = false;
-	public required = false;
-	public _value = '';
+	public _value = null;
+	public _height = 0;
+	public _width = 0;
 
 	// Type specific: checkbox/radio
-	public checked = false;
 	public defaultChecked = false;
-	public indeterminate = false;
-
-	// Type specific: image
-	public alt = '';
-	public height = 0;
-	public src = '';
-	public width = 0;
 
 	// Type specific: file
 	public files: File[] = [];
-	public accept = '';
-	public allowdirs = '';
-
-	// Type specific: text/number
-	public autocomplete = '';
-	public min = '';
-	public max = '';
-	public minLength = -1;
-	public maxLength = -1;
-	public pattern = '';
-	public placeholder = '';
-	public readOnly = false;
-	public size = 0;
 
 	// Type specific: text/password/search/tel/url/week/month
-	private _selectionStart = 0;
-	private _selectionEnd = 0;
+	private _selectionStart = null;
+	private _selectionEnd = null;
 	private _selectionDirection = HTMLInputElementSelectionDirectionEnum.none;
 
-	// Email
-	public multiple = false;
-
 	// Not categorized
 	public defaultValue = '';
-	public step = '';
-	public inputmode = '';
+	/**
+	 * Returns height.
+	 *
+	 * @return Height.
+	 */
+	public get height(): number {
+		return this._height;
+	}
+
+	/**
+	 * Sets height.
+	 *
+	 * @param height Height.
+	 */
+	public set height(height: number) {
+		this._height = height;
+		this.setAttributeNS(null, 'height', String(height));
+	}
+
+	/**
+	 * Returns width.
+	 *
+	 * @return Width.
+	 */
+	public get width(): number {
+		return this._width;
+	}
+
+	/**
+	 * Sets width.
+	 *
+	 * @param width Width.
+	 */
+	public set width(width: number) {
+		this._width = width;
+		this.setAttributeNS(null, 'width', String(width));
+	}
+
+	/**
+	 * Returns size.
+	 *
+	 * @return Size.
+	 */
+	public get size(): number {
+		const size = this.getAttributeNS(null, 'size');
+		if (size !== null) {
+			return parseInt(size);
+		}
+		return 20;
+	}
+
+	/**
+	 * Sets size.
+	 *
+	 * @param size Size.
+	 */
+	public set size(size: number) {
+		this.setAttributeNS(null, 'size', String(size));
+	}
+
+	/**
+	 * Returns minlength.
+	 *
+	 * @return Min length.
+	 */
+	public get minLength(): number {
+		const minLength = this.getAttributeNS(null, 'minlength');
+		if (minLength !== null) {
+			return parseInt(minLength);
+		}
+		return -1;
+	}
+
+	/**
+	 * Sets minlength.
+	 *
+	 * @param minLength Min length.
+	 */
+	public set minLength(minlength: number) {
+		this.setAttributeNS(null, 'minlength', String(minlength));
+	}
+
+	/**
+	 * Returns maxlength.
+	 *
+	 * @return Max length.
+	 */
+	public get maxLength(): number {
+		const maxLength = this.getAttributeNS(null, 'maxlength');
+		if (maxLength !== null) {
+			return parseInt(maxLength);
+		}
+		return -1;
+	}
+
+	/**
+	 * Sets maxlength.
+	 *
+	 * @param maxlength Max length.
+	 */
+	public set maxLength(maxLength: number) {
+		this.setAttributeNS(null, 'maxlength', String(maxLength));
+	}
+
+	/**
+	 * Returns type.
+	 *
+	 * @return Type. Defaults to "text".
+	 */
+	public get type(): string {
+		return this.getAttributeNS(null, 'type') || 'text';
+	}
+
+	/**
+	 * Sets type.
+	 *
+	 * @param type Type.
+	 */
+	public set type(type: string) {
+		this.setAttributeNS(null, 'type', type.toLowerCase());
+	}
+
+	/**
+	 * Returns name.
+	 *
+	 * @return Name.
+	 */
+	public get name(): string {
+		return this.getAttributeNS(null, 'name') || '';
+	}
+
+	/**
+	 * Sets name.
+	 *
+	 * @param name Name.
+	 */
+	public set name(name: string) {
+		this.setAttributeNS(null, 'name', name);
+	}
+
+	/**
+	 * Returns alt.
+	 *
+	 * @return Alt.
+	 */
+	public get alt(): string {
+		return this.getAttributeNS(null, 'alt') || '';
+	}
+
+	/**
+	 * Sets alt.
+	 *
+	 * @param alt Alt.
+	 */
+	public set alt(alt: string) {
+		this.setAttributeNS(null, 'alt', alt);
+	}
+
+	/**
+	 * Returns min.
+	 *
+	 * @return Min.
+	 */
+	public get min(): string {
+		return this.getAttributeNS(null, 'min') || '';
+	}
+
+	/**
+	 * Sets min.
+	 *
+	 * @param min Min.
+	 */
+	public set min(min: string) {
+		this.setAttributeNS(null, 'min', min);
+	}
+
+	/**
+	 * Returns max.
+	 *
+	 * @return Max.
+	 */
+	public get max(): string {
+		return this.getAttributeNS(null, 'max') || '';
+	}
+
+	/**
+	 * Sets max.
+	 *
+	 * @param max Max.
+	 */
+	public set max(max: string) {
+		this.setAttributeNS(null, 'max', max);
+	}
+
+	/**
+	 * Returns pattern.
+	 *
+	 * @return Pattern.
+	 */
+	public get pattern(): string {
+		return this.getAttributeNS(null, 'pattern') || '';
+	}
+
+	/**
+	 * Sets pattern.
+	 *
+	 * @param pattern Pattern.
+	 */
+	public set pattern(pattern: string) {
+		this.setAttributeNS(null, 'pattern', pattern);
+	}
+
+	/**
+	 * Returns placeholder.
+	 *
+	 * @return Placeholder.
+	 */
+	public get placeholder(): string {
+		return this.getAttributeNS(null, 'placeholder') || '';
+	}
+
+	/**
+	 * Sets placeholder.
+	 *
+	 * @param placeholder Placeholder.
+	 */
+	public set placeholder(placeholder: string) {
+		this.setAttributeNS(null, 'placeholder', placeholder);
+	}
+
+	/**
+	 * Returns step.
+	 *
+	 * @return Step.
+	 */
+	public get step(): string {
+		return this.getAttributeNS(null, 'step') || '';
+	}
+
+	/**
+	 * Sets step.
+	 *
+	 * @param step Step.
+	 */
+	public set step(step: string) {
+		this.setAttributeNS(null, 'step', step);
+	}
+
+	/**
+	 * Returns inputmode.
+	 *
+	 * @return Inputmode.
+	 */
+	public get inputmode(): string {
+		return this.getAttributeNS(null, 'inputmode') || '';
+	}
+
+	/**
+	 * Sets inputmode.
+	 *
+	 * @param inputmode Inputmode.
+	 */
+	public set inputmode(inputmode: string) {
+		this.setAttributeNS(null, 'inputmode', inputmode);
+	}
+
+	/**
+	 * Returns accept.
+	 *
+	 * @return Accept.
+	 */
+	public get accept(): string {
+		return this.getAttributeNS(null, 'accept') || '';
+	}
+
+	/**
+	 * Sets accept.
+	 *
+	 * @param accept Accept.
+	 */
+	public set accept(accept: string) {
+		this.setAttributeNS(null, 'accept', accept);
+	}
+
+	/**
+	 * Returns allowdirs.
+	 *
+	 * @return Allowdirs.
+	 */
+	public get allowdirs(): string {
+		return this.getAttributeNS(null, 'allowdirs') || '';
+	}
+
+	/**
+	 * Sets allowdirs.
+	 *
+	 * @param allowdirs Allowdirs.
+	 */
+	public set allowdirs(allowdirs: string) {
+		this.setAttributeNS(null, 'allowdirs', allowdirs);
+	}
+
+	/**
+	 * Returns autocomplete.
+	 *
+	 * @return Autocomplete.
+	 */
+	public get autocomplete(): string {
+		return this.getAttributeNS(null, 'autocomplete') || '';
+	}
+
+	/**
+	 * Sets autocomplete.
+	 *
+	 * @param autocomplete Autocomplete.
+	 */
+	public set autocomplete(autocomplete: string) {
+		this.setAttributeNS(null, 'autocomplete', autocomplete);
+	}
+
+	/**
+	 * Returns src.
+	 *
+	 * @return Src.
+	 */
+	public get src(): string {
+		return this.getAttributeNS(null, 'src') || '';
+	}
+
+	/**
+	 * Sets src.
+	 *
+	 * @param src Src.
+	 */
+	public set src(src: string) {
+		this.setAttributeNS(null, 'src', src);
+	}
+
+	/**
+	 * Returns defaultvalue.
+	 *
+	 * @return Defaultvalue.
+	 */
+	public get defaultvalue(): string {
+		return this.getAttributeNS(null, 'defaultvalue') || '';
+	}
+
+	/**
+	 * Sets defaultvalue.
+	 *
+	 * @param defaultvalue Defaultvalue.
+	 */
+	public set defaultvalue(defaultvalue: string) {
+		this.setAttributeNS(null, 'defaultvalue', defaultvalue);
+	}
+
+	/**
+	 * Returns read only.
+	 *
+	 * @return Read only.
+	 */
+	public get readOnly(): boolean {
+		return this.getAttributeNS(null, 'readonly') !== null;
+	}
+
+	/**
+	 * Sets read only.
+	 *
+	 * @param readOnly Read only.
+	 */
+	public set readOnly(readOnly: boolean) {
+		this.setAttributeNS(null, 'readonly', readOnly ? '' : null);
+	}
+
+	/**
+	 * Returns disabled.
+	 *
+	 * @return Disabled.
+	 */
+	public get disabled(): boolean {
+		return this.getAttributeNS(null, 'disabled') !== null;
+	}
+
+	/**
+	 * Sets disabled.
+	 *
+	 * @param disabled Disabled.
+	 */
+	public set disabled(disabled: boolean) {
+		this.setAttributeNS(null, 'disabled', disabled ? '' : null);
+	}
+
+	/**
+	 * Returns autofocus.
+	 *
+	 * @return Autofocus.
+	 */
+	public get autofocus(): boolean {
+		return this.getAttributeNS(null, 'autofocus') !== null;
+	}
+
+	/**
+	 * Sets autofocus.
+	 *
+	 * @param autofocus Autofocus.
+	 */
+	public set autofocus(autofocus: boolean) {
+		this.setAttributeNS(null, 'autofocus', autofocus ? '' : null);
+	}
+
+	/**
+	 * Returns required.
+	 *
+	 * @return Required.
+	 */
+	public get required(): boolean {
+		return this.getAttributeNS(null, 'required') !== null;
+	}
+
+	/**
+	 * Sets required.
+	 *
+	 * @param required Required.
+	 */
+	public set required(required: boolean) {
+		this.setAttributeNS(null, 'required', required ? '' : null);
+	}
+
+	/**
+	 * Returns indeterminate.
+	 *
+	 * @return Indeterminate.
+	 */
+	public get indeterminate(): boolean {
+		return this.getAttributeNS(null, 'indeterminate') !== null;
+	}
+
+	/**
+	 * Sets indeterminate.
+	 *
+	 * @param indeterminate Indeterminate.
+	 */
+	public set indeterminate(indeterminate: boolean) {
+		this.setAttributeNS(null, 'indeterminate', indeterminate ? '' : null);
+	}
+
+	/**
+	 * Returns multiple.
+	 *
+	 * @return Multiple.
+	 */
+	public get multiple(): boolean {
+		return this.getAttributeNS(null, 'multiple') !== null;
+	}
+
+	/**
+	 * Sets multiple.
+	 *
+	 * @param multiple Multiple.
+	 */
+	public set multiple(multiple: boolean) {
+		this.setAttributeNS(null, 'multiple', multiple ? '' : null);
+	}
+
+	/**
+	 * Returns checked.
+	 *
+	 * @return Checked.
+	 */
+	public get checked(): boolean {
+		return this.getAttributeNS(null, 'checked') !== null;
+	}
+
+	/**
+	 * Sets checked.
+	 *
+	 * @param checked Checked.
+	 */
+	public set checked(checked: boolean) {
+		this.setAttributeNS(null, 'checked', checked ? '' : null);
+	}
 
 	/**
 	 * Returns value.
@@ -94,6 +545,10 @@ export default class HTMLInputElement extends HTMLElement {
 				return this.files.length > 0 ? '/fake/path/' + this.files[0].name : '';
 		}
 
+		if (this._value === null) {
+			return this.getAttributeNS(null, 'value') || '';
+		}
+
 		return this._value;
 	}
 
@@ -103,7 +558,7 @@ export default class HTMLInputElement extends HTMLElement {
 	 * @param value Value.
 	 */
 	public set value(value: string) {
-		switch (this._type) {
+		switch (this.type) {
 			case 'hidden':
 			case 'submit':
 			case 'image':
@@ -126,8 +581,8 @@ export default class HTMLInputElement extends HTMLElement {
 				this._value = HTMLInputElementValueSanitizer.sanitize(this, value);
 
 				if (oldValue !== this._value) {
-					this._selectionStart = value.length;
-					this._selectionEnd = value.length;
+					this._selectionStart = this._value.length;
+					this._selectionEnd = this._value.length;
 					this._selectionDirection = HTMLInputElementSelectionDirectionEnum.none;
 				}
 
@@ -135,24 +590,6 @@ export default class HTMLInputElement extends HTMLElement {
 		}
 	}
 
-	/**
-	 * Sets type.
-	 *
-	 * @param type Type.
-	 */
-	public set type(type: string) {
-		this._type = type.toLowerCase();
-	}
-
-	/**
-	 * Returns type.
-	 *
-	 * @param type Type.
-	 */
-	public get type(): string {
-		return this._type;
-	}
-
 	/**
 	 * Returns selection start.
 	 *
@@ -163,6 +600,10 @@ export default class HTMLInputElement extends HTMLElement {
 			return null;
 		}
 
+		if (this._selectionStart === null) {
+			return this.value.length;
+		}
+
 		return this._selectionStart;
 	}
 
@@ -179,7 +620,7 @@ export default class HTMLInputElement extends HTMLElement {
 			);
 		}
 
-		this.setSelectionRange(start, Math.max(start, this._selectionEnd), this._selectionDirection);
+		this.setSelectionRange(start, Math.max(start, this.selectionEnd), this._selectionDirection);
 	}
 
 	/**
@@ -192,6 +633,10 @@ export default class HTMLInputElement extends HTMLElement {
 			return null;
 		}
 
+		if (this._selectionEnd === null) {
+			return this.value.length;
+		}
+
 		return this._selectionEnd;
 	}
 
@@ -208,7 +653,7 @@ export default class HTMLInputElement extends HTMLElement {
 			);
 		}
 
-		this.setSelectionRange(this._selectionStart, end, this._selectionDirection);
+		this.setSelectionRange(this.selectionStart, end, this._selectionDirection);
 	}
 
 	/**
@@ -414,132 +859,6 @@ export default class HTMLInputElement extends HTMLElement {
 		return true;
 	}
 
-	/**
-	 * Removes an Attr node.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 */
-	public removeAttributeNode(attribute: Attr): void {
-		super.removeAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'height': // number
-			case 'width': // number
-			case 'size': // number
-				this[attribute.name] = 0;
-				break;
-			case 'selectionstart': // number
-				this.selectionStart = 0;
-				break;
-			case 'selectionend': // number
-				this.selectionEnd = 0;
-				break;
-			case 'minlength': // number
-				this.minLength = -1;
-				break;
-			case 'maxlength': // number
-				this.maxLength = -1;
-				break;
-			case 'type': // string
-				this[attribute.name] = 'text';
-				break;
-			case 'name': // string
-			case 'alt': // string
-			case 'min': // string
-			case 'max': // string
-			case 'pattern': // string
-			case 'placeholder': // string
-			case 'step': // string
-			case 'inputmode': // string
-			case 'accept': // string
-			case 'allowdirs': // string
-			case 'autocomplete': // string
-			case 'src': // string
-				this[attribute.name] = '';
-				break;
-			case 'value': // string
-				if (this._value === attribute.value && this._isPrivateValueSupported()) {
-					this.value = '';
-				}
-				break;
-			case 'defaultvalue': // string
-				this.defaultValue = '';
-				break;
-			case 'readonly': //  boolean
-				this.readOnly = false;
-			case 'disabled': // boolean
-			case 'autofocus': // boolean
-			case 'required': // boolean
-			case 'indeterminate': // boolean
-			case 'multiple': // boolean
-			case 'checked': // boolean
-				this[attribute.name] = false;
-				break;
-		}
-	}
-
-	/**
-	 * The setAttributeNode() method adds a new Attr node to the specified element.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 * @returns Replaced attribute.
-	 */
-	public setAttributeNode(attribute: Attr): Attr {
-		const replacedAttribute = super.setAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'height': // number
-			case 'width': // number
-			case 'size': // number
-				this[attribute.name] = !!attribute.value ? Number(attribute.value) : 0;
-				break;
-			case 'minlength': // number
-				this.minLength = !!attribute.value ? Number(attribute.value) : -1;
-				break;
-			case 'maxlength': // number
-				this.maxLength = !!attribute.value ? Number(attribute.value) : -1;
-				break;
-			case 'type': // string
-			case 'name': // string
-			case 'alt': // string
-			case 'min': // string
-			case 'max': // string
-			case 'pattern': // string
-			case 'placeholder': // string
-			case 'step': // string
-			case 'inputmode': // string
-			case 'accept': // string
-			case 'allowdirs': // string
-			case 'autocomplete': // string
-			case 'src': // string
-				this[attribute.name] = attribute.value || '';
-				break;
-			case 'value': // string
-				if (!this._value && this._isPrivateValueSupported()) {
-					this.value = attribute.value || '';
-				}
-				break;
-			case 'defaultvalue': // string
-				this.defaultValue = attribute.value || '';
-				break;
-			case 'readonly': //  boolean
-				this.readOnly = attribute.value !== null;
-				break;
-			case 'disabled': // boolean
-			case 'autofocus': // boolean
-			case 'required': // boolean
-			case 'indeterminate': // boolean
-			case 'multiple': // boolean
-			case 'checked': // boolean
-				this[attribute.name] = attribute.value !== null;
-				break;
-		}
-
-		return replacedAttribute;
-	}
-
 	/**
 	 * Clones a node.
 	 *
@@ -552,37 +871,14 @@ export default class HTMLInputElement extends HTMLElement {
 		clone.formAction = this.formAction;
 		clone.formMethod = this.formMethod;
 		clone.formNoValidate = this.formNoValidate;
-		clone.name = this.name;
-		clone.type = this.type;
-		clone.disabled = this.disabled;
-		clone.autofocus = this.autofocus;
-		clone.required = this.required;
 		clone._value = this._value;
-		clone.checked = this.checked;
+		clone._height = this._height;
+		clone._width = this._width;
 		clone.defaultChecked = this.defaultChecked;
-		clone.indeterminate = this.indeterminate;
-		clone.alt = this.alt;
-		clone.height = this.height;
-		clone.src = this.src;
-		clone.width = this.width;
-		clone.accept = this.accept;
-		clone.allowdirs = this.allowdirs;
-		clone.autocomplete = this.autocomplete;
-		clone.min = this.min;
-		clone.max = this.max;
-		clone.minLength = this.minLength;
-		clone.maxLength = this.maxLength;
-		clone.pattern = this.pattern;
-		clone.placeholder = this.placeholder;
-		clone.readOnly = this.readOnly;
-		clone.size = this.size;
-		clone.selectionStart = this.selectionStart;
-		clone.selectionEnd = this.selectionEnd;
-		clone.selectionDirection = this.selectionDirection;
-		clone.defaultValue = this.defaultValue;
-		clone.multiple = this.multiple;
-		clone.step = this.step;
-		clone.inputmode = this.inputmode;
+		clone.files = this.files.slice();
+		clone._selectionStart = this._selectionStart;
+		clone._selectionEnd = this._selectionEnd;
+		clone._selectionDirection = this._selectionDirection;
 		return clone;
 	}
 
@@ -591,18 +887,18 @@ export default class HTMLInputElement extends HTMLElement {
 	 *
 	 * @returns "true" if private value is supported.
 	 */
-	private _isPrivateValueSupported(): boolean {
-		return (
-			this.type !== 'hidden' &&
-			this.type !== 'submit' &&
-			this.type !== 'image' &&
-			this.type !== 'reset' &&
-			this.type !== 'button' &&
-			this.type !== 'checkbox' &&
-			this.type !== 'radio' &&
-			this.type !== 'file'
-		);
-	}
+	// private _isPrivateValueSupported(): boolean {
+	// 	return (
+	// 		this.type !== 'hidden' &&
+	// 		this.type !== 'submit' &&
+	// 		this.type !== 'image' &&
+	// 		this.type !== 'reset' &&
+	// 		this.type !== 'button' &&
+	// 		this.type !== 'checkbox' &&
+	// 		this.type !== 'radio' &&
+	// 		this.type !== 'file'
+	// 	);
+	// }
 
 	/**
 	 * Checks is selection is supported.
diff --git a/packages/happy-dom/src/nodes/html-text-area-element/HTMLTextAreaElement.ts b/packages/happy-dom/src/nodes/html-text-area-element/HTMLTextAreaElement.ts
index 30e14880..3781d03a 100644
--- a/packages/happy-dom/src/nodes/html-text-area-element/HTMLTextAreaElement.ts
+++ b/packages/happy-dom/src/nodes/html-text-area-element/HTMLTextAreaElement.ts
@@ -1,30 +1,245 @@
+import Event from '../../event/Event';
+import DOMException from '../../exception/DOMException';
+import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum';
 import HTMLElement from '../html-element/HTMLElement';
-import Node from '../node/Node';
 import HTMLFormElement from '../html-form-element/HTMLFormElement';
-import Attr from '../../attribute/Attr';
+import HTMLInputElementSelectionDirectionEnum from '../html-input-element/HTMLInputElementSelectionDirectionEnum';
+import HTMLInputElementSelectionModeEnum from '../html-input-element/HTMLInputElementSelectionModeEnum';
 
 /**
  * HTMLTextAreaElement.
  */
 export default class HTMLTextAreaElement extends HTMLElement {
-	public name = '';
 	public readonly type = 'textarea';
-	public disabled = false;
-	public autofocus = false;
-	public required = false;
-	public _value = '';
-	public autocomplete = '';
-	public cols = '';
-	public rows = '';
-	public minLength = -1;
-	public maxLength = -1;
-	public placeholder = '';
-	public readOnly = false;
-	public selectionStart = 0;
-	public selectionEnd = 0;
-	public selectionDirection = 'forward';
+	public _value = null;
+	public _selectionStart = null;
+	public _selectionEnd = null;
+	public _selectionDirection = HTMLInputElementSelectionDirectionEnum.none;
 	public defaultValue = '';
-	public inputmode = '';
+
+	/**
+	 * Returns minlength.
+	 *
+	 * @return Min length.
+	 */
+	public get minLength(): number {
+		const minLength = this.getAttributeNS(null, 'minlength');
+		if (minLength !== null) {
+			return parseInt(minLength);
+		}
+		return -1;
+	}
+
+	/**
+	 * Sets minlength.
+	 *
+	 * @param minLength Min length.
+	 */
+	public set minLength(minlength: number) {
+		this.setAttributeNS(null, 'minlength', String(minlength));
+	}
+
+	/**
+	 * Returns maxlength.
+	 *
+	 * @return Max length.
+	 */
+	public get maxLength(): number {
+		const maxLength = this.getAttributeNS(null, 'maxlength');
+		if (maxLength !== null) {
+			return parseInt(maxLength);
+		}
+		return -1;
+	}
+
+	/**
+	 * Sets maxlength.
+	 *
+	 * @param maxlength Max length.
+	 */
+	public set maxLength(maxLength: number) {
+		this.setAttributeNS(null, 'maxlength', String(maxLength));
+	}
+
+	/**
+	 * Returns name.
+	 *
+	 * @return Name.
+	 */
+	public get name(): string {
+		return this.getAttributeNS(null, 'name') || '';
+	}
+
+	/**
+	 * Sets name.
+	 *
+	 * @param name Name.
+	 */
+	public set name(name: string) {
+		this.setAttributeNS(null, 'name', name);
+	}
+
+	/**
+	 * Returns placeholder.
+	 *
+	 * @return Placeholder.
+	 */
+	public get placeholder(): string {
+		return this.getAttributeNS(null, 'placeholder') || '';
+	}
+
+	/**
+	 * Sets placeholder.
+	 *
+	 * @param placeholder Placeholder.
+	 */
+	public set placeholder(placeholder: string) {
+		this.setAttributeNS(null, 'placeholder', placeholder);
+	}
+
+	/**
+	 * Returns inputmode.
+	 *
+	 * @return Inputmode.
+	 */
+	public get inputmode(): string {
+		return this.getAttributeNS(null, 'inputmode') || '';
+	}
+
+	/**
+	 * Sets inputmode.
+	 *
+	 * @param inputmode Inputmode.
+	 */
+	public set inputmode(inputmode: string) {
+		this.setAttributeNS(null, 'inputmode', inputmode);
+	}
+
+	/**
+	 * Returns cols.
+	 *
+	 * @return Cols.
+	 */
+	public get cols(): string {
+		return this.getAttributeNS(null, 'cols') || '';
+	}
+
+	/**
+	 * Sets cols.
+	 *
+	 * @param cols Cols.
+	 */
+	public set cols(cols: string) {
+		this.setAttributeNS(null, 'cols', cols);
+	}
+
+	/**
+	 * Returns rows.
+	 *
+	 * @return Rows.
+	 */
+	public get rows(): string {
+		return this.getAttributeNS(null, 'rows') || '';
+	}
+
+	/**
+	 * Sets rows.
+	 *
+	 * @param rows Rows.
+	 */
+	public set rows(rows: string) {
+		this.setAttributeNS(null, 'rows', rows);
+	}
+
+	/**
+	 * Returns autocomplete.
+	 *
+	 * @return Autocomplete.
+	 */
+	public get autocomplete(): string {
+		return this.getAttributeNS(null, 'autocomplete') || '';
+	}
+
+	/**
+	 * Sets autocomplete.
+	 *
+	 * @param autocomplete Autocomplete.
+	 */
+	public set autocomplete(autocomplete: string) {
+		this.setAttributeNS(null, 'autocomplete', autocomplete);
+	}
+
+	/**
+	 * Returns readOnly.
+	 *
+	 * @return ReadOnly.
+	 */
+	public get readOnly(): boolean {
+		return this.getAttributeNS(null, 'readonly') !== null;
+	}
+
+	/**
+	 * Sets readOnly.
+	 *
+	 * @param readOnly ReadOnly.
+	 */
+	public set readOnly(readOnly: boolean) {
+		this.setAttributeNS(null, 'readonly', readOnly ? '' : null);
+	}
+
+	/**
+	 * Returns disabled.
+	 *
+	 * @return Disabled.
+	 */
+	public get disabled(): boolean {
+		return this.getAttributeNS(null, 'disabled') !== null;
+	}
+
+	/**
+	 * Sets disabled.
+	 *
+	 * @param disabled Disabled.
+	 */
+	public set disabled(disabled: boolean) {
+		this.setAttributeNS(null, 'disabled', disabled ? '' : null);
+	}
+
+	/**
+	 * Returns autofocus.
+	 *
+	 * @return Autofocus.
+	 */
+	public get autofocus(): boolean {
+		return this.getAttributeNS(null, 'autofocus') !== null;
+	}
+
+	/**
+	 * Sets autofocus.
+	 *
+	 * @param autofocus Autofocus.
+	 */
+	public set autofocus(autofocus: boolean) {
+		this.setAttributeNS(null, 'autofocus', autofocus ? '' : null);
+	}
+
+	/**
+	 * Returns required.
+	 *
+	 * @return Required.
+	 */
+	public get required(): boolean {
+		return this.getAttributeNS(null, 'required') !== null;
+	}
+
+	/**
+	 * Sets required.
+	 *
+	 * @param required Required.
+	 */
+	public set required(required: boolean) {
+		this.setAttributeNS(null, 'required', required ? '' : null);
+	}
 
 	/**
 	 * Returns value.
@@ -32,6 +247,10 @@ export default class HTMLTextAreaElement extends HTMLElement {
 	 * @return Value.
 	 */
 	public get value(): string {
+		if (this._value === null) {
+			return this.getAttributeNS(null, 'value') || '';
+		}
+
 		return this._value;
 	}
 
@@ -41,170 +260,194 @@ export default class HTMLTextAreaElement extends HTMLElement {
 	 * @param value Value.
 	 */
 	public set value(value: string) {
+		const oldValue = this._value;
 		this._value = value;
-		if (this.defaultValue === null) {
-			this.defaultValue = value;
+
+		if (oldValue !== this._value) {
+			this._selectionStart = this._value.length;
+			this._selectionEnd = this._value.length;
+			this._selectionDirection = HTMLInputElementSelectionDirectionEnum.none;
 		}
 	}
 
 	/**
-	 * Returns the parent form element.
+	 * Returns selection start.
 	 *
-	 * @return Form.
+	 * @return Selection start.
 	 */
-	public get form(): HTMLFormElement {
-		let parent = <HTMLElement>this.parentNode;
-		while (parent && parent.tagName !== 'FORM') {
-			parent = <HTMLElement>this.parentNode;
+	public get selectionStart(): number {
+		if (this._selectionStart === null) {
+			return this.value.length;
 		}
-		return <HTMLFormElement>parent;
+
+		return this._selectionStart;
 	}
 
 	/**
-	 * Returns text length.
+	 * Sets selection start.
 	 *
-	 * @param Text length.
+	 * @param start Start.
 	 */
-	public get textLength(): number {
-		return this.value.length;
+	public set selectionStart(start: number) {
+		this.setSelectionRange(start, Math.max(start, this.selectionEnd), this._selectionDirection);
 	}
 
 	/**
-	 * @override
+	 * Returns selection end.
+	 *
+	 * @return Selection end.
 	 */
-	public appendChild(node: Node): Node {
-		super.appendChild(node);
-		this.value = this.textContent;
-		return node;
+	public get selectionEnd(): number {
+		if (this._selectionEnd === null) {
+			return this.value.length;
+		}
+
+		return this._selectionEnd;
 	}
 
 	/**
-	 * Remove Child element from childNodes array.
+	 * Sets selection end.
 	 *
-	 * @param node Node to remove
+	 * @param end End.
 	 */
-	public removeChild(node: Node): void {
-		super.removeChild(node);
-		this.value = this.textContent;
+	public set selectionEnd(end: number) {
+		this.setSelectionRange(this.selectionStart, end, this._selectionDirection);
 	}
 
 	/**
-	 * Inserts a node before another.
+	 * Returns selection direction.
 	 *
-	 * @param newNode Node to insert.
-	 * @param referenceNode Node to insert before.
-	 * @return Inserted node.
+	 * @return Selection direction.
 	 */
-	public insertBefore(newNode: Node, referenceNode: Node): Node {
-		super.insertBefore(newNode, referenceNode);
-		this.value = this.textContent;
-		return newNode;
+	public get selectionDirection(): string {
+		return this._selectionDirection;
 	}
 
 	/**
-	 * Checks validity.
+	 * Sets selection direction.
 	 *
-	 * @return "true" if validation does'nt fail.
+	 * @param direction Direction.
 	 */
-	public checkValidity(): boolean {
-		return true;
+	public set selectionDirection(direction: string) {
+		this.setSelectionRange(this._selectionStart, this._selectionEnd, direction);
 	}
 
 	/**
-	 * Removes an Attr node.
+	 * Returns the parent form element.
 	 *
-	 * @override
-	 * @param attribute Attribute.
+	 * @return Form.
 	 */
-	public removeAttributeNode(attribute: Attr): void {
-		super.removeAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'selectionstart': // number
-				this.selectionStart = 0;
-				break;
-			case 'selectionend': // number
-				this.selectionEnd = 0;
-				break;
-			case 'minlength': // number
-				this.minLength = -1;
-				break;
-			case 'maxlength': // number
-				this.maxLength = -1;
-				break;
-			case 'name': // string
-			case 'placeholder': // string
-			case 'inputmode': // string
-			case 'cols': // string
-			case 'rows': // string
-			case 'autocomplete': // string
-				this[attribute.name] = '';
-				break;
-			case 'selectiondirection': // string
-				this.selectionDirection = 'forward';
-				break;
-			case 'value': // string
-				this._value = '';
-				break;
-			case 'readonly': //  boolean
-				this.readOnly = false;
-				break;
-			case 'disabled': // boolean
-			case 'autofocus': // boolean
-			case 'required': // boolean
-				this[attribute.name] = false;
-				break;
+	public get form(): HTMLFormElement {
+		let parent = <HTMLElement>this.parentNode;
+		while (parent && parent.tagName !== 'FORM') {
+			parent = <HTMLElement>this.parentNode;
 		}
+		return <HTMLFormElement>parent;
 	}
 
 	/**
-	 * The setAttributeNode() method adds a new Attr node to the specified element.
+	 * Returns text length.
 	 *
-	 * @override
-	 * @param attribute Attribute.
-	 * @returns Replaced attribute.
+	 * @param Text length.
 	 */
-	public setAttributeNode(attribute: Attr): Attr {
-		const replacedAttribute = super.setAttributeNode(attribute);
+	public get textLength(): number {
+		return this.value.length;
+	}
 
-		switch (attribute.name) {
-			case 'selectionstart': // number
-				this.selectionStart = !!attribute.value ? Number(attribute.value) : 0;
-				break;
-			case 'selectionend': // number
-				this.selectionEnd = !!attribute.value ? Number(attribute.value) : 0;
-				break;
-			case 'minlength': // number
-				this.minLength = !!attribute.value ? Number(attribute.value) : -1;
-				break;
-			case 'maxlength': // number
-				this.maxLength = !!attribute.value ? Number(attribute.value) : -1;
-				break;
-			case 'name': // string
-			case 'placeholder': // string
-			case 'inputmode': // string
-			case 'cols': // string
-			case 'rows': // string
-			case 'autocomplete': // string
-				this[attribute.name] = attribute.value || '';
-				break;
-			case 'selectiondirection': // string
-				this.selectionDirection = attribute.value || '';
+	/**
+	 * Set selection range.
+	 *
+	 * @param start Start.
+	 * @param end End.
+	 * @param [direction="none"] Direction.
+	 */
+	public setSelectionRange(start: number, end: number, direction = 'none'): void {
+		this._selectionEnd = Math.min(end, this.value.length);
+		this._selectionStart = Math.min(start, this._selectionEnd);
+		this._selectionDirection =
+			direction === HTMLInputElementSelectionDirectionEnum.forward ||
+			direction === HTMLInputElementSelectionDirectionEnum.backward
+				? direction
+				: HTMLInputElementSelectionDirectionEnum.none;
+		this.dispatchEvent(new Event('select', { bubbles: true, cancelable: true }));
+	}
+
+	/**
+	 * Set range text.
+	 *
+	 * @param replacement Replacement.
+	 * @param [start] Start.
+	 * @param [end] End.
+	 * @param [direction] Direction.
+	 */
+	public setRangeText(
+		replacement,
+		start = null,
+		end = null,
+		selectionMode = HTMLInputElementSelectionModeEnum.preserve
+	): void {
+		if (start === null) {
+			start = this._selectionStart;
+		}
+		if (end === null) {
+			end = this._selectionEnd;
+		}
+
+		if (start > end) {
+			throw new DOMException(
+				'The index is not in the allowed range.',
+				DOMExceptionNameEnum.invalidStateError
+			);
+		}
+
+		start = Math.min(start, this.value.length);
+		end = Math.min(end, this.value.length);
+
+		const val = this.value;
+		let selectionStart = this._selectionStart;
+		let selectionEnd = this._selectionEnd;
+
+		this.value = val.slice(0, start) + replacement + val.slice(end);
+
+		const newEnd = start + this.value.length;
+
+		switch (selectionMode) {
+			case HTMLInputElementSelectionModeEnum.select:
+				this.setSelectionRange(start, newEnd);
 				break;
-			case 'value': // string
-				this._value = attribute.value || '';
+			case HTMLInputElementSelectionModeEnum.start:
+				this.setSelectionRange(start, start);
 				break;
-			case 'readonly': //  boolean
-				this.readOnly = attribute.value != null;
+			case HTMLInputElementSelectionModeEnum.end:
+				this.setSelectionRange(newEnd, newEnd);
 				break;
-			case 'disabled': // boolean
-			case 'autofocus': // boolean
-			case 'required': // boolean
-				this[attribute.name] = attribute.value != null;
+			default:
+				const delta = replacement.length - (end - start);
+
+				if (selectionStart > end) {
+					selectionStart += delta;
+				} else if (selectionStart > start) {
+					selectionStart = start;
+				}
+
+				if (selectionEnd > end) {
+					selectionEnd += delta;
+				} else if (selectionEnd > start) {
+					selectionEnd = newEnd;
+				}
+
+				this.setSelectionRange(selectionStart, selectionEnd);
 				break;
 		}
+	}
 
-		return replacedAttribute;
+	/**
+	 * Checks validity.
+	 *
+	 * @return "true" if validation does'nt fail.
+	 */
+	public checkValidity(): boolean {
+		return true;
 	}
 
 	/**
@@ -217,23 +460,11 @@ export default class HTMLTextAreaElement extends HTMLElement {
 	public cloneNode(deep = false): HTMLTextAreaElement {
 		const clone = <HTMLTextAreaElement>super.cloneNode(deep);
 
-		clone.name = this.name;
-		clone.disabled = this.disabled;
-		clone.autofocus = this.autofocus;
-		clone.required = this.required;
 		clone._value = this._value;
-		clone.autocomplete = this.autocomplete;
-		clone.cols = this.cols;
-		clone.rows = this.rows;
-		clone.minLength = this.minLength;
-		clone.maxLength = this.maxLength;
-		clone.placeholder = this.placeholder;
-		clone.readOnly = this.readOnly;
-		clone.selectionStart = this.selectionStart;
-		clone.selectionEnd = this.selectionEnd;
-		clone.selectionDirection = this.selectionDirection;
+		clone._selectionStart = this._selectionStart;
+		clone._selectionEnd = this._selectionEnd;
+		clone._selectionDirection = this._selectionDirection;
 		clone.defaultValue = this.defaultValue;
-		clone.inputmode = this.inputmode;
 
 		return clone;
 	}
diff --git a/packages/happy-dom/src/nodes/svg-element/SVGSVGElement.ts b/packages/happy-dom/src/nodes/svg-element/SVGSVGElement.ts
index 7f665e2a..e8f00943 100644
--- a/packages/happy-dom/src/nodes/svg-element/SVGSVGElement.ts
+++ b/packages/happy-dom/src/nodes/svg-element/SVGSVGElement.ts
@@ -7,19 +7,140 @@ import SVGAngle from './SVGAngle';
 import SVGNumber from './SVGNumber';
 import SVGTransform from './SVGTransform';
 import SVGAnimatedRect from './SVGAnimatedRect';
-import Attr from '../../attribute/Attr';
 
 /**
  * SVGSVGElement.
  */
 export default class SVGSVGElement extends SVGGraphicsElement {
-	public preserveAspectRatio = 'xMidYMid meet';
-	public width = '';
-	public height = '';
-	public x = '';
-	public y = '';
-	public contentScriptType = '';
-	public currentScale = 1;
+	/**
+	 * Returns preserveAspectRatio.
+	 *
+	 * @return PreserveAspectRatio.
+	 */
+	public get preserveAspectRatio(): string {
+		return this.getAttributeNS(null, 'preserveAspectRatio') || 'xMidYMid meet';
+	}
+
+	/**
+	 * Sets preserveAspectRatio.
+	 *
+	 * @param preserveAspectRatio PreserveAspectRatio.
+	 */
+	public set preserveAspectRatio(preserveAspectRatio: string) {
+		this.setAttributeNS(null, 'preserveAspectRatio', preserveAspectRatio);
+	}
+
+	/**
+	 * Returns width.
+	 *
+	 * @return Width.
+	 */
+	public get width(): string {
+		return this.getAttributeNS(null, 'width') || '';
+	}
+
+	/**
+	 * Sets width.
+	 *
+	 * @param width Width.
+	 */
+	public set width(width: string) {
+		this.setAttributeNS(null, 'width', width);
+	}
+
+	/**
+	 * Returns height.
+	 *
+	 * @return Height.
+	 */
+	public get height(): string {
+		return this.getAttributeNS(null, 'height') || '';
+	}
+
+	/**
+	 * Sets height.
+	 *
+	 * @param height Height.
+	 */
+	public set height(height: string) {
+		this.setAttributeNS(null, 'height', height);
+	}
+
+	/**
+	 * Returns x.
+	 *
+	 * @return X.
+	 */
+	public get x(): string {
+		return this.getAttributeNS(null, 'x') || '';
+	}
+
+	/**
+	 * Sets x.
+	 *
+	 * @param x X.
+	 */
+	public set x(x: string) {
+		this.setAttributeNS(null, 'x', x);
+	}
+
+	/**
+	 * Returns y.
+	 *
+	 * @return Y.
+	 */
+	public get y(): string {
+		return this.getAttributeNS(null, 'y') || '';
+	}
+
+	/**
+	 * Sets y.
+	 *
+	 * @param y Y.
+	 */
+	public set y(y: string) {
+		this.setAttributeNS(null, 'y', y);
+	}
+
+	/**
+	 * Returns contentScriptType.
+	 *
+	 * @return ContentScriptType.
+	 */
+	public get contentScriptType(): string {
+		return this.getAttributeNS(null, 'contentScriptType') || '';
+	}
+
+	/**
+	 * Sets contentScriptType.
+	 *
+	 * @param contentScriptType ContentScriptType.
+	 */
+	public set contentScriptType(contentScriptType: string) {
+		this.setAttributeNS(null, 'contentScriptType', contentScriptType);
+	}
+
+	/**
+	 * Returns currentScale.
+	 *
+	 * @return CurrentScale.
+	 */
+	public get currentScale(): number {
+		const currentScale = this.getAttributeNS(null, 'currentScale');
+		if (currentScale !== null) {
+			return parseFloat(currentScale);
+		}
+		return 1;
+	}
+
+	/**
+	 * Sets currentScale.
+	 *
+	 * @param currentScale CurrentScale.
+	 */
+	public set currentScale(currentScale: number) {
+		this.setAttributeNS(null, 'currentScale', String(currentScale));
+	}
 
 	/**
 	 * Returns viewport.
@@ -134,7 +255,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Number.
 	 */
-	public get createSVGNumber(): SVGNumber {
+	public createSVGNumber(): SVGNumber {
 		return new SVGNumber();
 	}
 
@@ -143,7 +264,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Length.
 	 */
-	public get createSVGLength(): SVGLength {
+	public createSVGLength(): SVGLength {
 		return new SVGLength();
 	}
 
@@ -152,7 +273,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Angle.
 	 */
-	public get createSVGAngle(): SVGAngle {
+	public createSVGAngle(): SVGAngle {
 		return new SVGAngle();
 	}
 
@@ -161,7 +282,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Point.
 	 */
-	public get createSVGPoint(): SVGPoint {
+	public createSVGPoint(): SVGPoint {
 		return new SVGPoint();
 	}
 
@@ -170,7 +291,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Rect.
 	 */
-	public get createSVGRect(): SVGRect {
+	public createSVGRect(): SVGRect {
 		return new SVGRect();
 	}
 
@@ -179,79 +300,7 @@ export default class SVGSVGElement extends SVGGraphicsElement {
 	 *
 	 * @returns Transform.
 	 */
-	public get createSVGTransform(): SVGTransform {
+	public createSVGTransform(): SVGTransform {
 		return new SVGTransform();
 	}
-
-	/**
-	 * Removes an Attr node.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 */
-	public removeAttributeNode(attribute: Attr): void {
-		super.removeAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'preserveAspectRatio': // string
-				this[attribute.name] = 'xMidYMid meet';
-				break;
-			case 'width': // string
-			case 'height': // string
-			case 'x': // string
-			case 'y': // string
-			case 'contentScriptType': // string
-				this[attribute.name] = '';
-				break;
-			case 'currentScale': // number
-				this[attribute.name] = 1;
-				break;
-		}
-	}
-
-	/**
-	 * The setAttributeNode() method adds a new Attr node to the specified element.
-	 *
-	 * @override
-	 * @param attribute Attribute.
-	 * @returns Replaced attribute.
-	 */
-	public setAttributeNode(attribute: Attr): Attr {
-		const replacedAttribute = super.setAttributeNode(attribute);
-
-		switch (attribute.name) {
-			case 'preserveAspectRatio': // string
-			case 'width': // string
-			case 'height': // string
-			case 'x': // string
-			case 'y': // string
-			case 'contentScriptType': // string
-				this[attribute.name] = attribute.value;
-				break;
-			case 'currentScale': // number
-				this[attribute.name] = !!attribute.value ? Number(attribute.value) : 0;
-				break;
-		}
-
-		return replacedAttribute;
-	}
-
-	/**
-	 * Clones a node.
-	 *
-	 * @override
-	 * @param [deep=false] "true" to clone deep.
-	 * @return Cloned node.
-	 */
-	public cloneNode(deep = false): SVGSVGElement {
-		const clone = <SVGSVGElement>super.cloneNode(deep);
-		clone.preserveAspectRatio = this.preserveAspectRatio;
-		clone.width = this.width;
-		clone.height = this.height;
-		clone.x = this.x;
-		clone.y = this.y;
-		clone.contentScriptType = this.contentScriptType;
-		clone.currentScale = this.currentScale;
-		return clone;
-	}
 }


Test patch:
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 d7d61c7b..1f63f550 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
@@ -2,18 +2,6 @@ import Window from '../../../src/window/Window';
 import Document from '../../../src/nodes/document/Document';
 import HTMLFormElement from '../../../src/nodes/html-form-element/HTMLFormElement';
 
-const PROPERTIES = {
-	name: '',
-	method: 'get',
-	target: '',
-	action: '',
-	encoding: '',
-	enctype: '',
-	acceptCharset: '',
-	autocomplete: '',
-	noValidate: ''
-};
-
 describe('HTMLFormElement', () => {
 	let window: Window;
 	let document: Document;
@@ -25,24 +13,44 @@ describe('HTMLFormElement', () => {
 		element = <HTMLFormElement>document.createElement('form');
 	});
 
-	describe('removeAttributeNode()', () => {
-		test('Sets properties to its default value.', () => {
-			const newForm = <HTMLFormElement>document.createElement('form');
+	for (const property of [
+		'name',
+		'target',
+		'action',
+		'encoding',
+		'enctype',
+		'acceptCharset',
+		'autocomplete',
+		'noValidate'
+	]) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe('');
+				element.setAttribute(property, 'value');
+				expect(element[property]).toBe('value');
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 'value';
+				expect(element.getAttribute(property)).toBe('value');
+			});
+		});
+	}
 
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				element.removeAttribute(key);
-				expect(element[key]).toBe(newForm[key]);
-			}
+	describe('get method()', () => {
+		test('Returns attribute value.', () => {
+			expect(element.method).toBe('get');
+			element.setAttribute('method', 'post');
+			expect(element.method).toBe('post');
 		});
 	});
 
-	describe('setAttributeNode()', () => {
-		test('Sets attributes as properties.', () => {
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				expect(element[key]).toBe(PROPERTIES[key]);
-			}
+	describe('set method()', () => {
+		test('Sets attribute value.', () => {
+			element.method = 'post';
+			expect(element.getAttribute('method')).toBe('post');
 		});
 	});
 });
diff --git a/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts b/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
index ad1cfcaa..e2c9d7ba 100644
--- a/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-input-element/HTMLInputElement.test.ts
@@ -3,36 +3,7 @@ import Document from '../../../src/nodes/document/Document';
 import HTMLInputElement from '../../../src/nodes/html-input-element/HTMLInputElement';
 import File from '../../../src/file/File';
 import HTMLInputElementSelectionModeEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionModeEnum';
-
-const PROPERTIES = {
-	name: 'name',
-	type: 'type',
-	disabled: true,
-	autofocus: true,
-	required: true,
-	checked: true,
-	indeterminate: true,
-	alt: 'alt',
-	height: 10,
-	src: 'src',
-	value: 'value',
-	width: 10,
-	accept: 'accept',
-	allowdirs: 'allowdirs',
-	autocomplete: 'autocomplete',
-	min: 'min',
-	max: 'max',
-	minLength: 10,
-	maxLength: 10,
-	pattern: 'pattern',
-	placeholder: 'placeholder',
-	readOnly: true,
-	size: 10,
-	defaultValue: 'defaultValue',
-	multiple: true,
-	step: 'step',
-	inputmode: 'inputmode'
-};
+import HTMLInputElementSelectionDirectionEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum';
 
 describe('HTMLInputElement', () => {
 	let window: Window;
@@ -127,6 +98,17 @@ describe('HTMLInputElement', () => {
 				element.type = type;
 				element.value = '\n\rVALUE\n\r';
 				expect(element.value).toBe('VALUE');
+				expect(element.selectionStart).toBe(5);
+				expect(element.selectionEnd).toBe(5);
+			});
+
+			test(`Sets selection range.`, () => {
+				element.type = type;
+				element.selectionDirection = HTMLInputElementSelectionDirectionEnum.forward;
+				element.value = 'VALUE';
+				expect(element.selectionStart).toBe(5);
+				expect(element.selectionEnd).toBe(5);
+				expect(element.selectionDirection).toBe(HTMLInputElementSelectionDirectionEnum.none);
 			});
 		}
 
@@ -208,6 +190,186 @@ describe('HTMLInputElement', () => {
 		});
 	});
 
+	describe('get selectionStart()', () => {
+		test('Returns the length of the attribute "value" if value has not been set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			expect(element.selectionStart).toBe(10);
+		});
+
+		test('Returns the length of the value set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionStart = 5;
+			expect(element.selectionStart).toBe(5);
+		});
+	});
+
+	describe('set selectionStart()', () => {
+		test('Sets the value to the length of the property "value" if it is out of range.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionStart = 20;
+			expect(element.selectionStart).toBe(10);
+		});
+
+		test('Sets the property.', () => {
+			element.value = 'TEST_VALUE';
+			element.selectionStart = 5;
+			expect(element.selectionStart).toBe(5);
+		});
+	});
+
+	describe('get selectionEnd()', () => {
+		test('Returns the length of the attribute "value" if value has not been set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			expect(element.selectionEnd).toBe(10);
+		});
+
+		test('Returns the length of the value set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionEnd = 5;
+			expect(element.selectionEnd).toBe(5);
+		});
+	});
+
+	describe('set selectionEnd()', () => {
+		test('Sets the value to the length of the property "value" if it is out of range.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionEnd = 20;
+			expect(element.selectionEnd).toBe(10);
+		});
+
+		test('Sets the property.', () => {
+			element.value = 'TEST_VALUE';
+			element.selectionEnd = 5;
+			expect(element.selectionEnd).toBe(5);
+		});
+	});
+
+	for (const property of [
+		'disabled',
+		'autofocus',
+		'required',
+		'checked',
+		'indeterminate',
+		'multiple',
+		'readOnly'
+	]) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe(false);
+				element.setAttribute(property, '');
+				expect(element[property]).toBe(true);
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = true;
+				expect(element.getAttribute(property)).toBe('');
+			});
+		});
+	}
+
+	for (const property of [
+		'name',
+		'alt',
+		'src',
+		'accept',
+		'allowdirs',
+		'autocomplete',
+		'min',
+		'max',
+		'pattern',
+		'placeholder',
+		'step',
+		'inputmode'
+	]) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe('');
+				element.setAttribute(property, 'value');
+				expect(element[property]).toBe('value');
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 'value';
+				expect(element.getAttribute(property)).toBe('value');
+			});
+		});
+	}
+
+	for (const property of ['height', 'width']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe(0);
+				element[property] = 20;
+				expect(element[property]).toBe(20);
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element.setAttribute(property, '50');
+				expect(element[property]).toBe(0);
+				element[property] = 50;
+				expect(element[property]).toBe(50);
+				expect(element.getAttribute(property)).toBe('50');
+			});
+		});
+	}
+
+	for (const property of ['minLength', 'maxLength']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe(-1);
+				element.setAttribute(property, '50');
+				expect(element[property]).toBe(50);
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 50;
+				expect(element[property]).toBe(50);
+				expect(element.getAttribute(property)).toBe('50');
+			});
+		});
+	}
+
+	describe('get type()', () => {
+		test('Returns attribute value.', () => {
+			expect(element.type).toBe('text');
+			element.setAttribute('type', 'date');
+			expect(element.type).toBe('date');
+		});
+	});
+
+	describe('set type()', () => {
+		test('Sets attribute value.', () => {
+			element.type = 'date';
+			expect(element.getAttribute('type')).toBe('date');
+		});
+	});
+
+	describe('get size()', () => {
+		test('Returns attribute value.', () => {
+			expect(element.size).toBe(20);
+			element.size = 50;
+			expect(element.size).toBe(50);
+		});
+	});
+
+	describe('set size()', () => {
+		test('Sets attribute value.', () => {
+			element.setAttribute('size', '50');
+			expect(element.size).toBe(50);
+			element.size = 60;
+			expect(element.size).toBe(60);
+			expect(element.getAttribute('size')).toBe('60');
+		});
+	});
+
 	describe('setSelectionRange()', () => {
 		test('Sets selection range.', () => {
 			element.value = 'TEST_VALUE';
@@ -260,24 +422,63 @@ describe('HTMLInputElement', () => {
 		});
 	});
 
-	describe('removeAttributeNode()', () => {
-		test('Sets properties to its default value.', () => {
-			const newInput = <HTMLInputElement>document.createElement('input');
+	describe('cloneNode()', () => {
+		test('Clones when type is "checkbox".', () => {
+			element.type = 'checkbox';
+
+			const clone = element.cloneNode(true);
+
+			expect(clone.formAction).toBe(element.formAction);
+			expect(clone.formMethod).toBe(element.formMethod);
+			expect(clone.formNoValidate).toBe(element.formNoValidate);
+			expect(clone.value).toBe(element.value);
+			expect(clone.height).toBe(element.height);
+			expect(clone.width).toBe(element.width);
+			expect(clone.defaultChecked).toBe(element.defaultChecked);
+			expect(clone.files).toEqual(element.files);
+			expect(clone.selectionStart).toBe(element.selectionStart);
+			expect(clone.selectionEnd).toBe(element.selectionEnd);
+			expect(clone.selectionDirection).toBe(element.selectionDirection);
+		});
 
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				element.removeAttribute(key);
-				expect(element[key]).toBe(newInput[key]);
-			}
+		test('Clones when type is "search".', () => {
+			element.type = 'search';
+			element.value = 'TEST_VALUE';
+			element.selectionStart = 4;
+			element.selectionEnd = 4;
+
+			const clone = element.cloneNode(true);
+
+			expect(clone.formAction).toBe(element.formAction);
+			expect(clone.formMethod).toBe(element.formMethod);
+			expect(clone.formNoValidate).toBe(element.formNoValidate);
+			expect(clone.value).toBe(element.value);
+			expect(clone.height).toBe(element.height);
+			expect(clone.width).toBe(element.width);
+			expect(clone.defaultChecked).toBe(element.defaultChecked);
+			expect(clone.files).toEqual(element.files);
+			expect(clone.selectionStart).toBe(element.selectionStart);
+			expect(clone.selectionEnd).toBe(element.selectionEnd);
+			expect(clone.selectionDirection).toBe(element.selectionDirection);
 		});
-	});
 
-	describe('setAttributeNode()', () => {
-		test('Sets attributes as properties.', () => {
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				expect(element[key]).toBe(PROPERTIES[key]);
-			}
+		test('Clones when type is "file".', () => {
+			element.type = 'file';
+			element.files.push(new File(['test'], 'file.jpg'));
+
+			const clone = element.cloneNode(true);
+
+			expect(clone.formAction).toBe(element.formAction);
+			expect(clone.formMethod).toBe(element.formMethod);
+			expect(clone.formNoValidate).toBe(element.formNoValidate);
+			expect(clone.value).toBe(element.value);
+			expect(clone.height).toBe(element.height);
+			expect(clone.width).toBe(element.width);
+			expect(clone.defaultChecked).toBe(element.defaultChecked);
+			expect(clone.files).toEqual(element.files);
+			expect(clone.selectionStart).toEqual(element.selectionStart);
+			expect(clone.selectionEnd).toEqual(element.selectionEnd);
+			expect(clone.selectionDirection).toEqual(element.selectionDirection);
 		});
 	});
 });
diff --git a/packages/happy-dom/test/nodes/html-text-area-element/HTMLTextAreaElement.test.ts b/packages/happy-dom/test/nodes/html-text-area-element/HTMLTextAreaElement.test.ts
index ff13e695..e26552af 100644
--- a/packages/happy-dom/test/nodes/html-text-area-element/HTMLTextAreaElement.test.ts
+++ b/packages/happy-dom/test/nodes/html-text-area-element/HTMLTextAreaElement.test.ts
@@ -1,21 +1,8 @@
 import Window from '../../../src/window/Window';
 import Document from '../../../src/nodes/document/Document';
 import HTMLTextAreaElement from '../../../src/nodes/html-text-area-element/HTMLTextAreaElement';
-
-const PROPERTIES = {
-	name: 'name',
-	disabled: true,
-	autofocus: true,
-	required: true,
-	value: 'value',
-	autocomplete: 'autocomplete',
-	cols: 'cols',
-	rows: 'rows',
-	minLength: 10,
-	maxLength: 10,
-	placeholder: 'placeholder',
-	readOnly: true
-};
+import HTMLInputElementSelectionModeEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionModeEnum';
+import HTMLInputElementSelectionDirectionEnum from '../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum';
 
 describe('HTMLTextAreaElement', () => {
 	let window: Window;
@@ -28,24 +15,200 @@ describe('HTMLTextAreaElement', () => {
 		element = <HTMLTextAreaElement>document.createElement('textarea');
 	});
 
-	describe('removeAttributeNode()', () => {
-		test('Sets properties to its default value.', () => {
-			const newTextarea = <HTMLTextAreaElement>document.createElement('textarea');
+	describe('get value()', () => {
+		test('Returns the attribute "value" if it has not been set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			expect(element.value).toBe('TEST_VALUE');
+		});
+
+		test('Returns value set using the property.', () => {
+			element.value = 'TEST_VALUE';
+			expect(element.value).toBe('TEST_VALUE');
+		});
+	});
+
+	describe('set value()', () => {
+		test('Sets a value and selection range.', () => {
+			element.selectionDirection = HTMLInputElementSelectionDirectionEnum.forward;
+			element.value = 'TEST_VALUE';
+			expect(element.value).toBe('TEST_VALUE');
+			expect(element.selectionStart).toBe(10);
+			expect(element.selectionEnd).toBe(10);
+			expect(element.selectionDirection).toBe(HTMLInputElementSelectionDirectionEnum.none);
+		});
+	});
+
+	describe('get selectionStart()', () => {
+		test('Returns the length of the attribute "value" if value has not been set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			expect(element.selectionStart).toBe(10);
+		});
 
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				element.removeAttribute(key);
-				expect(element[key]).toBe(newTextarea[key]);
-			}
+		test('Returns the length of the value set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionStart = 5;
+			expect(element.selectionStart).toBe(5);
 		});
 	});
 
-	describe('setAttributeNode()', () => {
-		test('Sets attributes as properties.', () => {
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				expect(element[key]).toBe(PROPERTIES[key]);
-			}
+	describe('set selectionStart()', () => {
+		test('Sets the value to the length of the property "value" if it is out of range.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionStart = 20;
+			expect(element.selectionStart).toBe(10);
+		});
+
+		test('Sets the property.', () => {
+			element.value = 'TEST_VALUE';
+			element.selectionStart = 5;
+			expect(element.selectionStart).toBe(5);
+		});
+	});
+
+	describe('get selectionEnd()', () => {
+		test('Returns the length of the attribute "value" if value has not been set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			expect(element.selectionEnd).toBe(10);
+		});
+
+		test('Returns the length of the value set using the property.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionEnd = 5;
+			expect(element.selectionEnd).toBe(5);
+		});
+	});
+
+	describe('set selectionEnd()', () => {
+		test('Sets the value to the length of the property "value" if it is out of range.', () => {
+			element.setAttribute('value', 'TEST_VALUE');
+			element.selectionEnd = 20;
+			expect(element.selectionEnd).toBe(10);
+		});
+
+		test('Sets the property.', () => {
+			element.value = 'TEST_VALUE';
+			element.selectionEnd = 5;
+			expect(element.selectionEnd).toBe(5);
+		});
+	});
+
+	for (const property of ['disabled', 'autofocus', 'required', 'readOnly']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe(false);
+				element.setAttribute(property, '');
+				expect(element[property]).toBe(true);
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = true;
+				expect(element.getAttribute(property)).toBe('');
+			});
+		});
+	}
+
+	for (const property of ['name', 'autocomplete', 'cols', 'rows', 'placeholder']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe('');
+				element.setAttribute(property, 'value');
+				expect(element[property]).toBe('value');
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 'value';
+				expect(element.getAttribute(property)).toBe('value');
+			});
+		});
+	}
+
+	for (const property of ['minLength', 'maxLength']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe(-1);
+				element.setAttribute(property, '50');
+				expect(element[property]).toBe(50);
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 50;
+				expect(element[property]).toBe(50);
+				expect(element.getAttribute(property)).toBe('50');
+			});
+		});
+	}
+
+	describe('setSelectionRange()', () => {
+		test('Sets selection range.', () => {
+			element.value = 'TEST_VALUE';
+			element.setSelectionRange(1, 5, 'forward');
+			expect(element.selectionStart).toBe(1);
+			expect(element.selectionEnd).toBe(5);
+			expect(element.selectionDirection).toBe('forward');
+		});
+
+		test('Sets selection end to the value length if out of range.', () => {
+			element.value = 'TEST_VALUE';
+			element.setSelectionRange(1, 100, 'backward');
+			expect(element.selectionStart).toBe(1);
+			expect(element.selectionEnd).toBe(10);
+			expect(element.selectionDirection).toBe('backward');
+		});
+	});
+
+	describe('setRangeText()', () => {
+		test('Sets a range text with selection mode set to "preserve".', () => {
+			element.value = 'TEST_VALUE';
+			element.setRangeText('_NEW_', 4, 5);
+			expect(element.selectionStart).toBe(14);
+			expect(element.selectionEnd).toBe(14);
+			expect(element.value).toBe('TEST_NEW_VALUE');
+		});
+
+		test('Sets a range text with selection mode set to "select".', () => {
+			element.value = 'TEST_VALUE';
+			element.setRangeText('_NEW_', 4, 5, HTMLInputElementSelectionModeEnum.select);
+			expect(element.selectionStart).toBe(4);
+			expect(element.selectionEnd).toBe(14);
+			expect(element.value).toBe('TEST_NEW_VALUE');
+		});
+
+		test('Sets a range text with selection mode set to "start".', () => {
+			element.value = 'TEST_VALUE';
+			element.setRangeText('_NEW_', 4, 5, HTMLInputElementSelectionModeEnum.start);
+			expect(element.selectionStart).toBe(4);
+			expect(element.selectionEnd).toBe(4);
+			expect(element.value).toBe('TEST_NEW_VALUE');
+		});
+
+		test('Sets a range text with selection mode set to "end".', () => {
+			element.value = 'TEST_VALUE';
+			element.setRangeText('_NEW_', 4, 5, HTMLInputElementSelectionModeEnum.end);
+			expect(element.selectionStart).toBe(14);
+			expect(element.selectionEnd).toBe(14);
+			expect(element.value).toBe('TEST_NEW_VALUE');
+		});
+	});
+
+	describe('cloneNode()', () => {
+		test('Clones node.', () => {
+			element.value = 'TEST_VALUE';
+			element.selectionStart = 4;
+			element.selectionEnd = 4;
+
+			const clone = element.cloneNode(true);
+
+			expect(clone.value).toBe(element.value);
+			expect(clone.defaultValue).toBe(element.defaultValue);
+			expect(clone.selectionStart).toBe(element.selectionStart);
+			expect(clone.selectionEnd).toBe(element.selectionEnd);
+			expect(clone.selectionDirection).toBe(element.selectionDirection);
 		});
 	});
 });
diff --git a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
index f7383201..aeac87e2 100644
--- a/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
+++ b/packages/happy-dom/test/nodes/svg-element/SVGSVGElement.test.ts
@@ -2,16 +2,13 @@ import Window from '../../../src/window/Window';
 import Document from '../../../src/nodes/document/Document';
 import SVGSVGElement from '../../../src/nodes/svg-element/SVGSVGElement';
 import NamespaceURI from '../../../src/config/NamespaceURI';
-
-const PROPERTIES = {
-	preserveAspectRatio: 'preserveAspectRatio',
-	width: 'width',
-	height: 'height',
-	x: 'x',
-	y: 'y',
-	contentScriptType: 'contentScriptType',
-	currentScale: 10
-};
+import SVGRect from '../../../src/nodes/svg-element/SVGRect';
+import SVGPoint from '../../../src/nodes/svg-element/SVGPoint';
+import SVGAnimatedRect from '../../../src/nodes/svg-element/SVGAnimatedRect';
+import SVGNumber from '../../../src/nodes/svg-element/SVGNumber';
+import SVGLength from '../../../src/nodes/svg-element/SVGLength';
+import SVGAngle from '../../../src/nodes/svg-element/SVGAngle';
+import SVGTransform from '../../../src/nodes/svg-element/SVGTransform';
 
 describe('SVGSVGElement', () => {
 	let window: Window;
@@ -24,24 +21,172 @@ describe('SVGSVGElement', () => {
 		element = <SVGSVGElement>document.createElementNS(NamespaceURI.svg, 'svg');
 	});
 
-	describe('removeAttributeNode()', () => {
-		test('Sets properties to its default value.', () => {
-			const newSVG = <SVGSVGElement>document.createElementNS(NamespaceURI.svg, 'svg');
+	for (const property of ['width', 'height', 'x', 'y', 'contentScriptType']) {
+		describe(`get ${property}()`, () => {
+			test('Returns attribute value.', () => {
+				expect(element[property]).toBe('');
+				element.setAttribute(property, 'value');
+				expect(element[property]).toBe('value');
+			});
+		});
+
+		describe(`set ${property}()`, () => {
+			test('Sets attribute value.', () => {
+				element[property] = 'value';
+				expect(element.getAttribute(property)).toBe('value');
+			});
+		});
+	}
+
+	describe('get preserveAspectRatio()', () => {
+		test('Returns attribute value.', () => {
+			expect(element.preserveAspectRatio).toBe('xMidYMid meet');
+			element.setAttribute('preserveAspectRatio', 'xMidYMin');
+			expect(element.preserveAspectRatio).toBe('xMidYMin');
+		});
+	});
+
+	describe('set preserveAspectRatio()', () => {
+		test('Sets attribute value.', () => {
+			element.preserveAspectRatio = 'xMidYMin';
+			expect(element.getAttribute('preserveAspectRatio')).toBe('xMidYMin');
+		});
+	});
+
+	describe('get currentScale()', () => {
+		test('Returns attribute value.', () => {
+			expect(element.currentScale).toBe(1);
+			element.currentScale = 2;
+			expect(element.currentScale).toBe(2);
+		});
+	});
+
+	describe('set currentScale()', () => {
+		test('Sets attribute value.', () => {
+			element.setAttribute('currentScale', '2');
+			expect(element.currentScale).toBe(2);
+			element.currentScale = 3;
+			expect(element.currentScale).toBe(3);
+			expect(element.getAttribute('currentScale')).toBe('3');
+		});
+	});
+
+	describe('get viewport()', () => {
+		test('Returns an instanceof SVGRect.', () => {
+			expect(element.viewport instanceof SVGRect).toBe(true);
+		});
+	});
+
+	describe('get currentTranslate()', () => {
+		test('Returns an instanceof SVGPoint.', () => {
+			expect(element.currentTranslate instanceof SVGPoint).toBe(true);
+		});
+	});
+
+	describe('get viewBox()', () => {
+		test('Returns an instanceof SVGAnimatedRect with values from the attribute "viewBox".', () => {
+			element.setAttribute('viewBox', '0 0 100 100');
+			expect(element.viewBox instanceof SVGAnimatedRect).toBe(true);
+			expect(element.viewBox.baseVal).toEqual({
+				x: 0,
+				y: 0,
+				width: 100,
+				height: 100
+			});
+		});
+	});
+
+	describe('pauseAnimations()', () => {
+		test('Exists and does nothing.', () => {
+			expect(typeof element.pauseAnimations).toBe('function');
+			element.pauseAnimations();
+		});
+	});
+
+	describe('unpauseAnimations()', () => {
+		test('Exists and does nothing.', () => {
+			expect(typeof element.unpauseAnimations).toBe('function');
+			element.unpauseAnimations();
+		});
+	});
+
+	describe('getCurrentTime()', () => {
+		test('Returns "0".', () => {
+			expect(element.getCurrentTime()).toBe(0);
+		});
+	});
+
+	describe('setCurrentTime()', () => {
+		test('Exists and does nothing.', () => {
+			expect(typeof element.setCurrentTime).toBe('function');
+			element.setCurrentTime();
+		});
+	});
+
+	describe('getIntersectionList()', () => {
+		test('Returns an empty Array.', () => {
+			expect(element.getIntersectionList()).toEqual([]);
+		});
+	});
+
+	describe('getEnclosureList()', () => {
+		test('Returns an empty Array.', () => {
+			expect(element.getEnclosureList()).toEqual([]);
+		});
+	});
+
+	describe('checkIntersection()', () => {
+		test('Returns "false".', () => {
+			expect(element.checkIntersection()).toBe(false);
+		});
+	});
+
+	describe('checkEnclosure()', () => {
+		test('Returns "false".', () => {
+			expect(element.checkEnclosure()).toBe(false);
+		});
+	});
+
+	describe('deselectAll()', () => {
+		test('Exists and does nothing.', () => {
+			expect(typeof element.deselectAll).toBe('function');
+			element.deselectAll();
+		});
+	});
+
+	describe('createSVGNumber()', () => {
+		test('Returns an instance of SVGNumber.', () => {
+			expect(element.createSVGNumber() instanceof SVGNumber).toBe(true);
+		});
+	});
+
+	describe('createSVGLength()', () => {
+		test('Returns an instance of SVGLength.', () => {
+			expect(element.createSVGLength() instanceof SVGLength).toBe(true);
+		});
+	});
+
+	describe('createSVGAngle()', () => {
+		test('Returns an instance of SVGAngle.', () => {
+			expect(element.createSVGAngle() instanceof SVGAngle).toBe(true);
+		});
+	});
+
+	describe('createSVGPoint()', () => {
+		test('Returns an instance of SVGPoint.', () => {
+			expect(element.createSVGPoint() instanceof SVGPoint).toBe(true);
+		});
+	});
 
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				element.removeAttribute(key);
-				expect(element[key]).toBe(newSVG[key]);
-			}
+	describe('createSVGRect()', () => {
+		test('Returns an instance of SVGRect.', () => {
+			expect(element.createSVGRect() instanceof SVGRect).toBe(true);
 		});
 	});
 
-	describe('setAttributeNode()', () => {
-		test('Sets attributes as properties.', () => {
-			for (const key of Object.keys(PROPERTIES)) {
-				element.setAttribute(key, PROPERTIES[key]);
-				expect(element[key]).toBe(PROPERTIES[key]);
-			}
+	describe('createSVGTransform()', () => {
+		test('Returns an instance of SVGTransform.', () => {
+			expect(element.createSVGTransform() instanceof SVGTransform).toBe(true);
 		});
 	});
 });