Skip to content

Commit

Permalink
fix: [#1577] Event.target should be the target element after an event…
Browse files Browse the repository at this point in the history
… has been dispatched (#1676)
  • Loading branch information
capricorn86 authored Jan 8, 2025
1 parent f79188a commit 8bf1f26
Show file tree
Hide file tree
Showing 13 changed files with 43 additions and 27 deletions.
1 change: 1 addition & 0 deletions packages/happy-dom/src/PropertySymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,4 @@ export const xmlProcessingInstruction = Symbol('xmlProcessingInstruction');
export const root = Symbol('root');
export const filterNode = Symbol('filterNode');
export const customElementReactionStack = Symbol('customElementReactionStack');
export const dispatching = Symbol('dispatching');
1 change: 1 addition & 0 deletions packages/happy-dom/src/event/Event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default class Event {
public [PropertySymbol.timeStamp] = performance.now();
public [PropertySymbol.type]: string;

public [PropertySymbol.dispatching] = false;
public [PropertySymbol.immediatePropagationStopped] = false;
public [PropertySymbol.propagationStopped] = false;
public [PropertySymbol.target]: EventTarget = null;
Expand Down
12 changes: 8 additions & 4 deletions packages/happy-dom/src/event/EventTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,18 @@ export default class EventTarget {
* @returns The return value is false if event is cancelable and at least one of the event handlers which handled this event called Event.preventDefault().
*/
public dispatchEvent(event: Event): boolean {
if (!event[PropertySymbol.target]) {
// The "load" event is a special case. It should not bubble up to the window from the document.
if (
!event[PropertySymbol.dispatching] &&
(event[PropertySymbol.type] !== 'load' || !event[PropertySymbol.target])
) {
event[PropertySymbol.dispatching] = true;
event[PropertySymbol.target] = this[PropertySymbol.proxy] || this;

this.#goThroughDispatchEventPhases(event);

event[PropertySymbol.dispatching] = false;

return !(event[PropertySymbol.cancelable] && event[PropertySymbol.defaultPrevented]);
}

Expand Down Expand Up @@ -172,7 +179,6 @@ export default class EventTarget {
event[PropertySymbol.immediatePropagationStopped]
) {
event[PropertySymbol.eventPhase] = EventPhaseEnum.none;
event[PropertySymbol.target] = null;
event[PropertySymbol.currentTarget] = null;
return;
}
Expand Down Expand Up @@ -201,7 +207,6 @@ export default class EventTarget {
event[PropertySymbol.immediatePropagationStopped]
) {
event[PropertySymbol.eventPhase] = EventPhaseEnum.none;
event[PropertySymbol.target] = null;
event[PropertySymbol.currentTarget] = null;
return;
}
Expand All @@ -210,7 +215,6 @@ export default class EventTarget {

// None phase (done)
event[PropertySymbol.eventPhase] = EventPhaseEnum.none;
event[PropertySymbol.target] = null;
event[PropertySymbol.currentTarget] = null;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/window/BrowserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,15 +845,15 @@ export default class BrowserWindow extends EventTarget implements INodeJSGlobal
// Not sure why target is set to document here, but this is how it works in the browser
const loadEvent = new Event('load');

loadEvent[PropertySymbol.currentTarget] = this.document;
loadEvent[PropertySymbol.currentTarget] = this;
loadEvent[PropertySymbol.target] = this.document;
loadEvent[PropertySymbol.eventPhase] = EventPhaseEnum.atTarget;

this.dispatchEvent(loadEvent);

loadEvent[PropertySymbol.target] = null;
loadEvent[PropertySymbol.currentTarget] = null;
loadEvent[PropertySymbol.eventPhase] = EventPhaseEnum.none;
loadEvent[PropertySymbol.dispatching] = false;
});

this[PropertySymbol.bindMethods]();
Expand Down
2 changes: 1 addition & 1 deletion packages/happy-dom/test/event/Event.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Event', () => {
});
span.dispatchEvent(event);

expect(event.target).toBe(null);
expect(event.target).toBe(span);
expect(target).toBe(span);
});
});
Expand Down
10 changes: 10 additions & 0 deletions packages/happy-dom/test/event/EventTarget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Window from '../../src/window/Window.js';
import EventTarget from '../../src/event/EventTarget.js';
import Event from '../../src/event/Event.js';
import CustomEvent from '../../src/event/events/CustomEvent.js';
import * as PropertySymbol from '../../src/PropertySymbol.js';
import { beforeEach, describe, it, expect } from 'vitest';

const EVENT_TYPE = 'click';
Expand Down Expand Up @@ -160,6 +161,10 @@ describe('EventTarget', () => {
expect(recievedEvent).toBe(dispatchedEvent);
expect(recievedTarget).toBe(eventTarget);
expect(recievedCurrentTarget).toBe(eventTarget);
expect(dispatchedEvent.target).toBe(eventTarget);
expect(dispatchedEvent.currentTarget).toBe(null);
expect(dispatchedEvent.defaultPrevented).toBe(false);
expect(dispatchedEvent[PropertySymbol.dispatching]).toBe(false);
});

