3
3
* @name material.components.tooltip
4
4
*/
5
5
angular
6
- . module ( 'material.components.tooltip' , [ 'material.core' ] )
7
- . directive ( 'mdTooltip' , MdTooltipDirective ) ;
6
+ . module ( 'material.components.tooltip' , [ 'material.core' ] )
7
+ . directive ( 'mdTooltip' , MdTooltipDirective )
8
+ . service ( '$$mdTooltipRegistry' , MdTooltipRegistry ) ;
8
9
9
10
/**
10
11
* @ngdoc directive
@@ -33,8 +34,8 @@ angular
33
34
* @param {boolean= } md-autohide If present or provided with a boolean value, the tooltip will hide on mouse leave, regardless of focus
34
35
* @param {string= } md-direction Which direction would you like the tooltip to go? Supports left, right, top, and bottom. Defaults to bottom.
35
36
*/
36
- function MdTooltipDirective ( $timeout , $window , $$rAF , $document , $mdUtil , $mdTheming , $rootElement ,
37
- $animate , $q , $interpolate ) {
37
+ function MdTooltipDirective ( $timeout , $window , $$rAF , $document , $mdUtil , $mdTheming , $animate ,
38
+ $interpolate , $$mdTooltipRegistry ) {
38
39
39
40
var ENTER_EVENTS = 'focus touchstart mouseenter' ;
40
41
var LEAVE_EVENTS = 'blur touchcancel mouseleave' ;
@@ -143,7 +144,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
143
144
// - In these cases the scope might not have been destroyed, which is why we
144
145
// destroy it manually. An example of this can be having `md-visible="false"` and
145
146
// adding tooltips while they're invisible. If `md-visible` becomes true, at some
146
- // point, you'd usually get a lot of inputs .
147
+ // point, you'd usually get a lot of tooltips .
147
148
// - We use `.one`, not `.on`, because this only needs to fire once. If we were
148
149
// using `.on`, it would get thrown into an infinite loop.
149
150
// - This kicks off the scope's `$destroy` event which finishes the cleanup.
@@ -202,21 +203,21 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
202
203
var windowBlurHandler = function ( ) {
203
204
elementFocusedOnWindowBlur = document . activeElement === parent [ 0 ] ;
204
205
} ;
206
+
205
207
var elementFocusedOnWindowBlur = false ;
206
208
207
209
function windowScrollHandler ( ) {
208
210
setVisible ( false ) ;
209
211
}
210
212
211
- angular . element ( $window )
212
- . on ( 'blur' , windowBlurHandler )
213
- . on ( 'resize' , debouncedOnResize ) ;
213
+ $$mdTooltipRegistry . register ( 'scroll' , windowScrollHandler , true ) ;
214
+ $$mdTooltipRegistry . register ( 'blur' , windowBlurHandler ) ;
215
+ $$mdTooltipRegistry . register ( 'resize' , debouncedOnResize ) ;
214
216
215
- document . addEventListener ( 'scroll' , windowScrollHandler , true ) ;
216
217
scope . $on ( '$destroy' , function ( ) {
217
- angular . element ( $window )
218
- . off ( 'blur' , windowBlurHandler )
219
- . off ( 'resize' , debouncedOnResize ) ;
218
+ $$mdTooltipRegistry . deregister ( 'scroll' , windowScrollHandler , true ) ;
219
+ $$mdTooltipRegistry . deregister ( 'blur' , windowBlurHandler ) ;
220
+ $$mdTooltipRegistry . deregister ( 'resize' , debouncedOnResize ) ;
220
221
221
222
parent
222
223
. off ( ENTER_EVENTS , enterHandler )
@@ -225,7 +226,6 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
225
226
226
227
// Trigger the handler in case any the tooltip was still visible.
227
228
leaveHandler ( ) ;
228
- document . removeEventListener ( 'scroll' , windowScrollHandler , true ) ;
229
229
attributeObserver && attributeObserver . disconnect ( ) ;
230
230
} ) ;
231
231
@@ -248,6 +248,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
248
248
}
249
249
}
250
250
} ;
251
+
251
252
var leaveHandler = function ( ) {
252
253
var autohide = scope . hasOwnProperty ( 'autohide' ) ? scope . autohide : attr . hasOwnProperty ( 'mdAutohide' ) ;
253
254
@@ -266,6 +267,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
266
267
}
267
268
mouseActive = false ;
268
269
} ;
270
+
269
271
var mousedownHandler = function ( ) {
270
272
mouseActive = true ;
271
273
} ;
@@ -386,3 +388,78 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
386
388
}
387
389
388
390
}
391
+
392
+ /**
393
+ * Service that is used to reduce the amount of listeners that are being
394
+ * registered on the `window` by the tooltip component. Works by collecting
395
+ * the individual event handlers and dispatching them from a global handler.
396
+ *
397
+ * @ngInject
398
+ */
399
+ function MdTooltipRegistry ( ) {
400
+ var listeners = { } ;
401
+ var ngWindow = angular . element ( window ) ;
402
+
403
+ return {
404
+ register : register ,
405
+ deregister : deregister
406
+ } ;
407
+
408
+ /**
409
+ * Global event handler that dispatches the registered
410
+ * handlers in the service.
411
+ * @param {Event } event Event object passed in by the browser.
412
+ */
413
+ function globalEventHandler ( event ) {
414
+ if ( listeners [ event . type ] ) {
415
+ listeners [ event . type ] . forEach ( function ( currentHandler ) {
416
+ currentHandler . call ( this , event ) ;
417
+ } , this ) ;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Registers a new handler with the service.
423
+ * @param {String } type Type of event to be registered.
424
+ * @param {Function } handler Event handler
425
+ * @param {Boolean } useCapture Whether to use event capturing.
426
+ */
427
+ function register ( type , handler , useCapture ) {
428
+ var array = listeners [ type ] = listeners [ type ] || [ ] ;
429
+
430
+ if ( ! array . length ) {
431
+ if ( useCapture ) {
432
+ window . addEventListener ( type , globalEventHandler , true ) ;
433
+ } else {
434
+ ngWindow . on ( type , globalEventHandler ) ;
435
+ }
436
+ }
437
+
438
+ if ( array . indexOf ( handler ) === - 1 ) {
439
+ array . push ( handler ) ;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Removes an event handler from the service.
445
+ * @param {String } type Type of event handler.
446
+ * @param {Function } handler The event handler itself.
447
+ * @param {Boolean } useCapture Whether the event handler used event capturing.
448
+ */
449
+ function deregister ( type , handler , useCapture ) {
450
+ var array = listeners [ type ] ;
451
+ var index = array ? array . indexOf ( handler ) : - 1 ;
452
+
453
+ if ( index > - 1 ) {
454
+ array . splice ( index , 1 ) ;
455
+
456
+ if ( array . length === 0 ) {
457
+ if ( useCapture ) {
458
+ window . removeEventListener ( type , globalEventHandler , true ) ;
459
+ } else {
460
+ ngWindow . off ( type , globalEventHandler ) ;
461
+ }
462
+ }
463
+ }
464
+ }
465
+ }
0 commit comments