# Managing focus in SVG
This document explains how managing focus in SVG works and how it differs from HTML.
# SVG specifications
There are three relevant iterations of SVG considering focus management: SVG 1.1, SVG 1.2 Tiny and SVG 2. Although SVG 1.1 knew focus events such as
focusout and mentions the CSS pseudo-class
:focus, it does not describe how elements can be made focusable. SVG 1.2 Tiny rectifies that by adding the
focusable attribute. And finally SVG 2 defines focus management to be the same as HTML, including the
# Browser compatibility
While the Blink and WebKit based browsers (Google Chrome, Apple Safari) have had solid support for
tabindex on SVG elements for quite a while, the story is different for Gecko (Mozilla Firefox), Trident (Microsoft Internet Explorer) and EdgeHTML (Microsoft Edge).
Up to version 50 Firefox had very limited support for focus in SVG.
<a xlink:href="…"> elements could be focused by keyboard and mouse, but the
.focus() method was missing. The only way to shift focus to an SVG element was by user action (keyboard, pointer). As of version 51 Firefox has caught up.
Internet Explorer is the only mainstream browser to implement SVG 1.2 Tiny's
focusable attribute to make arbitrary elements focusable. However, it does not support the
tabindex attribute and
.focus() method on
SVGElements. Support for
focusable and the lack of
.focus() carried over to Microsoft Edge, which only added support for the
tabindex attribute in version 14.
# Making SVG elements focusable
In SVG only the element
<a xlink:href="…"> is considered naturally focusable by all browsers. Internet Explorer and Microsoft Edge up to version 13 also consider
<svg> itself keyboard focusable. Firefox 52 considers
<svg> focusable, if it's the root element of a browsing context (e.g.
Browsers on macOS in general and Apple's Safari in particular need to be configured for keyboard navigation.
The HTML attribute
tabindex made its way into SVG in SVG 2. The behavior of the attribute is defined to be the same as in HTML, i.e. a negative value making elements script and pointer focusable, a value of
0 adding the element to the document's tabbing order, and a positive value affecting the order in the document's sequential focus navigation list.
Don’t Use Tabindex Greater than 0 also applies to SVG content.
focusable attribute defined by SVG Tiny 1.2 is only implemented in Internet Explorer and Microsoft Edge. Unlike
tabindex this attribute has a boolean value, where
focusable="false" makes the element inert. Besides removing SVG links from the tabbing order by way of
<a xlink:href="…" focusable="false">, this property also comes in handy to prevent the SVG's root element from unnecessarily receiving focus:
In general it's not a good idea to remove interactive elements from the tabbing order, such as
<a href="…" tabindex="-1"> would achieve. However, this is the only way for utilities like
ally.element.disabled and the WICG
inert polyfill to prevent elements from receiving keyboard focus, while maintaining their visual presentation on screen.
As of version 14 Microsoft Edge also supports the
tabindex attribute. For the case
<rect … tabindex="0" focusable="true"> the situation is clear: the element element is keyboard focusable. However both attributes could be contradicting each other, in which case the
focusable attribute wins and the
tabindex attribute is ignored:
<svg focusable="false" tabindex="0">is considered inert
<svg focusable="true" tabindex="-1">is considered keyboard focusable
# Event Listeners
SVG2 - 15.9. Focus: […] may allow focus to reach elements which have been assigned listeners for mouse, pointer, or focus events.
Blink and WebKit (Chrome and Safari) consider any SVG element with a
focus event handler focusable. However, mouse events (e.g.
mousedown) and keyboard events (e.g.
keydown) don't cause elements to become focusable.
Since we can't query the event listeners attached to DOM elements, it's impossible to identify elements that have become focusable by way of a
focus event listener.
Any element that has a
focus event listener attached should also be made focusable by adding
# Focusing SVG elements
In Blink and WebKit based browsers focusing SVG elements is not different from focusing an HTML element, the
.focus() method is available on
SVGElement. Since Firefox 51 the
.focus() method is also available. In Internet Explorer and Microsoft Edge this is not the case.
Internet Explorer and Microsoft Edge up to version 12 allow misappropriating the
.focus() method by applying the method from
HTMLElement's prototype to an
HTMLElement.prototoype.focus.apply(svgElement). Microsoft Edge 13 joined Firefox (up to version 50) in rejecting such attempts by throwing an error:
// Firefox TypeError: 'focus' called on an object that does not implement interface HTMLElement. // Edge TypeError: Invalid calling object
While Firefox before version 51 can't focus SVG elements because of this limitation, Microsoft Edge 13 and newer can be persuaded otherwise.
# The focus SVG element hack
Mutating the active element shows that Edge will shift focus to the next focusable ancestor when the active element is disabled. While it's not possible to use an
<input> element in
<svg> directly, the
<foreignObject> element allows HTML to be embedded in SVG.
It is possible to programmatically shift focus to an
<a xlink:href="…"> element in Microsoft Edge 13+ by going through the following steps:
<foreignObject><input /></foreignObject>to the target element
element.focus()on the input element
- set the input element to
element.disabled = true;
- remove the
<foreignObject>element injected in step 1
- the target element has received focus and become the active element
# The blur SVG element hack
Browsers that don't provide the
.focus() method on
SVGElement also lack the
.blur() method. Sadly calling
document.body.focus() won't achieve what
document.activeElement.blur() should've done. But shifting focus back to
<body> is possible by going through the following steps:
element.focus()on the input element
element.blur()on the input element
- remove the
<input>element injected in step 1
document.bodyis now the active element
# Styling focused SVG elements
All browsers support the CSS pseudo-class
:focus. However, Firefox until version 50, Internet Explorer and Edge including version 15 do not support the CSS property
outline on SVG elements.
It is also worth noting that
<a> elements do not have dimensions of their own. The focus outline can therefore only be drawn on the element's bounding box, which is the smallest rectangle enclosing all of the
<a> element's children (demo). WebKit (Safari 10) does not render the focus outline on an
<a> element's bounding box, so the focus state of SVG links is not visually indicated. Internet Explorer and Edge render the native focus outline, as long as the
outline property isn't set.
stroke on text will create a different kind of outline than produced by the CSS properties
border on HTML elements. A rectangular outline similar to the browser's default focus outline would have to be rendered using SVG itself, e.g. by way of a
Also note that browsers are inconsistent about how focus outlines should behave in scaled images, as the following table shows (test case):
|Browser||Native focus indication||
|MS Edge 15||fix||not supported||scaled|
<foreignObject> allows arbitrary HTML to be embedded in SVG. This includes interactive elements like
<input> elements, as we've already seen in the focus SVG element hack. However, the presented hack (deliberately) omitted a few elements and attributes.
Most resources on the web, including the SVG 2 spec present the
<foreignObject> element in a setting like the following:
<svg xmlns="http://www.w3.org/2000/svg"> <switch> <foreignObject requiredExtensions="http://example.com/SVGExtensions/EmbeddedXHTML"> <body xmlns="http://www.w3.org/1999/xhtml"> <p>HTML content</p> </body> </foreignObject> </switch> </svg>
It's not immediately obvious that the snippet won't render the text HTML content in any browser. The reason is the invalid extension URI
http://example.com/SVGExtensions/EmbeddedXHTML provided in the
requiredExtensions attribute. But even if the correct URI
http://www.w3.org/1999/xhtml is used, Chrome still won't render the embedded HTML. Similar problems exist with the
requiredFeatures attribute, which was dropped in SVG 2. See the following table and example for a support matrix:
|invalid extension||not rendered||not rendered||not rendered|
|valid extension||not rendered||rendered||rendered|
|invalid feature||rendered||not rendered||not rendered|
|no extension or feature||rendered||rendered||rendered|
Do not use the attributes
<foreignObject> is being rendered because of these restrictions.
Note that the
<foreignObject> element is not supported in Internet Explorer 11 and below.
<defs> element defines that descendants are not rendered directly and should therefore not be focusable according to SVG 2 - 15.9. Focus. But currently all browsers consider the link in the following snippet keyboard focusable:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <g id="use-somewhere"> <a xlink:href="…"><text>a link</text></a> </g> </defs> </svg>
Do not put interactive content in
<defs> to avoid visual focus from getting lost.
<use> element references other content, that is rendered in its stead. The
<use> element is a Shadow Host, hiding all of the referenced content.
If the referenced content has focusable elements and one of those shadowed elements has focus, the
<use> element will be the document's active element. Currently only Chrome and Safari 10 have functioning Shadow DOM implementations to handle this "correctly". However, Chrome is not able to prevent shadowed content from receiving keyboard focus when the host is removed from the document's tabbing order via
As with other Shadow Hosts, Firefox does not properly encapsulate focused elements. Instead of the
<use> element becoming the active element, the focusable content within the ShadowRoot are exposed as the document's active elements.
Chrome has an interesting bug (665121) regarding the rendering of focus indication within
<use> elements, as can be observed in the test case. The previous section already explained that elements in
<defs> receive focus when they shouldn't. But to make things worse, the supposedly cloned elements in the
<use> elements indicate focus when the original element in
<defs> has focus. But when the clones themselves receive focus, there is no indication.
Safari 8 really trips up when it encounters
<use> elements that reference focusable content. Once a
<use> element's content received focus, it is not possible to shift focus with Tab and Shift Tab anymore. Keyboard users are effectively stranded, as this demo shows.
There is a weird problem with
<use> elements in Safari 10 (but not in Safari 9 or the current WebKit nightly). When
<use> elements are not surrounded by whitespace (e.g.
<svg><use xlink:href="…" /></svg>), the document's tabbing order is truncated so that all elements following that
<use> are not accessible via the Tab key anymore, as this demo shows.
Do not reference interactive content in
<use>. Browser support is not great and in the case of Safari 8 a real problem.
# Hidden SVG with focusable content
We must be careful when hiding SVGs that host focusable content. Opening the demo in Safari will show how focus cannot be shifted from the first input to the second using the Tab key. The reason is this hidden (via
display: none or
visibility: hidden) SVG link in between. While Blink 586200 has been fixed in early 2016, WebKit 154114 is still alive and kicking in Safari 10.
ally.element.blurto programmatically shift focus SVG elements.
- Avoid the attributes
<foreignObject>, since their interpretations vary among browsers.
tabindex="0"to all interactive SVG content.
:focusstate using styles other than
outline, as that CSS property is not widely supported for SVG elements.
- Do not host focusable content in
<defs>, as these elements are not made properly inert by browsers.
- Do not reference focusable content in
<use>, since Shadow DOM isn't yet properly implemented in most browsers.
- Do not hide SVG content that hosts focusable elements, as that would break the ability to traverse the document's tabbing order via the keyboard in Safari.
Thanks to Amelia Bellamy-Royds for taking the time to review this piece and provide some very good comments and insights!