From d06ad753e844f4feaf42f32706d2e64313cea3fc Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 7 Mar 2017 01:51:59 +0100 Subject: [PATCH] fix(focus-trap): exception when element contains SVG on IE (#3432) Fixes an exception being thrown by the focus trap, if it contains an SVG element on IE. The problem was that on IE, SVG elements don't have the `children` property, which means that we have to fall back to `childNodes`. Fixes #3410. --- src/lib/core/a11y/focus-trap.spec.ts | 36 +++++++++++++++++++++++++++- src/lib/core/a11y/focus-trap.ts | 22 ++++++++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/lib/core/a11y/focus-trap.spec.ts b/src/lib/core/a11y/focus-trap.spec.ts index 80a72e1b0f0..49900604f4f 100644 --- a/src/lib/core/a11y/focus-trap.spec.ts +++ b/src/lib/core/a11y/focus-trap.spec.ts @@ -9,7 +9,13 @@ describe('FocusTrap', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [FocusTrapDirective, FocusTrapWithBindings, SimpleFocusTrap, FocusTrapTargets], + declarations: [ + FocusTrapDirective, + FocusTrapWithBindings, + SimpleFocusTrap, + FocusTrapTargets, + FocusTrapWithSvg + ], providers: [InteractivityChecker, Platform, FocusTrapFactory] }); @@ -112,6 +118,20 @@ describe('FocusTrap', () => { expect(document.activeElement.id).toBe('last'); }); }); + + describe('special cases', () => { + it('should not throw when it has a SVG child', () => { + let fixture = TestBed.createComponent(FocusTrapWithSvg); + + fixture.detectChanges(); + + let focusTrapInstance = fixture.componentInstance.focusTrapDirective.focusTrap; + + expect(() => focusTrapInstance.focusFirstTabbableElement()).not.toThrow(); + expect(() => focusTrapInstance.focusLastTabbableElement()).not.toThrow(); + }); + }); + }); @@ -156,3 +176,17 @@ class FocusTrapWithBindings { class FocusTrapTargets { @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; } + + +@Component({ + template: ` +
+ + + +
+ ` +}) +class FocusTrapWithSvg { + @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; +} diff --git a/src/lib/core/a11y/focus-trap.ts b/src/lib/core/a11y/focus-trap.ts index c2d4c95b2d5..cd4fb72780d 100644 --- a/src/lib/core/a11y/focus-trap.ts +++ b/src/lib/core/a11y/focus-trap.ts @@ -128,10 +128,15 @@ export class FocusTrap { return root; } - // Iterate in DOM order. - let childCount = root.children.length; - for (let i = 0; i < childCount; i++) { - let tabbableChild = this._getFirstTabbableElement(root.children[i] as HTMLElement); + // Iterate in DOM order. Note that IE doesn't have `children` for SVG so we fall + // back to `childNodes` which includes text nodes, comments etc. + let children = root.children || root.childNodes; + + for (let i = 0; i < children.length; i++) { + let tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ? + this._getFirstTabbableElement(children[i] as HTMLElement) : + null; + if (tabbableChild) { return tabbableChild; } @@ -147,8 +152,13 @@ export class FocusTrap { } // Iterate in reverse DOM order. - for (let i = root.children.length - 1; i >= 0; i--) { - let tabbableChild = this._getLastTabbableElement(root.children[i] as HTMLElement); + let children = root.children || root.childNodes; + + for (let i = children.length - 1; i >= 0; i--) { + let tabbableChild = children[i].nodeType === Node.ELEMENT_NODE ? + this._getLastTabbableElement(children[i] as HTMLElement) : + null; + if (tabbableChild) { return tabbableChild; }