it('Triggers all listeners, even though listeners are removed while dispatching.', () => {
Expand Down Expand Up @@ -196,6 +201,11 @@ describe('EventTarget', () => {
expect(recievedCurrentTarget1).toBe(eventTarget);
expect(recievedTarget2).toBe(eventTarget);
expect(recievedCurrentTarget2).toBe(eventTarget);

expect(dispatchedEvent.target).toBe(eventTarget);
expect(dispatchedEvent.currentTarget).toBe(null);
expect(dispatchedEvent.defaultPrevented).toBe(false);
expect(dispatchedEvent[PropertySymbol.dispatching]).toBe(false);
});
});

Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/test/nodes/document/Document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1397,7 +1397,7 @@ describe('Document', () => {
expect(document.readyState).toBe(DocumentReadyStateEnum.interactive);

setTimeout(() => {
expect((<Event>event).target).toBe(null);
expect((<Event>event).target).toBe(document);
expect(target).toBe(document);
expect(currentTarget).toBe(document);
expect(document.readyState).toBe(DocumentReadyStateEnum.complete);
Expand Down Expand Up @@ -1456,7 +1456,7 @@ describe('Document', () => {
expect(resourceFetchCSSURL).toBe(cssURL);
expect(resourceFetchJSWindow).toBe(window);
expect(resourceFetchJSURL).toBe(jsURL);
expect((<Event>event).target).toBe(null);
expect((<Event>event).target).toBe(document);
expect(target).toBe(document);
expect(currentTarget).toBe(document);
expect(document.readyState).toBe(DocumentReadyStateEnum.complete);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ describe('HTMLElement', () => {
expect((<PointerEvent>(<unknown>event)).composed).toBe(true);
expect((<PointerEvent>(<unknown>event)).width).toBe(1);
expect((<PointerEvent>(<unknown>event)).height).toBe(1);
expect((<PointerEvent>(<unknown>event)).target).toBe(null);
expect((<PointerEvent>(<unknown>event)).target).toBe(element);
expect((<PointerEvent>(<unknown>event)).currentTarget).toBe(null);
expect(target).toBe(element);
expect(currentTarget).toBe(element);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ describe('HTMLElementUtility', () => {
expect((<FocusEvent>(<unknown>blurEvent)).type).toBe('blur');
expect((<FocusEvent>(<unknown>blurEvent)).bubbles).toBe(false);
expect((<FocusEvent>(<unknown>blurEvent)).composed).toBe(true);
expect((<FocusEvent>(<unknown>blurEvent)).target).toBe(null);
expect((<FocusEvent>(<unknown>blurEvent)).target).toBe(element);
expect(blurTarget).toBe(element);
expect(blurCurrentTarget).toBe(element);

expect((<FocusEvent>(<unknown>focusOutEvent)).type).toBe('focusout');
expect((<FocusEvent>(<unknown>focusOutEvent)).bubbles).toBe(true);
expect((<FocusEvent>(<unknown>focusOutEvent)).composed).toBe(true);
expect((<FocusEvent>(<unknown>focusOutEvent)).target).toBe(null);
expect((<FocusEvent>(<unknown>focusOutEvent)).target).toBe(element);
expect(focusOutTarget).toBe(element);
expect(focusOutCurrentTarget).toBe(element);

Expand Down Expand Up @@ -133,14 +133,14 @@ describe('HTMLElementUtility', () => {
expect((<FocusEvent>(<unknown>focusEvent)).type).toBe('focus');
expect((<FocusEvent>(<unknown>focusEvent)).bubbles).toBe(false);
expect((<FocusEvent>(<unknown>focusEvent)).composed).toBe(true);
expect((<FocusEvent>(<unknown>focusEvent)).target).toBe(null);
expect((<FocusEvent>(<unknown>focusEvent)).target).toBe(element);
expect(focusTarget).toBe(element);
expect(focusCurrentTarget).toBe(element);

expect((<FocusEvent>(<unknown>focusInEvent)).type).toBe('focusin');
expect((<FocusEvent>(<unknown>focusInEvent)).bubbles).toBe(true);
expect((<FocusEvent>(<unknown>focusInEvent)).composed).toBe(true);
expect((<FocusEvent>(<unknown>focusInEvent)).target).toBe(null);
expect((<FocusEvent>(<unknown>focusInEvent)).target).toBe(element);
expect(focusInTarget).toBe(element);
expect(focusInCurrentTarget).toBe(element);

Expand Down Expand Up @@ -212,7 +212,7 @@ describe('HTMLElementUtility', () => {
expect((<FocusEvent>(<unknown>event)).type).toBe('blur');
expect((<FocusEvent>(<unknown>event)).bubbles).toBe(false);
expect((<FocusEvent>(<unknown>event)).composed).toBe(true);
expect((<FocusEvent>(<unknown>event)).target).toBe(null);
expect((<FocusEvent>(<unknown>event)).target).toBe(previousElement);
expect(target).toBe(previousElement);
expect(currentTarget).toBe(previousElement);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe('HTMLLinkElement', () => {
expect(loadedURL).toBe('https://localhost:8080/test/path/file.css');
expect(element.sheet.cssRules.length).toBe(1);
expect(element.sheet.cssRules[0].cssText).toBe('div { background: red; }');
expect((<Event>(<unknown>loadEvent)).target).toBe(null);
expect((<Event>(<unknown>loadEvent)).target).toBe(element);
expect(loadEventTarget).toBe(element);
expect(loadEventCurrentTarget).toBe(element);
});
Expand Down Expand Up @@ -198,7 +198,7 @@ describe('HTMLLinkElement', () => {
expect(loadedURL).toBe('https://localhost:8080/test/path/file.css');
expect(element.sheet.cssRules.length).toBe(1);
expect(element.sheet.cssRules[0].cssText).toBe('div { background: red; }');
expect((<Event>(<unknown>loadEvent)).target).toBe(null);
expect((<Event>(<unknown>loadEvent)).target).toBe(element);
expect(loadEventTarget).toBe(element);
expect(loadEventCurrentTarget).toBe(element);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe('HTMLScriptElement', () => {

await window.happyDOM?.waitUntilComplete();

expect((<Event>(<unknown>loadEvent)).target).toBe(null);
expect((<Event>(<unknown>loadEvent)).target).toBe(script);
expect(loadEventTarget).toBe(script);
expect(loadEventCurrentTarget).toBe(script);
expect(fetchedURL).toBe('https://localhost:8080/path/to/script.js');
Expand Down Expand Up @@ -269,7 +269,7 @@ describe('HTMLScriptElement', () => {

window.document.body.appendChild(script);

expect((<Event>(<unknown>loadEvent)).target).toBe(null);
expect((<Event>(<unknown>loadEvent)).target).toBe(script);
expect(loadEventTarget).toBe(script);
expect(loadEventCurrentTarget).toBe(script);
expect(fetchedWindow).toBe(window);
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/test/nodes/node/Node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ describe('Node', () => {
expect(child.dispatchEvent(event)).toBe(true);

expect(childEvent).toBe(event);
expect((<Event>(<unknown>childEvent)).target).toBe(null);
expect((<Event>(<unknown>childEvent)).target).toBe(child);
expect((<Event>(<unknown>childEvent)).currentTarget).toBe(null);
expect(childEventTarget).toBe(child);
expect(childEventCurrentTarget).toBe(child);
Expand Down Expand Up @@ -922,7 +922,7 @@ describe('Node', () => {

expect(childEvent).toBe(event);
expect(parentEvent).toBe(event);
expect((<Event>(<unknown>parentEvent)).target).toBe(null);
expect((<Event>(<unknown>parentEvent)).target).toBe(child);
expect((<Event>(<unknown>parentEvent)).currentTarget).toBe(null);
expect(childEventTarget).toBe(child);
expect(childEventCurrentTarget).toBe(child);
Expand Down
12 changes: 6 additions & 6 deletions packages/happy-dom/test/window/BrowserWindow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1556,11 +1556,11 @@ describe('BrowserWindow', () => {
});

setTimeout(() => {
expect((<Event>event).target).toBe(null);
expect((<Event>event).target).toBe(document);
expect((<Event>event).currentTarget).toBe(null);
expect((<Event>event).eventPhase).toBe(EventPhaseEnum.none);
expect(target).toBe(document);
expect(currentTarget).toBe(document);
expect(currentTarget).toBe(window);
resolve(null);
}, 20);
});
Expand Down Expand Up @@ -1614,11 +1614,11 @@ describe('BrowserWindow', () => {
expect(resourceFetchCSSURL).toBe(cssURL);
expect(resourceFetchJSWindow === window).toBe(true);
expect(resourceFetchJSURL).toBe(jsURL);
expect((<Event>loadEvent).target).toBe(null);
expect((<Event>loadEvent).target).toBe(document);
expect((<Event>loadEvent).currentTarget).toBe(null);
expect((<Event>loadEvent).eventPhase).toBe(EventPhaseEnum.none);
expect(loadEventTarget).toBe(document);
expect(loadEventCurrentTarget).toBe(document);
expect(loadEventCurrentTarget).toBe(window);
expect(document.styleSheets.length).toBe(1);
expect(document.styleSheets[0].cssRules[0].cssText).toBe(cssResponse);

Expand Down Expand Up @@ -1648,9 +1648,9 @@ describe('BrowserWindow', () => {

setTimeout(() => {
expect(errorEvents.length).toBe(2);
expect(errorEvents[0].target).toBe(null);
expect(errorEvents[0].target).toBe(window);
expect((<Error>errorEvents[0].error).message).toBe('Script error');
expect(errorEvents[1].target).toBe(null);
expect(errorEvents[1].target).toBe(window);
expect((<Error>errorEvents[1].error).message).toBe('Timeout error');

resolve(null);
Expand Down

0 comments on commit 8bf1f26

Please sign in to comment.