It's reproducable in all browsers in the following case:
<!doctype html>
<html lang="en">
<head>
<title>Focus</title>
<style>
button { display: inline-block; width: 30px; height: 30px; background-color: red; }
button:focus { background-color:green; }
span { position: absolute; display:inline-block; width:100px }
</style>
</head>
<body>
<button>
<span tabindex="1">
click here
</span>
</button>
</body>
</html>
And tabindex
is not guaranteed to add focus
due to the following:
an element that is only focusable because of its tabindex attribute will fire a click event in response to a non-mouse activation
Accessibility guidelines describe the workaround as such:
Techniques for providing access to scripts include the following:
Allow the user to configure the user agent so that mouseover/mouseout event handlers are activated by (and activate) focus/blur events. Similarly, allow the user to use a key command, such as enter
and Shift-Enter
to fire onclick
and ondblclick
events.
Implement "Document Object Model (DOM) Level 2 Events Specification" [DOM2EVENTS] events with a single activation event and provide a method for firing that event with each supported input device or input API. These should be the same as the click events and mappings provided above (but note that a user agent which is also an editor may wish to use single click events for moving a system caret, and want to provide a different behavior to activate using the mouse).
For example, Amaya [AMAYA] uses a doAction
command for activating links and form elements, which can be activated either by the mouse (and it is possible to set it for single-click or double-click) or by the keyboard (it is possible to set it for any key using Amaya's keyboard configuration)
For authors: Document the effects of known important scripts to give users an idea in advance of what they do. Do so by using the relevant elements or attributes of the (markup language) specification, or if there aren't any, make available a description of the script behavior.
Also, this behavior is unspecified:
The :focus pseudo-class applies while an element has the focus (accepts keyboard events or other forms of text input).
An element may match several pseudo-classes at the same time.
CSS does not define which elements may be in the above states, or how the states are entered and left.
Use a focusin/focusout polyfill to normalize the cross-browser behavior:
function focusPolyfill()
{
'use strict';
var w = window,
d = w.document;
function addPolyfill(e)
{
var
type = e.type === 'focus' ? 'focusin' : 'focusout',
event = new CustomEvent(type, { bubbles:true, cancelable:false });
event.c1Generated = true;
e.target.dispatchEvent( event );
}
function removePolyfill(e)
{
if(!e.c1Generated)
{
// focus after focusin, so chrome will the first time trigger tow times focusin
d.removeEventListener('focus' ,addPolyfill ,true);
d.removeEventListener('blur' ,addPolyfill ,true);
d.removeEventListener('focusin' ,removePolyfill ,true);
d.removeEventListener('focusout' ,removePolyfill ,true);
}
}
function removeHandler()
{
d.removeEventListener('focusin' ,removePolyfill ,true);
d.removeEventListener('focusout' ,removePolyfill ,true);
}
if( w.onfocusin === undefined )
{
d.addEventListener('focus' ,addPolyfill ,true);
d.addEventListener('blur' ,addPolyfill ,true);
d.addEventListener('focusin' ,removePolyfill ,true);
d.addEventListener('focusout' ,removePolyfill ,true);
}
w.setTimeout(removeHandler);
}
The following is the typical sequence of events when a focus is shifted between elements, including the deprecated DOMFocusIn and DOMFocusOut events. The order shown assumes that no element is initially focused.
User shifts focus
1. focusin Sent before first target element receives focus
2. focus Sent after first target element receives focus
3. DOMFocusIn If supported
User shifts focus
4. focusout Sent before first target element loses focus
5. focusin Sent before second target element receives focus
6. blur Sent after first target element loses focus
7. DOMFocusOut If supported
8. focus Sent after second target element receives focus
9. DOMFocusIn If supported
References