Skip to content

Commit 7553e1c

Browse files
authored
fix(material/core): ripple loader not working in shadow DOM (#29015)
Fixes the following issues that prevented the button ripples from being loaded in the shadow DOM: * We weren't resolving the event target properly. * We were using `click` as a fallback which happens too late for the very first ripple. These changes switch to `mousedown`. Fixes #29011.
1 parent d0348dd commit 7553e1c

File tree

2 files changed

+21
-15
lines changed

2 files changed

+21
-15
lines changed

src/material/button/button.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ describe('MDC-based MatButton', () => {
333333
});
334334

335335
// Ensure each of these events triggers the initialization of the button ripple.
336-
for (const event of ['click', 'touchstart', 'mouseenter', 'focus']) {
336+
for (const event of ['mousedown', 'touchstart', 'mouseenter', 'focus']) {
337337
it(`should render the ripple once a button has received a "${event}" event`, () => {
338338
const fab = fixture.debugElement.query(By.css('button[mat-fab]'))!;
339339
let ripple = fab.nativeElement.querySelector('.mat-mdc-button-ripple');

src/material/core/private/ripple-loader.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ import {
1616
inject,
1717
} from '@angular/core';
1818
import {MAT_RIPPLE_GLOBAL_OPTIONS, MatRipple} from '../ripple';
19-
import {Platform} from '@angular/cdk/platform';
19+
import {Platform, _getEventTarget} from '@angular/cdk/platform';
2020

2121
/** The options for the MatRippleLoader's event listeners. */
2222
const eventListenerOptions = {capture: true};
2323

24-
/** The events that should trigger the initialization of the ripple. */
25-
const rippleInteractionEvents = ['focus', 'click', 'mouseenter', 'touchstart'];
24+
/**
25+
* The events that should trigger the initialization of the ripple.
26+
* Note that we use `mousedown`, rather than `click`, for mouse devices because
27+
* we can't rely on `mouseenter` in the shadow DOM and `click` happens too late.
28+
*/
29+
const rippleInteractionEvents = ['focus', 'mousedown', 'mouseenter', 'touchstart'];
2630

2731
/** The attribute attached to a component whose ripple has not yet been initialized. */
2832
const matRippleUninitialized = 'mat-ripple-loader-uninitialized';
@@ -130,20 +134,22 @@ export class MatRippleLoader implements OnDestroy {
130134
}
131135
}
132136

133-
/** Handles creating and attaching component internals when a component it is initially interacted with. */
137+
/**
138+
* Handles creating and attaching component internals
139+
* when a component is initially interacted with.
140+
*/
134141
private _onInteraction = (event: Event) => {
135-
if (!(event.target instanceof HTMLElement)) {
136-
return;
137-
}
138-
const eventTarget = event.target as HTMLElement;
142+
const eventTarget = _getEventTarget(event);
139143

140-
// TODO(wagnermaciel): Consider batching these events to improve runtime performance.
144+
if (eventTarget instanceof HTMLElement) {
145+
// TODO(wagnermaciel): Consider batching these events to improve runtime performance.
146+
const element = eventTarget.closest(
147+
`[${matRippleUninitialized}="${this._globalRippleOptions?.namespace ?? ''}"]`,
148+
);
141149

142-
const element = eventTarget.closest(
143-
`[${matRippleUninitialized}="${this._globalRippleOptions?.namespace ?? ''}"]`,
144-
);
145-
if (element) {
146-
this._createRipple(element as HTMLElement);
150+
if (element) {
151+
this._createRipple(element as HTMLElement);
152+
}
147153
}
148154
};
149155

0 commit comments

Comments
 (0)