@@ -4,6 +4,7 @@ import {Subject} from 'rxjs/Subject';
44import { Observable } from 'rxjs/Observable' ;
55import { Subscription } from 'rxjs/Subscription' ;
66import 'rxjs/add/observable/fromEvent' ;
7+ import 'rxjs/add/observable/merge' ;
78import 'rxjs/add/operator/auditTime' ;
89
910
@@ -19,25 +20,26 @@ export class ScrollDispatcher {
1920 /** Subject for notifying that a registered scrollable reference element has been scrolled. */
2021 _scrolled : Subject < void > = new Subject < void > ( ) ;
2122
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+
2229 /**
2330 * Map of all the scrollable references that are registered with the service and their
2431 * scroll event subscriptions.
2532 */
2633 scrollableReferences : Map < Scrollable , Subscription > = new Map ( ) ;
2734
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-
3435 /**
3536 * Registers a Scrollable with the service and listens for its scrolled events. When the
3637 * scrollable is scrolled, the service emits the event in its scrolled observable.
3738 * @param scrollable Scrollable instance to be registered.
3839 */
3940 register ( scrollable : Scrollable ) : void {
4041 const scrollSubscription = scrollable . elementScrolled ( ) . subscribe ( ( ) => this . _notify ( ) ) ;
42+
4143 this . scrollableReferences . set ( scrollable , scrollSubscription ) ;
4244 }
4345
@@ -53,18 +55,36 @@ export class ScrollDispatcher {
5355 }
5456
5557 /**
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
5759 * references (or window, document, or body) fire a scrolled event. Can provide a time in ms
5860 * to override the default "throttle" time.
5961 */
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 ( ) ) ;
6576 }
6677
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+ } ) ;
6888 }
6989
7090 /** Returns all registered Scrollables that contain the provided element. */
0 commit comments