diff --git a/src/focus-visible.js b/src/focus-visible.js index aa752cf..83cd8c7 100644 --- a/src/focus-visible.js +++ b/src/focus-visible.js @@ -24,7 +24,7 @@ function init() { /** * Helper function for legacy browsers and iframes which sometimes focus - * elements like document and body. + * elements like document, body, and non-interactive SVG. * @param {Element} el */ function isValidFocusTarget(el) { @@ -32,7 +32,9 @@ function init() { el && el !== document && el.nodeName !== 'HTML' && - el.nodeName !== 'BODY' + el.nodeName !== 'BODY' && + 'classList' in el && + 'contains' in el.classList ) { return true; } diff --git a/test/fixtures/svg.html b/test/fixtures/svg.html new file mode 100644 index 0000000..3976b91 --- /dev/null +++ b/test/fixtures/svg.html @@ -0,0 +1,26 @@ + + + + svg focus fixture + + + + + + + + + + + diff --git a/test/specs/svg.js b/test/specs/svg.js new file mode 100644 index 0000000..ba10a03 --- /dev/null +++ b/test/specs/svg.js @@ -0,0 +1,63 @@ +const { fixture, FOCUS_RING_STYLE } = require('./helpers'); +const { Key, By } = require('selenium-webdriver'); +const expect = require('expect'); +const driver = global.__driver; + +// IE11 has a strange behavior where it will always focus an on the page. +// This test is to verify that we don't hit an error in this situation. +// See https://github.com/WICG/focus-visible/issues/80#issuecomment-383424156. +describe.only('svg focus', function() { + beforeEach(function() { + return fixture('svg.html'); + }); + + it('should NOT apply .focus-visible if a non-interactive SVG is keyboard focused', async function() { + let actual; + + // Tells Selenium to keep sending Tabs until the end element is reached. + async function tabUntil(body, end) { + let activeId; + let activeElement; + + while (true) { + activeId = await driver.executeScript( + `return document.activeElement.id` + ); + + if (activeId) { + if (activeId === end) { + break; + } + // Only IE11 will stop here and focus the #icon element. + // If the element has focus, assert that :focus-visible has not been + // applied to it. + if (activeId === 'icon') { + actual = await driver.executeScript(` + return window.getComputedStyle(document.querySelector('#end')).outlineColor + `); + expect(actual).toNotEqual(FOCUS_RING_STYLE); + } + // Move focus to the next element. + // IE11's selenium driver won't move focus if we send it to body again + // so we need to send it to the activeElement. + activeElement = await driver.findElement(By.css(`#${activeId}`)); + await activeElement.sendKeys(Key.TAB); + } else { + // Work around IE11 weirdness which sends focus to first. + await body.sendKeys(Key.TAB); + } + } + } + + let body = await driver.findElement(By.css('body')); + await body.click(); + // Tabs through the document until it reaches the last element. + // In IE11 the non-interactive SVG, #icon will be focused. + // If it throws, then the test will fail. + await tabUntil(body, 'end'); + actual = await driver.executeScript(` + return window.getComputedStyle(document.querySelector('#end')).outlineColor + `); + expect(actual).toEqual(FOCUS_RING_STYLE); + }); +});