@@ -4,6 +4,7 @@ import {Subject} from 'rxjs/Subject';
4
4
import { Observable } from 'rxjs/Observable' ;
5
5
import { Subscription } from 'rxjs/Subscription' ;
6
6
import 'rxjs/add/observable/fromEvent' ;
7
+ import 'rxjs/add/observable/merge' ;
7
8
import 'rxjs/add/operator/auditTime' ;
8
9
9
10
@@ -19,25 +20,26 @@ export class ScrollDispatcher {
19
20
/** Subject for notifying that a registered scrollable reference element has been scrolled. */
20
21
_scrolled : Subject < void > = new Subject < void > ( ) ;
21
22
23
+ /** Keeps track of the global `scroll` and `resize` subscriptions. */
24
+ _globalSubscription : Subscription = null ;
25
+
26
+ /** Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards. */
27
+ private _scrolledCount = 0 ;
28
+
22
29
/**
23
30
* Map of all the scrollable references that are registered with the service and their
24
31
* scroll event subscriptions.
25
32
*/
26
33
scrollableReferences : Map < Scrollable , Subscription > = new Map ( ) ;
27
34
28
- constructor ( ) {
29
- // By default, notify a scroll event when the document is scrolled or the window is resized.
30
- Observable . fromEvent ( window . document , 'scroll' ) . subscribe ( ( ) => this . _notify ( ) ) ;
31
- Observable . fromEvent ( window , 'resize' ) . subscribe ( ( ) => this . _notify ( ) ) ;
32
- }
33
-
34
35
/**
35
36
* Registers a Scrollable with the service and listens for its scrolled events. When the
36
37
* scrollable is scrolled, the service emits the event in its scrolled observable.
37
38
* @param scrollable Scrollable instance to be registered.
38
39
*/
39
40
register ( scrollable : Scrollable ) : void {
40
41
const scrollSubscription = scrollable . elementScrolled ( ) . subscribe ( ( ) => this . _notify ( ) ) ;
42
+
41
43
this . scrollableReferences . set ( scrollable , scrollSubscription ) ;
42
44
}
43
45
@@ -53,18 +55,36 @@ export class ScrollDispatcher {
53
55
}
54
56
55
57
/**
56
- * Returns an observable that emits an event whenever any of the registered Scrollable
58
+ * Subscribes to an observable that emits an event whenever any of the registered Scrollable
57
59
* references (or window, document, or body) fire a scrolled event. Can provide a time in ms
58
60
* to override the default "throttle" time.
59
61
*/
60
- scrolled ( auditTimeInMs : number = DEFAULT_SCROLL_TIME ) : Observable < void > {
61
- // In the case of a 0ms delay, return the observable without auditTime since it does add
62
- // a perceptible delay in processing overhead.
63
- if ( auditTimeInMs == 0 ) {
64
- return this . _scrolled . asObservable ( ) ;
62
+ scrolled ( auditTimeInMs : number = DEFAULT_SCROLL_TIME , callback : ( ) => any ) : Subscription {
63
+ // In the case of a 0ms delay, use an observable without auditTime
64
+ // since it does add a perceptible delay in processing overhead.
65
+ let observable = auditTimeInMs > 0 ?
66
+ this . _scrolled . asObservable ( ) . auditTime ( auditTimeInMs ) :
67
+ this . _scrolled . asObservable ( ) ;
68
+
69
+ this . _scrolledCount ++ ;
70
+
71
+ if ( ! this . _globalSubscription ) {
72
+ this . _globalSubscription = Observable . merge (
73
+ Observable . fromEvent ( window . document , 'scroll' ) ,
74
+ Observable . fromEvent ( window , 'resize' )
75
+ ) . subscribe ( ( ) => this . _notify ( ) ) ;
65
76
}
66
77
67
- return this . _scrolled . asObservable ( ) . auditTime ( auditTimeInMs ) ;
78
+ // Note that we need to do the subscribing from here, in order to be able to remove
79
+ // the global event listeners once there are no more subscriptions.
80
+ return observable . subscribe ( callback ) . add ( ( ) => {
81
+ this . _scrolledCount -- ;
82
+
83
+ if ( this . _globalSubscription && ! this . scrollableReferences . size && ! this . _scrolledCount ) {
84
+ this . _globalSubscription . unsubscribe ( ) ;
85
+ this . _globalSubscription = null ;
86
+ }
87
+ } ) ;
68
88
}
69
89
70
90
/** Returns all registered Scrollables that contain the provided element. */
0 commit comments