diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts index 152aa17b3973..5b55cd224e9e 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts @@ -287,16 +287,6 @@ describe('MDC-based MatSlideToggle without forms', () => { expect(document.activeElement).toBe(buttonElement); })); - it('should focus on underlying element when the host is focused', fakeAsync(() => { - expect(document.activeElement).not.toBe(buttonElement); - - slideToggleElement.focus(); - fixture.detectChanges(); - tick(); - - expect(document.activeElement).toBe(buttonElement); - })); - it('should not manually move focus to underlying when focus comes from mouse or touch', fakeAsync( inject([FocusMonitor], (focusMonitor: FocusMonitor) => { expect(document.activeElement).not.toBe(buttonElement); @@ -397,13 +387,13 @@ describe('MDC-based MatSlideToggle without forms', () => { expect(switchEl.classList).toContain('mdc-switch--checked'); })); - it('should set the tabindex of the host element to -1', fakeAsync(() => { + it('should remove the tabindex from the host node', fakeAsync(() => { const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr); fixture.detectChanges(); const slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.nativeElement; - expect(slideToggle.getAttribute('tabindex')).toBe('-1'); + expect(slideToggle.hasAttribute('tabindex')).toBe(false); })); it('should remove the tabindex from the host element when disabled', fakeAsync(() => { diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts index 458970f5e499..058238c8a43a 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts +++ b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts @@ -66,8 +66,8 @@ export class MatSlideToggleChange { host: { 'class': 'mat-mdc-slide-toggle', '[id]': 'id', - // Needs to be `-1` so it can still receive programmatic focus. - '[attr.tabindex]': 'disabled ? null : -1', + // Needs to be removed since it causes some a11y issues (see #21266). + '[attr.tabindex]': 'null', '[attr.aria-label]': 'null', '[attr.aria-labelledby]': 'null', '[class.mat-primary]': 'color === "primary"', @@ -221,12 +221,7 @@ export class MatSlideToggle implements ControlValueAccessor, AfterViewInit, OnDe foundation.setChecked(this.checked); this._focusMonitor.monitor(this._elementRef, true).subscribe(focusOrigin => { - // Only forward focus manually when it was received programmatically or through the - // keyboard. We should not do this for mouse/touch focus for two reasons: - // 1. It can prevent clicks from landing in Chrome (see #18269). - // 2. They're already handled by the wrapping `label` element. if (focusOrigin === 'keyboard' || focusOrigin === 'program') { - this._switchElement.nativeElement.focus(); this._focused = true; } else if (!focusOrigin) { // When a focused element becomes disabled, the browser *immediately* fires a blur event. diff --git a/src/material/slide-toggle/slide-toggle.spec.ts b/src/material/slide-toggle/slide-toggle.spec.ts index 071d81f0728c..fd6e137bed9f 100644 --- a/src/material/slide-toggle/slide-toggle.spec.ts +++ b/src/material/slide-toggle/slide-toggle.spec.ts @@ -318,16 +318,6 @@ describe('MatSlideToggle without forms', () => { expect(document.activeElement).toBe(inputElement); })); - it('should focus on underlying element when the host is focused', fakeAsync(() => { - expect(document.activeElement).not.toBe(inputElement); - - slideToggleElement.focus(); - fixture.detectChanges(); - flush(); - - expect(document.activeElement).toBe(inputElement); - })); - it('should not manually move focus to underlying when focus comes from mouse or touch', inject( [FocusMonitor], (focusMonitor: FocusMonitor) => { @@ -410,13 +400,13 @@ describe('MatSlideToggle without forms', () => { .toBe(5); })); - it('should set the tabindex of the host element to -1', fakeAsync(() => { + it('should remove the tabindex from the host node', fakeAsync(() => { const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr); fixture.detectChanges(); const slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.nativeElement; - expect(slideToggle.getAttribute('tabindex')).toBe('-1'); + expect(slideToggle.hasAttribute('tabindex')).toBe(false); })); it('should remove the tabindex from the host element when disabled', fakeAsync(() => { diff --git a/src/material/slide-toggle/slide-toggle.ts b/src/material/slide-toggle/slide-toggle.ts index 19205094945c..6cb2215e3c24 100644 --- a/src/material/slide-toggle/slide-toggle.ts +++ b/src/material/slide-toggle/slide-toggle.ts @@ -83,8 +83,8 @@ const _MatSlideToggleBase = mixinTabIndex( host: { 'class': 'mat-slide-toggle', '[id]': 'id', - // Needs to be `-1` so it can still receive programmatic focus. - '[attr.tabindex]': 'disabled ? null : -1', + // Needs to be removed since it causes some a11y issues (see #21266). + '[attr.tabindex]': 'null', '[attr.aria-label]': 'null', '[attr.aria-labelledby]': 'null', '[class.mat-checked]': 'checked', @@ -198,13 +198,7 @@ export class MatSlideToggle ngAfterContentInit() { this._focusMonitor.monitor(this._elementRef, true).subscribe(focusOrigin => { - // Only forward focus manually when it was received programmatically or through the - // keyboard. We should not do this for mouse/touch focus for two reasons: - // 1. It can prevent clicks from landing in Chrome (see #18269). - // 2. They're already handled by the wrapping `label` element. - if (focusOrigin === 'keyboard' || focusOrigin === 'program') { - this._inputElement.nativeElement.focus(); - } else if (!focusOrigin) { + if (!focusOrigin) { // When a focused element becomes disabled, the browser *immediately* fires a blur event. // Angular does not expect events to be raised during change detection, so any state // change (such as a form control's 'ng-touched') will cause a changed-after-checked