Replies: 36 comments 2 replies
-
Just to make sure that we're on the same page, you're referring to the same thing as this article discusses, correct? https://sarbbottam.github.io/blog/2016/10/22/focus-reset-and-guided-focus-management Whenever the location changes, you want to reset the focus to some initial position (like a full page refresh would produce). I think that what you would want to do is to have a root component (inside of your class Refocus extends React.Component {
componentDidUpdate() {
if (this.node) {
this.node.focus();
}
}
render() {
return <div ref={n => this.node = n} tabIndex={-1}>{this.props.children}</div>;
}
}
// usage
ReactDOM.render((
<BrowserRouter>
<Refocus>
<App />
</Refocus>
</BrowserRouter>
), holder); If I'm completely off, please let me know. |
Beta Was this translation helpful? Give feedback.
-
Yes focus management for SPAs using react is what I am looking for, thanks for the suggestion @pshrmn! I did some extra digging myself, Angular seems to focus on the first h1 on a client side change, which is a pretty nice user experience. Testing the problem, the issue is not just the focus management but letting an assistive technology (screen reader) user know when a page has changed. My current solution: An aria-live div at the top of page (only re-renders on server side) which will announce to AT when I change its contents. (Visually hidden class, non-focusable) <div aria-live="polite" className="gs-u-vh" id="accessibility-message"></div> Then to control the contents of the aria-live div, I have a function that changes its contents when my h1 changes. (As the aria-live div is not re-rendered client side, it will announce its new contents) export function pageChangeAnnouncement() {
let accessibilityHookMessage;
let myH1;
if (typeof window !== 'undefined') {
myH1 = document.getElementsByTagName("h1")[0].innerHTML;
accessibilityHookMessage = `${myH1} , View loaded`;
document.getElementById("accessibility-message").innerHTML = accessibilityHookMessage;
} else {
accessibilityHookMessage = `page change, View loaded`;
}
} I appreciate your suggestion @pshrmn, it sent my down the right path but when I tested it, it did not behave as expected. This way does look a bit old school JS, but it works. let ignoreFirstLoad = true;
if (ignoreFirstLoad) {
ignoreFirstLoad = false;
} else {
let resetFocusItem = document.getElementsByTagName("h1")[0];
resetFocusItem.setAttribute("tabindex", "-1");
resetFocusItem.style.outline = "none";
resetFocusItem.focus();
} A few notes to anyone else trying to manage focus:
|
Beta Was this translation helpful? Give feedback.
-
This seems like a great idea, but not working for me for some reason. I have tried a number of variations of focus, blur, etc. and it doesn't seem to be taking effect. I can see that the |
Beta Was this translation helpful? Give feedback.
-
Nevermind. My problem was that I was doing it in |
Beta Was this translation helpful? Give feedback.
-
I'd like to see more emphasis on an easy-to-use focus management solution in React Router instead of a live region, because functionally it is helpful to put the user's focus in a specific part of the page rather than leaving it where it is (on a Router link). I can use an The problem with focusing automatically when a component renders is you might be focusing at the wrong time, especially if components get reused. Setting focus when the user initiates the action by activating a link is a better approach, I've found. |
Beta Was this translation helpful? Give feedback.
-
@marcysutton I agree, setting focus when the user initiates an action seems the correct approach. In my approach, I reset focus to the h1 when the page changes and announce the new page name. |
Beta Was this translation helpful? Give feedback.
-
@LpmRaven are you still using the h1 solution and being explicit with regards to the action? |
Beta Was this translation helpful? Give feedback.
-
@jimthedev still using this, I haven't seen any other solutions since opening this issue. If anyone has any new information it would be great to hear. |
Beta Was this translation helpful? Give feedback.
-
@LpmRaven Hi, could you point me to information or explain shortly your advice about focus managment please?
What would be a good focus target instead? I think you are using a heading inside the landmark, but are not sure about it? Why is a landmark a bad idea (edit: Maybe you mean the iOS bug, where dynamic content inside a focussed landmark is not re-read)? I tried out NVDA briefly, and focussing on a heading element seemed OK, but I am not a screen reader user.. |
Beta Was this translation helpful? Give feedback.
-
In the land of Ember we have an addon called Ember A11y, which on route change focuses the browser on a div that wraps the main content (There is more to it but that's the gist). Example: <div class="ember-view focusing-outlet" tabindex="-1" role="group">
<h1>About Page</h1>
...
</div> As I just started really digging in to React I am hoping to find or create a similar solution as it works rather nicely for the Ember apps I work on. |
Beta Was this translation helpful? Give feedback.
-
A Whether this exists as part of core React Router DOM is mostly up to @mjackson. Arguments for including it in RRD would be to extend its reach (:wink:). Arguments for having it in its own package would be that it isn't core functionality and that different packages could choose different implementations (render-invoked props to pass |
Beta Was this translation helpful? Give feedback.
-
My solution (simplified example): class App extends React.Component {
constructor(props) {
super(props)
this.sectionFocusEl = React.createRef()
}
componentDidUpdate(prevProps) {
// https://stackoverflow.com/a/44410281/358804
if (this.props.location.pathname !== prevProps.location.pathname) {
this.sectionFocusEl.focus()
}
}
render() {
return <div ref={this.sectionFocusEl}/>;
}
}
export default withRouter(App) |
Beta Was this translation helpful? Give feedback.
-
Here is a scenario we need to reset focus and currently don't have proper and generic solution to reset focus. |
Beta Was this translation helpful? Give feedback.
-
I'm very keen to include something like this in core @pshrmn. I've been focusing a lot of work on other areas, so I haven't had a bunch of time to really think about this yet. But I'm open to suggestions. Your Is there any consensus yet on what a good solution looks like? |
Beta Was this translation helpful? Give feedback.
-
That article is amazing. If there is a way for users to specify an element to target, with updated content as a fallback, that would be ideal. Now, when a screen reader user clicks on a link, nothing happens, which violates a fundamental design principle. reach/router is being slowly deprecated as seen in: So this needs to be fixed in react-router now. |
Beta Was this translation helpful? Give feedback.
-
@frastlin I think there needs to be consistency across websites as to where the focus moves on route change which is why this is important to be implemented by the router rather than custom code (which I was initially trying to do in this issue). I hope this will be fixed soon, it does mention this issue on in the features list: Whoever implements it should be referring to @marcysutton 's article (in my previous comment). If anyone finds anymore tested research on this issue I would appreciate hearing about it. |
Beta Was this translation helpful? Give feedback.
-
I've been hesitant to make any recommendations here since I don't want to cause more harm than good. I'm grateful for the pioneering work that was done in reach/router, but according to Marcy's article the approach it takes is just the beginning of a really comprehensive solution. It seems like the "best practice" for managing focus with a client-side router continues to evolve (the last update to the article was less than 4 months ago). From that article:
She also says:
This reminds me of the approach taken by @pshrmn in #6449. It's not an automatic solution, so people are free to disregard it entirely. But at least it provides a starting point. Maybe if we did have a |
Beta Was this translation helpful? Give feedback.
-
Well now we have nothing, so anything is better than what we have now, as long as it doesn't significantly break. |
Beta Was this translation helpful? Give feedback.
-
Here's a variation of @pshrmn's refocus component, using hooks and TypeScript: let prevPathName: string | null = null
const FocusOnRouteChange: React.FC = ({ children }) => {
const history = useHistory()
const ref = useRef<HTMLDivElement>(null)
history.listen(({ pathname }) => {
// don't refocus if only the query params/hash have changed
if (pathname !== prevPathName) {
ref.current?.focus()
// prevent jank if focusing causes page to scroll
window.scrollTo(0, 0)
prevPathName = pathname
}
})
return (
<div ref={ref} tabIndex={-1} style={{ outline: 'none' }}>
{children}
</div>
)
} |
Beta Was this translation helpful? Give feedback.
-
@lionel-rowe thanks for the hook, I had to wrap the history listen inside a useEffect hook. |
Beta Was this translation helpful? Give feedback.
-
It's a bummer that React Router still doesn't handle focus at all. Is that going to be addressed soon? Sending focus to a wrapper element or a heading would be better than doing nothing, even if a comprehensive skip link solution isn't in the cards right now. |
Beta Was this translation helpful? Give feedback.
-
@marcysutton from what I've just read online: https://reacttraining.com/blog/reach-react-router-future/ The article is from 2019 though, so I wouldn't get your hopes up. Maybe we can help with this? |
Beta Was this translation helpful? Give feedback.
-
That post is from May 2019, and it's almost 2021–hence my comment. Given @ryanflorence's past commitment to accessibility including Reach UI and Reach Router, I'd hope the React Training team in its new rendition could meet this requirement and not leave it to the community to handle. It otherwise doesn't send a great signal to the community that accessibility is being taken seriously at all. |
Beta Was this translation helpful? Give feedback.
-
For those looking for a solution until this ticket is closed, the article "Accessible page title in a single-page React application" by Kitty Giraudel describes a really good solution based on React Router and React Helmet. |
Beta Was this translation helpful? Give feedback.
-
This is what I ended up doing (inspired by Kitty's article). const ref = useRef<HTMLSpanElement>(null);
const { pathname } = useLocation();
// remove once react-router accessibility issue is fixed
// https://github.com/ReactTraining/react-router/issues/5210
useEffect(() => {
ref.current?.focus();
}, [pathname]);
return (
<>
<span ref={ref} tabIndex={-1} />
<a href="#navigation" className={className}>
Go to navigation
</a>
<a href="#main" className={className}>
Go to content
</a>
</>
)
|
Beta Was this translation helpful? Give feedback.
-
Looking to revive this and get some default focus management on the table. Lots of great progress has been made in this area since the issue was first opened, much of it from contributors to this thread, and more than enough for us to act. I think we can offer a reasonable default in both v5 and v6 in the coming weeks. I'll follow up here with a few proposals for those interested in offering feedback. |
Beta Was this translation helpful? Give feedback.
-
This thread came up in my search for a solution to the same issue with Angular.
|
Beta Was this translation helpful? Give feedback.
-
I'm going to convert this to a discussion so it can go through our new Open Development process. Please upvote the new Proposal if you'd like to see this considered! |
Beta Was this translation helpful? Give feedback.
-
Actually I think this is probably a dup of #9555, so go upvote that one instead! |
Beta Was this translation helpful? Give feedback.
-
I am using a screen reader to interact with my application.
Moving between routes does not reset the focus, which would occur on a server-side render. Is there a fix to reset the focus on route changes?
Superseded by #9555
Beta Was this translation helpful? Give feedback.
All reactions