Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow focus to remain on the targets of hash links when the target is focusable #73

Merged
merged 4 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,9 @@ const MyComponent = () => (
</div>
);
```

## Focus Management

`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.

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.
22 changes: 17 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ function reset() {
}
}

function isInteractiveElement(element) {
const formTags = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'];
const linkTags = ['A', 'AREA'];
return (formTags.includes(element.tagName) && !element.hasAttribute('disabled'))
|| (linkTags.includes(element.tagName) && element.hasAttribute('href'));
}

function getElAndScroll() {
let element = null;
if (hashFragment === '#') {
Expand Down Expand Up @@ -43,11 +50,16 @@ function getElAndScroll() {
let originalTabIndex = element.getAttribute('tabindex');
if (originalTabIndex === null) element.setAttribute('tabindex', -1);
element.focus({ preventScroll: true });
// for some reason calling blur() in safari resets the focus region to where it was previously,
// if blur() is not called it works in safari, but then are stuck with default focus styles
// on an element that otherwise might never had focus styles applied, so not an option
element.blur();
if (originalTabIndex === null) element.removeAttribute('tabindex');
if (originalTabIndex === null) {
if (!isInteractiveElement(element)) {
// for some reason calling blur() in safari resets the focus region to where it was previously,
// if blur() is not called it works in safari, but then are stuck with default focus styles
// on an element that otherwise might never had focus styles applied, so not an option
element.blur();
}

element.removeAttribute('tabindex');
}

reset();
return true;
Expand Down