diff --git a/core/browser_events.ts b/core/browser_events.ts index 8176fe10ff3..66e8fa23d52 100644 --- a/core/browser_events.ts +++ b/core/browser_events.ts @@ -33,6 +33,18 @@ const LINE_MODE_MULTIPLIER = 40; */ const PAGE_MODE_MULTIPLIER = 125; +/** + * Options to control the behavior of bound event listeners. Based on + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options + * and should be kept consistent with web standards as they evolve. + */ +export type BindOptions = { + capture?: boolean; + once?: boolean; + passive?: boolean; + signal?: AbortSignal; +}; + /** * Bind an event handler that can be ignored if it is not part of the active * touch stream. @@ -46,6 +58,9 @@ const PAGE_MODE_MULTIPLIER = 125; * @param opt_noCaptureIdentifier True if triggering on this event should not * block execution of other event handlers on this touch or other * simultaneous touches. False by default. + * @param options An object with options controlling the behavior of the event + * listener. Passed through directly as the third argument to + * `addEventListener`. * @returns Opaque data that can be passed to unbindEvent_. */ export function conditionalBind( @@ -54,6 +69,7 @@ export function conditionalBind( thisObject: object | null, func: Function, opt_noCaptureIdentifier?: boolean, + options?: BindOptions, ): Data { /** * @@ -75,11 +91,11 @@ export function conditionalBind( if (name in Touch.TOUCH_MAP) { for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) { const type = Touch.TOUCH_MAP[name][i]; - node.addEventListener(type, wrapFunc, false); + node.addEventListener(type, wrapFunc, {capture: false, ...options}); bindData.push([node, type, wrapFunc]); } } else { - node.addEventListener(name, wrapFunc, false); + node.addEventListener(name, wrapFunc, {capture: false, ...options}); bindData.push([node, name, wrapFunc]); } return bindData; @@ -95,6 +111,9 @@ export function conditionalBind( * @param name Event name to listen to (e.g. 'mousedown'). * @param thisObject The value of 'this' in the function. * @param func Function to call when event is triggered. + * @param options An object with options controlling the behavior of the event + * listener. Passed through directly as the third argument to + * `addEventListener`. * @returns Opaque data that can be passed to unbindEvent_. */ export function bind( @@ -102,6 +121,7 @@ export function bind( name: string, thisObject: object | null, func: Function, + options?: BindOptions, ): Data { /** * @@ -119,11 +139,11 @@ export function bind( if (name in Touch.TOUCH_MAP) { for (let i = 0; i < Touch.TOUCH_MAP[name].length; i++) { const type = Touch.TOUCH_MAP[name][i]; - node.addEventListener(type, wrapFunc, false); + node.addEventListener(type, wrapFunc, {capture: false, ...options}); bindData.push([node, type, wrapFunc]); } } else { - node.addEventListener(name, wrapFunc, false); + node.addEventListener(name, wrapFunc, {capture: false, ...options}); bindData.push([node, name, wrapFunc]); } return bindData; diff --git a/core/comments/comment_editor.ts b/core/comments/comment_editor.ts index 2005a9991ae..92c92fa5464 100644 --- a/core/comments/comment_editor.ts +++ b/core/comments/comment_editor.ts @@ -95,9 +95,16 @@ export class CommentEditor implements IFocusableNode { ); // Don't zoom with mousewheel; let it scroll instead. - browserEvents.conditionalBind(this.textArea, 'wheel', this, (e: Event) => { - e.stopPropagation(); - }); + browserEvents.conditionalBind( + this.textArea, + 'wheel', + this, + (e: Event) => { + e.stopPropagation(); + }, + false, + {passive: true}, + ); // Register listener for keydown events that would finish editing. browserEvents.conditionalBind( diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 3f2ed32d5ba..ee8f72fb6f8 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -357,6 +357,8 @@ export abstract class Flyout 'wheel', this, this.wheel_, + false, + {passive: false}, ), ); diff --git a/core/inject.ts b/core/inject.ts index 4217c515119..d6fdd34f508 100644 --- a/core/inject.ts +++ b/core/inject.ts @@ -388,6 +388,7 @@ function loadSounds(pathToMedia: string, workspace: WorkspaceSvg) { null, unbindSounds, true, + {passive: true}, ), ); } diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index af395b077e5..de158c6d426 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -809,12 +809,16 @@ export class WorkspaceSvg // which otherwise prevents zoom/scroll events from being observed in // Safari. Once that bug is fixed it should be removed. this.dummyWheelListener = () => {}; - document.body.addEventListener('wheel', this.dummyWheelListener); + document.body.addEventListener('wheel', this.dummyWheelListener, { + passive: true, + }); browserEvents.conditionalBind( this.svgGroup_, 'wheel', this, this.onMouseWheel, + false, + {passive: false}, ); }