Skip to content

Commit ba413a5

Browse files
authored
Merge pull request #73 from mjlumetta/allow-focus-on-focusable-elements
Allow focus to remain on the targets of hash links when the target is focusable
2 parents 0a4fb66 + 83dba7c commit ba413a5

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,9 @@ const MyComponent = () => (
104104
</div>
105105
);
106106
```
107+
108+
## Focus Management
109+
110+
`react-router-hash-link` attempts to recreate the native browser focusing behavior as closely as possible. For non-interactive elements, it calls `element.focus()` followed by `element.blur()` (using a temporary `tabindex` to ensure that the element can be focused programmatically) so that focus _moves_ to the target element but does not remain on it or trigger any style changes. For interactive elements, it calls `element.focus()` and leaves focus on the target element.
111+
112+
If you would like to leave focus on a non-interactive element - for example, to augment the navigation interaction with a visual focus indicator - you can optionally set a `tabindex` on the target element. `react-router-hash-link` will respect the `tabindex` and leave focus on the target in that case.

src/index.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ function reset() {
1616
}
1717
}
1818

19+
function isInteractiveElement(element) {
20+
const formTags = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
21+
const linkTags = ['A', 'AREA'];
22+
return (formTags.includes(element.tagName) && !element.hasAttribute('disabled'))
23+
|| (linkTags.includes(element.tagName) && element.hasAttribute('href'));
24+
}
25+
1926
function getElAndScroll() {
2027
let element = null;
2128
if (hashFragment === '#') {
@@ -43,11 +50,16 @@ function getElAndScroll() {
4350
let originalTabIndex = element.getAttribute('tabindex');
4451
if (originalTabIndex === null) element.setAttribute('tabindex', -1);
4552
element.focus({ preventScroll: true });
46-
// for some reason calling blur() in safari resets the focus region to where it was previously,
47-
// if blur() is not called it works in safari, but then are stuck with default focus styles
48-
// on an element that otherwise might never had focus styles applied, so not an option
49-
element.blur();
50-
if (originalTabIndex === null) element.removeAttribute('tabindex');
53+
if (originalTabIndex === null) {
54+
if (!isInteractiveElement(element)) {
55+
// for some reason calling blur() in safari resets the focus region to where it was previously,
56+
// if blur() is not called it works in safari, but then are stuck with default focus styles
57+
// on an element that otherwise might never had focus styles applied, so not an option
58+
element.blur();
59+
}
60+
61+
element.removeAttribute('tabindex');
62+
}
5163

5264
reset();
5365
return true;

0 commit comments

Comments
 (0)