happy-dom 2.24.5 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

Files changed (70) hide show
  1. package/lib/event/EventTarget.d.ts +9 -0
  2. package/lib/event/EventTarget.js +11 -0
  3. package/lib/event/EventTarget.js.map +1 -1
  4. package/lib/event/NonImplementedEventTypes.js +0 -1
  5. package/lib/event/NonImplementedEventTypes.js.map +1 -1
  6. package/lib/event/events/IPointerEventInit.d.ts +13 -0
  7. package/lib/event/events/IPointerEventInit.js +3 -0
  8. package/lib/event/events/IPointerEventInit.js.map +1 -0
  9. package/lib/event/events/PointerEvent.d.ts +24 -0
  10. package/lib/event/events/PointerEvent.js +64 -0
  11. package/lib/event/events/PointerEvent.js.map +1 -0
  12. package/lib/nodes/document/Document.d.ts +8 -1
  13. package/lib/nodes/document/Document.js +14 -1
  14. package/lib/nodes/document/Document.js.map +1 -1
  15. package/lib/nodes/document/IDocument.d.ts +1 -0
  16. package/lib/nodes/document-fragment/DocumentFragment.d.ts +1 -0
  17. package/lib/nodes/document-fragment/DocumentFragment.js +1 -0
  18. package/lib/nodes/document-fragment/DocumentFragment.js.map +1 -1
  19. package/lib/nodes/element/Element.d.ts +7 -0
  20. package/lib/nodes/element/Element.js +38 -2
  21. package/lib/nodes/element/Element.js.map +1 -1
  22. package/lib/nodes/element/IElement.d.ts +7 -0
  23. package/lib/nodes/html-element/HTMLElement.js +16 -4
  24. package/lib/nodes/html-element/HTMLElement.js.map +1 -1
  25. package/lib/nodes/html-input-element/HTMLInputElement.d.ts +9 -6
  26. package/lib/nodes/html-input-element/HTMLInputElement.js +18 -8
  27. package/lib/nodes/html-input-element/HTMLInputElement.js.map +1 -1
  28. package/lib/nodes/html-input-element/IHTMLInputElement.d.ts +0 -1
  29. package/lib/nodes/html-link-element/HTMLLinkElement.d.ts +5 -12
  30. package/lib/nodes/html-link-element/HTMLLinkElement.js +39 -68
  31. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  32. package/lib/nodes/html-script-element/HTMLScriptElement.d.ts +5 -12
  33. package/lib/nodes/html-script-element/HTMLScriptElement.js +21 -49
  34. package/lib/nodes/html-script-element/HTMLScriptElement.js.map +1 -1
  35. package/lib/nodes/html-style-element/HTMLStyleElement.js +1 -1
  36. package/lib/nodes/html-style-element/HTMLStyleElement.js.map +1 -1
  37. package/lib/nodes/node/INode.d.ts +1 -1
  38. package/lib/nodes/node/Node.d.ts +8 -13
  39. package/lib/nodes/node/Node.js +57 -63
  40. package/lib/nodes/node/Node.js.map +1 -1
  41. package/lib/nodes/shadow-root/ShadowRoot.d.ts +7 -0
  42. package/lib/nodes/shadow-root/ShadowRoot.js +16 -0
  43. package/lib/nodes/shadow-root/ShadowRoot.js.map +1 -1
  44. package/lib/query-selector/SelectorItem.d.ts +8 -0
  45. package/lib/query-selector/SelectorItem.js +29 -11
  46. package/lib/query-selector/SelectorItem.js.map +1 -1
  47. package/lib/window/Window.d.ts +2 -0
  48. package/lib/window/Window.js +2 -0
  49. package/lib/window/Window.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/event/EventTarget.ts +12 -0
  52. package/src/event/NonImplementedEventTypes.ts +0 -1
  53. package/src/event/events/IPointerEventInit.ts +14 -0
  54. package/src/event/events/PointerEvent.ts +42 -0
  55. package/src/nodes/document/Document.ts +11 -1
  56. package/src/nodes/document/IDocument.ts +1 -0
  57. package/src/nodes/document-fragment/DocumentFragment.ts +1 -0
  58. package/src/nodes/element/Element.ts +41 -2
  59. package/src/nodes/element/IElement.ts +8 -0
  60. package/src/nodes/html-element/HTMLElement.ts +21 -4
  61. package/src/nodes/html-input-element/HTMLInputElement.ts +21 -9
  62. package/src/nodes/html-input-element/IHTMLInputElement.ts +0 -1
  63. package/src/nodes/html-link-element/HTMLLinkElement.ts +46 -73
  64. package/src/nodes/html-script-element/HTMLScriptElement.ts +24 -49
  65. package/src/nodes/html-style-element/HTMLStyleElement.ts +1 -1
  66. package/src/nodes/node/INode.ts +1 -1
  67. package/src/nodes/node/Node.ts +60 -62
  68. package/src/nodes/shadow-root/ShadowRoot.ts +14 -0
  69. package/src/query-selector/SelectorItem.ts +38 -19
  70. package/src/window/Window.ts +2 -0
