From 7fc4247572ebeea5ec5311181998629c34e4b752 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sat, 4 Mar 2017 13:16:56 +0100 Subject: [PATCH] fix(focus-trap): exception when element contains SVG on IE 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 80a72e1b0f0a..49900604f4ff 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 7bdbd42f5574..d6cc77704222 100644 --- a/src/lib/core/a11y/focus-trap.ts +++ b/src/lib/core/a11y/focus-trap.ts @@ -130,10 +130,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; } @@ -149,8 +154,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; }