@@ -10,26 +10,33 @@ import {
1010 ChangeDetectionStrategy ,
1111 Component ,
1212 Injectable ,
13+ ListenerOptions ,
1314 NgZone ,
1415 OnDestroy ,
16+ RendererFactory2 ,
1517 ViewEncapsulation ,
1618 WritableSignal ,
1719 inject ,
1820 signal ,
1921} from '@angular/core' ;
2022import { DOCUMENT } from '@angular/common' ;
21- import { normalizePassiveListenerOptions } from '@angular/cdk/platform' ;
23+ import { _bindEventWithOptions } from '@angular/cdk/platform' ;
2224import { _CdkPrivateStyleLoader } from '@angular/cdk/private' ;
2325import { Observable , Observer , Subject , merge } from 'rxjs' ;
2426import type { DropListRef } from './drop-list-ref' ;
2527import type { DragRef } from './drag-ref' ;
2628import type { CdkDrag } from './directives/drag' ;
2729
30+ /** Event options that can be used to bind a capturing event. */
31+ const capturingEventOptions = {
32+ capture : true ,
33+ } ;
34+
2835/** Event options that can be used to bind an active, capturing event. */
29- const activeCapturingEventOptions = normalizePassiveListenerOptions ( {
36+ const activeCapturingEventOptions = {
3037 passive : false ,
3138 capture : true ,
32- } ) ;
39+ } ;
3340
3441/**
3542 * Component used to load the drag&drop reset styles.
@@ -55,6 +62,8 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
5562 private _ngZone = inject ( NgZone ) ;
5663 private _document = inject ( DOCUMENT ) ;
5764 private _styleLoader = inject ( _CdkPrivateStyleLoader ) ;
65+ private _renderer = inject ( RendererFactory2 ) . createRenderer ( null , null ) ;
66+ private _cleanupDocumentTouchmove : ( ( ) => void ) | undefined ;
5867
5968 /** Registered drop container instances. */
6069 private _dropInstances = new Set < DropListRef > ( ) ;
@@ -66,13 +75,7 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
6675 private _activeDragInstances : WritableSignal < DragRef [ ] > = signal ( [ ] ) ;
6776
6877 /** Keeps track of the event listeners that we've bound to the `document`. */
69- private _globalListeners = new Map <
70- string ,
71- {
72- handler : ( event : Event ) => void ;
73- options ?: AddEventListenerOptions | boolean ;
74- }
75- > ( ) ;
78+ private _globalListeners : ( ( ) => void ) [ ] | undefined ;
7679
7780 /**
7881 * Predicate function to check if an item is being dragged. Moved out into a property,
@@ -127,7 +130,10 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
127130 this . _ngZone . runOutsideAngular ( ( ) => {
128131 // The event handler has to be explicitly active,
129132 // because newer browsers make it passive by default.
130- this . _document . addEventListener (
133+ this . _cleanupDocumentTouchmove ?.( ) ;
134+ this . _cleanupDocumentTouchmove = _bindEventWithOptions (
135+ this . _renderer ,
136+ this . _document ,
131137 'touchmove' ,
132138 this . _persistentTouchmoveListener ,
133139 activeCapturingEventOptions ,
@@ -147,11 +153,7 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
147153 this . stopDragging ( drag ) ;
148154
149155 if ( this . _dragInstances . size === 0 ) {
150- this . _document . removeEventListener (
151- 'touchmove' ,
152- this . _persistentTouchmoveListener ,
153- activeCapturingEventOptions ,
154- ) ;
156+ this . _cleanupDocumentTouchmove ?.( ) ;
155157 }
156158 }
157159
@@ -174,47 +176,43 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
174176 // passive ones for `mousemove` and `touchmove`. The events need to be active, because we
175177 // use `preventDefault` to prevent the page from scrolling while the user is dragging.
176178 const isTouchEvent = event . type . startsWith ( 'touch' ) ;
177- const endEventHandler = {
178- handler : ( e : Event ) => this . pointerUp . next ( e as TouchEvent | MouseEvent ) ,
179- options : true ,
180- } ;
179+ const endEventHandler = ( e : Event ) => this . pointerUp . next ( e as TouchEvent | MouseEvent ) ;
181180
182- if ( isTouchEvent ) {
183- this . _globalListeners . set ( 'touchend' , endEventHandler ) ;
184- this . _globalListeners . set ( 'touchcancel' , endEventHandler ) ;
185- } else {
186- this . _globalListeners . set ( 'mouseup' , endEventHandler ) ;
187- }
181+ const toBind : [ name : string , handler : ( event : Event ) => void , options : ListenerOptions ] [ ] = [
182+ // Use capturing so that we pick up scroll changes in any scrollable nodes that aren't
183+ // the document. See https://github.com/angular/components/issues/17144.
184+ [ 'scroll' , ( e : Event ) => this . scroll . next ( e ) , capturingEventOptions ] ,
188185
189- this . _globalListeners
190- . set ( 'scroll' , {
191- handler : ( e : Event ) => this . scroll . next ( e ) ,
192- // Use capturing so that we pick up scroll changes in any scrollable nodes that aren't
193- // the document. See https://github.com/angular/components/issues/17144.
194- options : true ,
195- } )
196186 // Preventing the default action on `mousemove` isn't enough to disable text selection
197187 // on Safari so we need to prevent the selection event as well. Alternatively this can
198188 // be done by setting `user-select: none` on the `body`, however it has causes a style
199189 // recalculation which can be expensive on pages with a lot of elements.
200- . set ( 'selectstart' , {
201- handler : this . _preventDefaultWhileDragging ,
202- options : activeCapturingEventOptions ,
203- } ) ;
190+ [ 'selectstart' , this . _preventDefaultWhileDragging , activeCapturingEventOptions ] ,
191+ ] ;
192+
193+ if ( isTouchEvent ) {
194+ toBind . push (
195+ [ 'touchend' , endEventHandler , capturingEventOptions ] ,
196+ [ 'touchcancel' , endEventHandler , capturingEventOptions ] ,
197+ ) ;
198+ } else {
199+ toBind . push ( [ 'mouseup' , endEventHandler , capturingEventOptions ] ) ;
200+ }
204201
205202 // We don't have to bind a move event for touch drag sequences, because
206203 // we already have a persistent global one bound from `registerDragItem`.
207204 if ( ! isTouchEvent ) {
208- this . _globalListeners . set ( 'mousemove' , {
209- handler : ( e : Event ) => this . pointerMove . next ( e as MouseEvent ) ,
210- options : activeCapturingEventOptions ,
211- } ) ;
205+ toBind . push ( [
206+ 'mousemove' ,
207+ ( e : Event ) => this . pointerMove . next ( e as MouseEvent ) ,
208+ activeCapturingEventOptions ,
209+ ] ) ;
212210 }
213211
214212 this . _ngZone . runOutsideAngular ( ( ) => {
215- this . _globalListeners . forEach ( ( config , name ) => {
216- this . _document . addEventListener ( name , config . handler , config . options ) ;
217- } ) ;
213+ this . _globalListeners = toBind . map ( ( [ name , handler , options ] ) =>
214+ _bindEventWithOptions ( this . _renderer , this . _document , name , handler , options ) ,
215+ ) ;
218216 } ) ;
219217 }
220218 }
@@ -257,17 +255,20 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
257255 streams . push (
258256 new Observable ( ( observer : Observer < Event > ) => {
259257 return this . _ngZone . runOutsideAngular ( ( ) => {
260- const eventOptions = true ;
261- const callback = ( event : Event ) => {
262- if ( this . _activeDragInstances ( ) . length ) {
263- observer . next ( event ) ;
264- }
265- } ;
266-
267- ( shadowRoot as ShadowRoot ) . addEventListener ( 'scroll' , callback , eventOptions ) ;
258+ const cleanup = _bindEventWithOptions (
259+ this . _renderer ,
260+ shadowRoot as ShadowRoot ,
261+ 'scroll' ,
262+ ( event : Event ) => {
263+ if ( this . _activeDragInstances ( ) . length ) {
264+ observer . next ( event ) ;
265+ }
266+ } ,
267+ capturingEventOptions ,
268+ ) ;
268269
269270 return ( ) => {
270- ( shadowRoot as ShadowRoot ) . removeEventListener ( 'scroll' , callback , eventOptions ) ;
271+ cleanup ( ) ;
271272 } ;
272273 } ) ;
273274 } ) ,
@@ -338,10 +339,7 @@ export class DragDropRegistry<_ = unknown, __ = unknown> implements OnDestroy {
338339
339340 /** Clears out the global event listeners from the `document`. */
340341 private _clearGlobalListeners ( ) {
341- this . _globalListeners . forEach ( ( config , name ) => {
342- this . _document . removeEventListener ( name , config . handler , config . options ) ;
343- } ) ;
344-
345- this . _globalListeners . clear ( ) ;
342+ this . _globalListeners ?. forEach ( cleanup => cleanup ( ) ) ;
343+ this . _globalListeners = undefined ;
346344 }
347345}
0 commit comments