diff --git a/packages/happy-dom/src/nodes/document/Document.ts b/packages/happy-dom/src/nodes/document/Document.ts index 1e1493a2d..7190461c4 100644 --- a/packages/happy-dom/src/nodes/document/Document.ts +++ b/packages/happy-dom/src/nodes/document/Document.ts @@ -345,6 +345,10 @@ export default class Document extends Node implements IDocument { * @returns Active element. */ public get activeElement(): IHTMLElement { + if (this._activeElement && !this._activeElement.isConnected) { + this._activeElement = null; + } + if (this._activeElement) { let rootNode: IShadowRoot | IDocument = ( this._activeElement.getRootNode() diff --git a/packages/happy-dom/src/nodes/node/Node.ts b/packages/happy-dom/src/nodes/node/Node.ts index 5abb8e419..ab40eec9b 100644 --- a/packages/happy-dom/src/nodes/node/Node.ts +++ b/packages/happy-dom/src/nodes/node/Node.ts @@ -552,6 +552,9 @@ export default class Node extends EventTarget implements INode { if (isConnected && this.connectedCallback) { this.connectedCallback(); } else if (!isConnected && this.disconnectedCallback) { + if (this.ownerDocument['_activeElement'] === this) { + this.ownerDocument['_activeElement'] = null; + } this.disconnectedCallback(); } diff --git a/packages/happy-dom/src/nodes/shadow-root/ShadowRoot.ts b/packages/happy-dom/src/nodes/shadow-root/ShadowRoot.ts index 59c937848..8fba913b0 100644 --- a/packages/happy-dom/src/nodes/shadow-root/ShadowRoot.ts +++ b/packages/happy-dom/src/nodes/shadow-root/ShadowRoot.ts @@ -54,7 +54,7 @@ export default class ShadowRoot extends DocumentFragment implements IShadowRoot */ public get activeElement(): IHTMLElement { const activeElement: IHTMLElement = this.ownerDocument['_activeElement']; - if (activeElement && activeElement.getRootNode() === this) { + if (activeElement && activeElement.isConnected && activeElement.getRootNode() === this) { return activeElement; } return null; diff --git a/packages/happy-dom/test/nodes/document/Document.test.ts b/packages/happy-dom/test/nodes/document/Document.test.ts index 86f3b1b6c..f93374aed 100644 --- a/packages/happy-dom/test/nodes/document/Document.test.ts +++ b/packages/happy-dom/test/nodes/document/Document.test.ts @@ -320,6 +320,22 @@ describe('Document', () => { expect(document.activeElement === document.body).toBe(true); }); + it('Unsets the active element when it gets disconnected.', () => { + const div = document.createElement('div'); + + document.appendChild(div); + + expect(document.activeElement === document.body).toBe(true); + + div.focus(); + + expect(document.activeElement === div).toBe(true); + + div.remove(); + + expect(document.activeElement === document.body).toBe(true); + }); + it('Returns the first custom element that has document as root node when the focused element is nestled in multiple shadow roots.', () => { class CustomElementA extends (window).HTMLElement { constructor() { diff --git a/packages/happy-dom/test/nodes/shadow-root/ShadowRoot.test.ts b/packages/happy-dom/test/nodes/shadow-root/ShadowRoot.test.ts index 7b9045c3a..3578e1135 100644 --- a/packages/happy-dom/test/nodes/shadow-root/ShadowRoot.test.ts +++ b/packages/happy-dom/test/nodes/shadow-root/ShadowRoot.test.ts @@ -68,6 +68,26 @@ describe('ShadowRoot', () => { expect(shadowRoot.activeElement === null).toBe(true); }); + + it('Unsets the active element when it gets disconnected.', () => { + const customElement = document.createElement('custom-element'); + const shadowRoot = customElement.shadowRoot; + const div = document.createElement('div'); + + document.body.appendChild(customElement); + + shadowRoot.appendChild(div); + + expect(shadowRoot.activeElement === null).toBe(true); + + div.focus(); + + expect(shadowRoot.activeElement === div).toBe(true); + + customElement.remove(); + + expect(shadowRoot.activeElement === null).toBe(true); + }); }); describe('toString()', () => {