Skip to content

Commit 5d54920

Browse files
crisbetommalerba
authored andcommitted
fix(tooltip): add fallback for touch devices if Hammer isn't loaded (#13580)
Unlike other components that we use Hammer on, the tooltip won't show up at all on a touch device if Hammer isn't loaded. These changes add a fallback to using a `touchstart` event to trigger the tooltip. Also fixes the `_manualListeners` not being cleared on Android. Fixes #13536.
1 parent bbeb5ef commit 5d54920

File tree

1 file changed

+17
-11
lines changed

1 file changed

+17
-11
lines changed

src/lib/tooltip/tooltip.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Directionality} from '@angular/cdk/bidi';
1111
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1212
import {ESCAPE} from '@angular/cdk/keycodes';
1313
import {BreakpointObserver, Breakpoints, BreakpointState} from '@angular/cdk/layout';
14+
import {HammerLoader, HAMMER_LOADER} from '@angular/platform-browser';
1415
import {
1516
FlexibleConnectedPositionStrategy,
1617
HorizontalConnectionPos,
@@ -201,27 +202,34 @@ export class MatTooltip implements OnDestroy {
201202
private _scrollDispatcher: ScrollDispatcher,
202203
private _viewContainerRef: ViewContainerRef,
203204
private _ngZone: NgZone,
204-
private _platform: Platform,
205+
platform: Platform,
205206
private _ariaDescriber: AriaDescriber,
206207
private _focusMonitor: FocusMonitor,
207208
@Inject(MAT_TOOLTIP_SCROLL_STRATEGY) scrollStrategy: any,
208209
@Optional() private _dir: Directionality,
209210
@Optional() @Inject(MAT_TOOLTIP_DEFAULT_OPTIONS)
210-
private _defaultOptions: MatTooltipDefaultOptions) {
211+
private _defaultOptions: MatTooltipDefaultOptions,
212+
@Optional() @Inject(HAMMER_LOADER) hammerLoader?: HammerLoader) {
211213

212214
this._scrollStrategy = scrollStrategy;
213215
const element: HTMLElement = _elementRef.nativeElement;
214216
const elementStyle = element.style as CSSStyleDeclaration & {webkitUserDrag: string};
217+
const hasGestures = typeof window === 'undefined' || (window as any).Hammer || hammerLoader;
215218

216219
// The mouse events shouldn't be bound on mobile devices, because they can prevent the
217220
// first tap from firing its click event or can cause the tooltip to open for clicks.
218-
if (!_platform.IOS && !_platform.ANDROID) {
221+
if (!platform.IOS && !platform.ANDROID) {
219222
this._manualListeners
220223
.set('mouseenter', () => this.show())
221-
.set('mouseleave', () => this.hide())
222-
.forEach((listener, event) => element.addEventListener(event, listener));
224+
.set('mouseleave', () => this.hide());
225+
} else if (!hasGestures) {
226+
// If Hammerjs isn't loaded, fall back to showing on `touchstart`, otherwise
227+
// there's no way for the user to trigger the tooltip on a touch device.
228+
this._manualListeners.set('touchstart', () => this.show());
223229
}
224230

231+
this._manualListeners.forEach((listener, event) => element.addEventListener(event, listener));
232+
225233
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
226234
// When we bind a gesture event on an element (in this case `longpress`), HammerJS
227235
// will add some inline styles by default, including `user-select: none`. This is
@@ -258,12 +266,10 @@ export class MatTooltip implements OnDestroy {
258266
}
259267

260268
// Clean up the event listeners set in the constructor
261-
if (!this._platform.IOS) {
262-
this._manualListeners.forEach((listener, event) =>
263-
this._elementRef.nativeElement.removeEventListener(event, listener));
264-
265-
this._manualListeners.clear();
266-
}
269+
this._manualListeners.forEach((listener, event) => {
270+
this._elementRef.nativeElement.removeEventListener(event, listener);
271+
});
272+
this._manualListeners.clear();
267273

268274
this._destroyed.next();
269275
this._destroyed.complete();

0 commit comments

Comments
 (0)