Open yao-motif opened 2 years ago
more context
Angular's drag and drop libary triggers
.preventDefault()
on each moment of drag (presumably to avoid unwanted side effects). And apparently it's not possible to call.preventDefault
on an element that has a passive event listener.That results in a stream of the following errors:
drag-drop.mjs:575 Unable to preventDefault inside passive event listener invocation.
and on drag and drop becoming sticky and janky in some browsers.
interestingly I can't replicate this by registering a passive click listener and then preventing default in a different click listener
[MDN says](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners:~:text=By%20setting%20the%20passive%20option%20to%20true%2C%20an%20event%20listener%20declares%20that%20it%20will%20not%20cancel%20the%20default%20action%2C%20so%20the%20browser%20can%20start%20the%20default%20action%20immediately%2C%20without%20waiting%20for%20the%20listener%20to%20finish.%20If%20the%20listener%20does%20then%20call%20Event.preventDefault()%2C%20this%20will%20have%20no%20effect.): By setting the passive option to true, an event listener declares that it will not cancel the default action, so the browser can start the default action immediately, without waiting for the listener to finish. If the listener does then call Event.preventDefault(), this will have no effect.
yep, I can't recreate this (doesn't mean it isn't happening, i could be confused 🤣)
is someone able to create a minimal reproduction? i've not used angular for more than a decade 🙈
Hi Paul,
I've created a StackBlitz example to mock up how Angular handles drag events and allows adding a passive event listener.
However, I haven't been able to reproduce the exact error in this isolated setup. Here’s the specific component code I used:
(Note that errors only show in Google Chrome not in safari - however the behaviour has been reported to lead to really troublesome drag and drop "stickiness" on android)
export class DraggableComponent implements AfterViewInit {
// Reference to the DOM element with #draggableElement in the template
@ViewChild('draggableElement') draggableElement!: ElementRef<HTMLElement>;
// Function reference for the passive event listener
private passiveEventListener?: (event: MouseEvent) => void;
// Lifecycle hook called after the component's view has been initialized
ngAfterViewInit(): void {
// Bind the passive event listener to the component's context
this.passiveEventListener = this.onPassiveDragMove.bind(this);
if (this.draggableElement) {
console.log('binding passive listener to draggable element');
// Add a passive mousemove listener to the draggable element
this.draggableElement.nativeElement.addEventListener(
'drag',
this.passiveEventListener,
{ passive: true }
);
}
}
onAngularDragMove(event: CdkDragMove): void {
// Handler for the CDK drag event, logs drag movement
console.log('Drag moved (CDK event)', event);
// event.event.preventDefault();
}
private onPassiveDragMove(event: MouseEvent): void {
// Handler for the passive mousemove event, logs mouse movement
console.log('Mouse moved (passive event)', event);
}
}
Analysis of rrweb's Event Handling:
Upon examining the code in rrweb, I found that rrweb does not directly add a drag event listener. Instead, it adds a passive mousemove event listener and calls the onDrag method internally. This approach, using { passive: true }, prevents the listener from calling preventDefault(). This passive listener could interfere with Angular CDK's drag-and-drop system, which suppresses native drag events in favor of its custom cdkDrag events. If rrweb’s passive listeners expect native drag behavior, they might conflict with CDK's custom event handling.
Summary:
Environment: Angular CDK Drag and Drop, Chrome, PostHog's rrweb. Issue: Errors related to preventDefault in passive event listeners. Reproduction Attempts: Could not reproduce the issue in an isolated StackBlitz example. Suspected Cause: The issue seems to stem from rrweb adding passive mousemove listeners, which interfere with Angular’s CDK drag handling that attempts to call preventDefault.
Suggested Fix: The easiest way to fix this could be to have an option that removes PH's drag mirroring.
Thank you
Hey @peternixey thanks for taking the time!
In my testing I could not make passive event listener A complain when active event listener B prevented default. the error was only thrown when a listener cancelled its own passive event.
In your test here is CDK cancelling its own event? I'm super unfamiliar with Angular and I'm wondering if this is really a bug in the Angular CDK drag and drop or in rrweb
(PostHog is stuck in the middle regardless 🤣 - happy to work to help resolve it but want to be sure we're poking the right thing with a stick)
Really appreciate you looking at this Paul - I feel it's nerd-sniped both of us 😂.
I asked ChatGPT to help identify what's going on and it came up with the following:
In the latest Angular CDK source code, the preventDefault()
function is used in the DragDropRegistry
class to manage drag-and-drop operations. Here's how:
Persistent Touchmove Listener: preventDefault()
is invoked in a persistent touchmove
event listener to prevent scrolling during drag operations (drag-drop-registry.ts
line 291). https://github.com/angular/components/blob/main/src/cdk/drag-drop/drag-drop-registry.ts
Dragging Sequence: preventDefault()
is used within a selectstart
event handler to stop text selection during dragging (drag-drop-registry.ts
line 384). https://github.com/angular/components/blob/main/src/cdk/drag-drop/drag-drop-registry.ts
These prevent default actions ensure a smooth drag-and-drop experience by preventing native touch and selection behaviors.
Source: drag-drop-registry.ts
https://chatgpt.com/share/6acfdd01-4639-4950-bf00-f06d117ca29c
sure, but the question here isn't whether CDK is calling preventDefault
but whether CDK calling preventDefault
is interacting with rrweb's passive listeners
and my testing indicated that with standard browser event listeners it isn't possible to interact (obvs i could be wrong)
if that's true then the "bug" is in CDK because it's doing something non-standard.
If/After posthog.init is called, whenever I drag-drop using the @angular/cdk/drag-drop library, a bunch of errors pop up
drag-drop.mjs:461 Unable to preventDefault inside passive event listener invocation.
While this doesn't currently seem to affect functionality, it really pollutes the console
Environment