@@ -23,6 +23,7 @@ export default interface IDocument extends IParentNode {
23
23
  readonly doctype: IDocumentType;
24
24
  readonly body: IHTMLElement;
25
25
  readonly head: IHTMLElement;
26
+ readonly activeElement: IHTMLElement;
26
27
 
27
28
  /**
28
29
  * Replaces the document HTML with new HTML.
@@ -13,6 +13,7 @@ import HTMLCollectionFactory from '../element/HTMLCollectionFactory';
13
13
  export default class DocumentFragment extends Node implements IDocumentFragment {
14
14
  public nodeType = Node.DOCUMENT_FRAGMENT_NODE;
15
15
  public readonly children: IHTMLCollection<IElement> = HTMLCollectionFactory.create();
16
+ public _rootNode: INode = this;
16
17
 
17
18
  /**
18
19
  * Last element child.
@@ -598,7 +598,7 @@ export default class Element extends Node implements IElement {
598
598
  (<IDocument>this.shadowRoot.ownerDocument) = this.ownerDocument;
599
599
  (<Element>this.shadowRoot.host) = this;
600
600
  (<string>this.shadowRoot.mode) = shadowRootInit.mode;
601
- this.shadowRoot.isConnected = this.isConnected;
601
+ (<ShadowRoot>this.shadowRoot)._connectToNode(this);
602
602
  return this.shadowRoot;
603
603
  }
604
604
 
@@ -636,7 +636,46 @@ export default class Element extends Node implements IElement {
636
636
  * @returns "true" if matching.
637
637
  */
638
638
  public matches(selector: string): boolean {
639
- return new SelectorItem(selector).match(this);
639
+ for (const part of selector.split(',')) {
640
+ if (new SelectorItem(part.trim()).match(this)) {
641
+ return true;
642
+ }
643
+ }
644
+ return false;
645
+ }
646
+
647
+ /**
648
+ * Traverses the Element and its parents (heading toward the document root) until it finds a node that matches the provided selector string.
649
+ *
650
+ * @param selector Selector.
651
+ * @returns Closest matching element.
652
+ */
653
+ public closest(selector: string): IElement {
654
+ let rootElement: IElement = this.ownerDocument.documentElement;
655
+ if (!this.isConnected) {
656
+ rootElement = this;
657
+ while (rootElement.parentNode) {
658
+ rootElement = <IElement>rootElement.parentNode;
659
+ }
660
+ }
661
+ const elements = rootElement.querySelectorAll(selector);
662
+
663
+ // eslint-disable-next-line
664
+ let parent: IElement = this;
665
+ while (parent) {
666
+ if (elements.includes(parent)) {
667
+ return parent;
668
+ }
669
+ parent = parent.parentElement;
670
+ }
671
+
672
+ // QuerySelectorAll() will not match the element it is looking in when searched for
673
+ // Therefore we need to check if it matches the root
674
+ if (rootElement.matches(selector)) {
675
+ return rootElement;
676
+ }
677
+
678
+ return null;
640
679
  }
641
680
 
642
681
  /**
@@ -152,6 +152,14 @@ export default interface IElement extends IChildNode, INonDocumentTypeChildNode,
152
152
  */
153
153
  matches(selector: string): boolean;
154
154
 
155
+ /**
156
+ * Traverses the Element and its parents (heading toward the document root) until it finds a node that matches the provided selector string.
157
+ *
158
+ * @param selector Selector.
159
+ * @returns Closest matching element.
160
+ */
161
+ closest(selector: string): IElement;
162
+
155
163
  /**
156
164
  * The setAttributeNode() method adds a new Attr node to the specified element.
157
165
  *
@@ -1,8 +1,9 @@
1
1
  import Element from '../element/Element';
2
- import Event from '../../event/Event';
3
2
  import IHTMLElement from './IHTMLElement';
4
3
  import CSSStyleDeclaration from '../../css/CSSStyleDeclaration';
5
4
  import Attr from '../../attribute/Attr';
5
+ import FocusEvent from '../../event/events/FocusEvent';
6
+ import PointerEvent from '../../event/events/PointerEvent';
6
7
 
7
8
  /**
8
9
  * HTML Element.
@@ -172,7 +173,7 @@ export default class HTMLElement extends Element implements IHTMLElement {
172
173
  * Triggers a click event.
173
174
  */
174
175
  public click(): void {
175
- const event = new Event('click', {
176
+ const event = new PointerEvent('click', {
176
177
  bubbles: true,
177
178
  composed: true
178
179
  });
@@ -185,8 +186,14 @@ export default class HTMLElement extends Element implements IHTMLElement {
185
186
  * Triggers a blur event.
186
187
  */
187
188
  public blur(): void {
189
+ if (this.ownerDocument['_activeElement'] !== this || !this.isConnected) {
190
+ return;
191
+ }
192
+
193
+ this.ownerDocument['_activeElement'] = null;
194
+
188
195
  for (const eventType of ['blur', 'focusout']) {
189
- const event = new Event(eventType, {
196
+ const event = new FocusEvent(eventType, {
190
197
  bubbles: true,
191
198
  composed: true
192
199
  });
@@ -200,8 +207,18 @@ export default class HTMLElement extends Element implements IHTMLElement {
200
207
  * Triggers a focus event.
201
208
  */
202
209
  public focus(): void {
210
+ if (this.ownerDocument['_activeElement'] === this || !this.isConnected) {
211
+ return;
212
+ }
213
+
214
+ if (this.ownerDocument['_activeElement'] !== null) {
215
+ this.ownerDocument['_activeElement'].blur();
216
+ }
217
+
218
+ this.ownerDocument['_activeElement'] = this;
219
+
203
220
  for (const eventType of ['focus', 'focusin']) {
204
- const event = new Event(eventType, {
221
+ const event = new FocusEvent(eventType, {
205
222
  bubbles: true,
206
223
  composed: true
207
224
  });
@@ -37,9 +37,6 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
37
37
  // Type specific: file
38
38
  public files: File[] = [];
39
39
 
40
- // Not categorized
41
- public defaultValue = '';
42
-
43
40
  // Type specific: text/password/search/tel/url/week/month
44
41
  private _selectionStart = null;
45
42
  private _selectionEnd = null;
@@ -384,21 +381,21 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
384
381
  }
385
382
 
386
383
  /**
387
- * Returns defaultvalue.
384
+ * Returns defaultValue.
388
385
  *
389
386
  * @returns Defaultvalue.
390
387
  */
391
- public get defaultvalue(): string {
388
+ public get defaultValue(): string {
392
389
  return this.getAttributeNS(null, 'defaultvalue') || '';
393
390
  }
394
391
 
395
392
  /**
396
- * Sets defaultvalue.
393
+ * Sets defaultValue.
397
394
  *
398
- * @param defaultvalue Defaultvalue.
395
+ * @param defaultValue Defaultvalue.
399
396
  */
400
- public set defaultvalue(defaultvalue: string) {
401
- this.setAttributeNS(null, 'defaultvalue', defaultvalue);
397
+ public set defaultValue(defaultValue: string) {
398
+ this.setAttributeNS(null, 'defaultvalue', defaultValue);
402
399
  }
403
400
 
404
401
  /**
@@ -780,6 +777,21 @@ export default class HTMLInputElement extends HTMLElement implements IHTMLInputE
780
777
  return this.value ? parseFloat(this.value) : NaN;
781
778
  }
782
779
 
780
+ /**
781
+ * Selects the text.
782
+ */
783
+ public select(): void {
784
+ if (!this._isSelectionSupported()) {
785
+ return null;
786
+ }
787
+
788
+ this._selectionStart = 0;
789
+ this._selectionEnd = this.value.length;
790
+ this._selectionDirection = HTMLInputElementSelectionDirectionEnum.none;
791
+
792
+ this.dispatchEvent(new Event('select', { bubbles: true, cancelable: true }));
793
+ }
794
+
783
795
  /**
784
796
  * Set selection range.
785
797
  *
@@ -35,7 +35,6 @@ export default interface IHTMLInputElement extends IHTMLElement {
35
35
  allowdirs: string;
36
36
  autocomplete: string;
37
37
  src: string;
38
- defaultvalue: string;
39
38
  readOnly: boolean;
40
39
  disabled: boolean;
41
40
  autofocus: boolean;
@@ -6,6 +6,7 @@ import Document from '../document/Document';
6
6
  import IHTMLLinkElement from './IHTMLLinkElement';
7
7
  import Event from '../../event/Event';
8
8
  import ErrorEvent from '../../event/events/ErrorEvent';
9
+ import INode from '../../nodes/node/INode';
9
10
 
10
11
  /**
11
12
  * HTML Link Element.
@@ -19,79 +20,6 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
19
20
  public readonly sheet: CSSStyleSheet = null;
20
21
  public _evaluateCSS = true;
21
22
 
22
- /**
23
- * Returns "true" if connected to DOM.
24
- *
25
- * @returns "true" if connected.
26
- */
27
- public get isConnected(): boolean {
28
- return this._isConnected;
29
- }
30
-
31
- /**
32
- * Sets the connected state.
33
- *
34
- * @param isConnected "true" if connected.
35
- */
36
- public set isConnected(isConnected) {
37
- if (this._isConnected !== isConnected) {
38
- this._isConnected = isConnected;
39
-
40
- for (const child of this.childNodes) {
41
- child.isConnected = isConnected;
42
- }
43
-
44
- // eslint-disable-next-line
45
- if (this.shadowRoot) {
46
- // eslint-disable-next-line
47
- this.shadowRoot.isConnected = isConnected;
48
- }
49
-
50
- if (isConnected && this._evaluateCSS) {
51
- const href = this.getAttributeNS(null, 'href');
52
- const rel = this.getAttributeNS(null, 'rel');
53
- if (href !== null && rel && rel.toLowerCase() === 'stylesheet') {
54
- (<Document>this.ownerDocument)._readyStateManager.startTask();
55
- ResourceFetcher.fetch({ window: this.ownerDocument.defaultView, url: href })
56
- .then(code => {
57
- const styleSheet = new CSSStyleSheet();
58
- styleSheet.replaceSync(code);
59
- (<CSSStyleSheet>this.sheet) = styleSheet;
60
- this.dispatchEvent(new Event('load'));
61
- (<Document>this.ownerDocument)._readyStateManager.endTask();
62
- })
63
- .catch(error => {
64
- this.dispatchEvent(
65
- new ErrorEvent('error', {
66
- message: error.message,
67
- error
68
- })
69
- );
70
- this.ownerDocument.defaultView.dispatchEvent(
71
- new ErrorEvent('error', {
72
- message: error.message,
73
- error
74
- })
75
- );
76
- (<Document>this.ownerDocument)._readyStateManager.endTask();
77
- if (
78
- !this._listeners['error'] &&
79
- !this.ownerDocument.defaultView._listeners['error']
80
- ) {
81
- this.ownerDocument.defaultView.console.error(error);
82
- }
83
- });
84
- }
85
- }
86
-
87
- if (isConnected && this.connectedCallback) {
88
- this.connectedCallback();
89
- } else if (!isConnected && this.disconnectedCallback) {
90
- this.disconnectedCallback();
91
- }
92
- }
93
- }
94
-
95
23
  /**
96
24
  * Returns as.
97
25
  *
@@ -286,4 +214,49 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
286
214
 
287
215
  return replacedAttribute;
288
216
  }
217
+
218
+ /**
219
+ * @override
220
+ */
221
+ public _connectToNode(parentNode: INode = null): void {
222
+ const isConnected = this.isConnected;
223
+ const isParentConnected = parentNode ? parentNode.isConnected : false;
224
+
225
+ super._connectToNode(parentNode);
226
+
227
+ if (isConnected !== isParentConnected && this._evaluateCSS) {
228
+ const href = this.getAttributeNS(null, 'href');
229
+ const rel = this.getAttributeNS(null, 'rel');
230
+
231
+ if (href !== null && rel && rel.toLowerCase() === 'stylesheet') {
232
+ (<Document>this.ownerDocument)._readyStateManager.startTask();
233
+ ResourceFetcher.fetch({ window: this.ownerDocument.defaultView, url: href })
234
+ .then(code => {
235
+ const styleSheet = new CSSStyleSheet();
236
+ styleSheet.replaceSync(code);
237
+ (<CSSStyleSheet>this.sheet) = styleSheet;
238
+ this.dispatchEvent(new Event('load'));
239
+ (<Document>this.ownerDocument)._readyStateManager.endTask();
240
+ })
241
+ .catch(error => {
242
+ this.dispatchEvent(
243
+ new ErrorEvent('error', {
244
+ message: error.message,
245
+ error
246
+ })
247
+ );
248
+ this.ownerDocument.defaultView.dispatchEvent(
249
+ new ErrorEvent('error', {
250
+ message: error.message,
251
+ error
252
+ })
253
+ );
254
+ (<Document>this.ownerDocument)._readyStateManager.endTask();
255
+ if (!this._listeners['error'] && !this.ownerDocument.defaultView._listeners['error']) {
256
+ this.ownerDocument.defaultView.console.error(error);
257
+ }
258
+ });
259
+ }
260
+ }
261
+ }
289
262
  }
@@ -4,6 +4,7 @@ import IHTMLScriptElement from './IHTMLScriptElement';
4
4
  import ScriptUtility from './ScriptUtility';
5
5
  import Event from '../../event/Event';
6
6
  import ErrorEvent from '../../event/events/ErrorEvent';
7
+ import INode from '../../nodes/node/INode';
7
8
 
8
9
  /**
9
10
  * HTML Script Element.
@@ -16,55 +17,6 @@ export default class HTMLScriptElement extends HTMLElement implements IHTMLScrip
16
17
  public onload: (event: Event) => void = null;
17
18
  public _evaluateScript = true;
18
19
 
19
- /**
20
- * Returns "true" if connected to DOM.
21
- *
22
- * @returns "true" if connected.
23
- */
24
- public get isConnected(): boolean {
25
- return this._isConnected;
26
- }
27
-
28
- /**
29
- * Sets the connected state.
30
- *
31
- * @param isConnected "true" if connected.
32
- */
33
- public set isConnected(isConnected) {
34
- if (this._isConnected !== isConnected) {
35
- this._isConnected = isConnected;
36
-
37
- for (const child of this.childNodes) {
38
- child.isConnected = isConnected;
39
- }
40
-
41
- // eslint-disable-next-line
42
- if (this.shadowRoot) {
43
- // eslint-disable-next-line
44
- this.shadowRoot.isConnected = isConnected;
45
- }
46
-
47
- if (isConnected && this._evaluateScript) {
48
- const src = this.getAttributeNS(null, 'src');
49
-
50
- if (src !== null) {
51
- ScriptUtility.loadExternalScript(this);
52
- } else {
53
- const textContent = this.textContent;
54
- if (textContent) {
55
- this.ownerDocument.defaultView.eval(textContent);
56
- }
57
- }
58
- }
59
-
60
- if (isConnected && this.connectedCallback) {
61
- this.connectedCallback();
62
- } else if (!isConnected && this.disconnectedCallback) {
63
- this.disconnectedCallback();
64
- }
65
- }
66
- }
67
-
68
20
  /**
69
21
  * Returns type.
70
22
  *
@@ -226,4 +178,27 @@ export default class HTMLScriptElement extends HTMLElement implements IHTMLScrip
226
178
  public cloneNode(deep = false): IHTMLScriptElement {
227
179
  return <IHTMLScriptElement>super.cloneNode(deep);
228
180
  }
181
+
182
+ /**
183
+ * @override
184
+ */
185
+ public _connectToNode(parentNode: INode = null): void {
186
+ const isConnected = this.isConnected;
187
+ const isParentConnected = parentNode ? parentNode.isConnected : false;
188
+
189
+ super._connectToNode(parentNode);
190
+
191
+ if (isConnected !== isParentConnected && this._evaluateScript) {
192
+ const src = this.getAttributeNS(null, 'src');
193
+
194
+ if (src !== null) {
195
+ ScriptUtility.loadExternalScript(this);
196
+ } else {
197
+ const textContent = this.textContent;
198
+ if (textContent) {
199
+ this.ownerDocument.defaultView.eval(textContent);
200
+ }
201
+ }
202
+ }
203
+ }
229
204
  }
@@ -15,7 +15,7 @@ export default class HTMLStyleElement extends HTMLElement implements IHTMLStyleE
15
15
  * @returns CSS style sheet.
16
16
  */
17
17
  public get sheet(): CSSStyleSheet {
18
- if (!this._isConnected) {
18
+ if (!this.isConnected) {
19
19
  return null;
20
20
  }
21
21
  const styleSheet = new CSSStyleSheet();
@@ -8,7 +8,7 @@ export default interface INode extends IEventTarget {
8
8
  readonly parentElement: IElement;
9
9
  readonly nodeType: number;
10
10
  readonly childNodes: INode[];
11
- isConnected: boolean;
11
+ readonly isConnected: boolean;
12
12
  readonly nodeValue: string;
13
13
  readonly nodeName: string;
14
14
  readonly previousSibling: INode;
@@ -9,7 +9,6 @@ import IDocument from '../document/IDocument';
9
9
  import IElement from '../element/IElement';
10
10
  import INodeList from './INodeList';
11
11
  import NodeListFactory from './NodeListFactory';
12
- import { IShadowRoot } from '../..';
13
12
 
14
13
  /**
15
14
  * Node.
@@ -27,9 +26,8 @@ export default class Node extends EventTarget implements INode {
27
26
  public readonly parentNode: INode = null;
28
27
  public readonly nodeType: number;
29
28
  public readonly childNodes: INodeList<INode> = NodeListFactory.create();
30
-
31
- // Protected properties
32
- protected _isConnected = false;
29
+ public readonly isConnected: boolean = false;
30
+ public _rootNode: INode = null;
33
31
 
34
32
  // Custom Properties (not part of HTML standard)
35
33
  protected _observers: MutationObserverListener[] = [];
@@ -42,42 +40,6 @@ export default class Node extends EventTarget implements INode {
42
40
  this.ownerDocument = (<typeof Node>this.constructor).ownerDocument;
43
41
  }
44
42
 
45
- /**
46
- * Returns "true" if connected to DOM.
47
- *
48
- * @returns "true" if connected.
49
- */
50
- public get isConnected(): boolean {
51
- return this._isConnected;
52
- }
53
-
54
- /**
55
- * Sets the connected state.
56
- *
57
- * @param isConnected "true" if connected.
58
- */
59
- public set isConnected(isConnected) {
60
- if (this._isConnected !== isConnected) {
61
- this._isConnected = isConnected;
62
-
63
- if (isConnected && this.connectedCallback) {
64
- this.connectedCallback();
65
- } else if (!isConnected && this.disconnectedCallback) {
66
- this.disconnectedCallback();
67
- }
68
-
69
- for (const child of this.childNodes) {
70
- child.isConnected = isConnected;
71
- }
72
-
73
- // eslint-disable-next-line
74
- if ((<any>this).shadowRoot) {
75
- // eslint-disable-next-line
76
- (<any>this).shadowRoot.isConnected = isConnected;
77
- }
78
- }
79
- }
80
-
81
43
  /**
82
44
  * Get text value of children.
83
45
  *
@@ -208,21 +170,15 @@ export default class Node extends EventTarget implements INode {
208
170
  * @returns Node.
209
171
  */
210
172
  public getRootNode(options?: { composed: boolean }): INode {
211
- // eslint-disable-next-line
212
- let parent: INode = this;
173
+ if (!this.isConnected) {
174
+ return this;
175
+ }
213
176
 
214
- while (!!parent) {
215
- if (!parent.parentNode) {
216
- if (!options?.composed || !(<IShadowRoot>parent).host) {
217
- return parent;
218
- }
219
- parent = (<IShadowRoot>parent).host;
220
- } else {
221
- parent = parent.parentNode;
222
- }
177
+ if (this._rootNode && !options?.composed) {
178
+ return this._rootNode;
223
179
  }
224
180
 
225
- return null;
181
+ return this.ownerDocument;
226
182
  }
227
183
 
228
184
  /**
@@ -234,8 +190,11 @@ export default class Node extends EventTarget implements INode {
234
190
  public cloneNode(deep = false): INode {
235
191
  const clone = new (<typeof Node>this.constructor)();
236
192
 
237
- for (const node of clone.childNodes.slice()) {
238
- node.parentNode.removeChild(node);
193
+ // Document has childNodes directly when it is created
194
+ if (clone.childNodes.length) {
195
+ for (const node of clone.childNodes.slice()) {
196
+ node.parentNode.removeChild(node);
197
+ }
239
198
  }
240
199
 
241
200
  if (deep) {
@@ -281,8 +240,7 @@ export default class Node extends EventTarget implements INode {
281
240
 
282
241
  this.childNodes.push(node);
283
242
 
284
- (<Node>node.parentNode) = this;
285
- node.isConnected = this.isConnected;
243
+ (<Node>node)._connectToNode(this);
286
244
 
287
245
  // MutationObserver
288
246
  if (this._observers.length > 0) {
@@ -318,8 +276,7 @@ export default class Node extends EventTarget implements INode {
318
276
 
319
277
  this.childNodes.splice(index, 1);
320
278
 
321
- (<Node>node.parentNode) = null;
322
- node.isConnected = false;
279
+ (<Node>node)._connectToNode(null);
323
280
 
324
281
  // MutationObserver
325
282
  if (this._observers.length > 0) {
@@ -384,8 +341,7 @@ export default class Node extends EventTarget implements INode {
384
341
 
385
342
  this.childNodes.splice(index, 0, newNode);
386
343
 
387
- (<Node>newNode.parentNode) = this;
388
- newNode.isConnected = this.isConnected;
344
+ (<Node>newNode)._connectToNode(this);
389
345
 
390
346
  // MutationObserver
391
347
  if (this._observers.length > 0) {
@@ -432,8 +388,16 @@ export default class Node extends EventTarget implements INode {
432
388
 
433
389
  const returnValue = super.dispatchEvent(event);
434
390
 
435
- if (event.bubbles && this.parentNode !== null && !event._propagationStopped) {
436
- return this.parentNode.dispatchEvent(event);
391
+ if (event.bubbles && !event._propagationStopped) {
392
+ if (this.parentNode) {
393
+ return this.parentNode.dispatchEvent(event);
394
+ }
395
+
396
+ // eslint-disable-next-line
397
+ if(event.composed && (<any>this).host) {
398
+ // eslint-disable-next-line
399
+ return (<any>this).host.dispatchEvent(event);
400
+ }
437
401
  }
438
402
 
439
403
  return returnValue;
@@ -480,4 +444,38 @@ export default class Node extends EventTarget implements INode {
480
444
  }
481
445
  }
482
446
  }
447
+
448
+ /**
449
+ * Connects this element to another element.
450
+ *
451
+ * @param parentNode Parent node.
452
+ */
453
+ public _connectToNode(parentNode: INode = null): void {
454
+ const isConnected = !!parentNode && parentNode.isConnected;
455
+
456
+ if (this.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
457
+ (<INode>this.parentNode) = parentNode;
458
+ (<Node>this)._rootNode = isConnected && parentNode ? (<Node>parentNode)._rootNode : null;
459
+ }
460
+
461
+ if (this.isConnected !== isConnected) {
462
+ (<boolean>this.isConnected) = isConnected;
463
+
464
+ if (isConnected && this.connectedCallback) {
465
+ this.connectedCallback();
466
+ } else if (!isConnected && this.disconnectedCallback) {
467
+ this.disconnectedCallback();
468
+ }
469
+
470
+ for (const child of this.childNodes) {
471
+ (<Node>child)._connectToNode(this);
472
+ }
473
+
474
+ // eslint-disable-next-line
475
+ if ((<any>this).shadowRoot) {
476
+ // eslint-disable-next-line
477
+ (<any>this).shadowRoot._connectToNode(this);
478
+ }
479
+ }
480
+ }
483
481
  }
@@ -4,6 +4,7 @@ import XMLSerializer from '../../xml-serializer/XMLSerializer';
4
4
  import IElement from '../element/IElement';
5
5
  import CSSStyleSheet from '../../css/CSSStyleSheet';
6
6
  import IShadowRoot from './IShadowRoot';
7
+ import IHTMLElement from '../../nodes/html-element/IHTMLElement';
7
8
 
8
9
  /**
9
10
  * ShadowRoot.
@@ -42,6 +43,19 @@ export default class ShadowRoot extends DocumentFragment implements IShadowRoot
42
43
  }
43
44
  }
44
45
 
46
+ /**
47
+ * Returns active element.
48
+ *
49
+ * @returns Active element.
50
+ */
51
+ public get activeElement(): IHTMLElement {
52
+ const activeElement: IHTMLElement = this.ownerDocument['_activeElement'];
53
+ if (activeElement && activeElement.getRootNode() === this) {
54
+ return activeElement;
55
+ }
56
+ return null;
57
+ }
58
+
45
59
  /**
46
60
  * Converts to string.
47
61
  *