-
Notifications
You must be signed in to change notification settings - Fork 46.4k
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
Add api for focus management #1791
Comments
Just a thought. Have you thought about using |
@volkanunsal |
What I am suggesting is making |
@volkanunsal Delaying focus() until the next requestAnimationFrame/setTimeout is probably your safest bet. It seems that even though elements have finished rendering and are attached to the DOM, that is not a guarantee for focus() succeeding. Personally I wouldn't mind adding a focus() method on the base DOM-component that could incorporate this "fix", but it's tricky decision I think, since there are a lot of other features that could be considered equally necessary, but doesn't really have to be supported by React (you can just have your own external focus-helper). It makes sense to only keep the required features part of React in the core and anything that is optional out of the core. |
@volkanunsal took a bit to grok what was being said. If we do add properties like
Activation works fine to well depending on if you want to wrap all the React.DOM.* components. If we flatten all the components we get into some odd cases for delegation:
Ignoring works fine to well as long as your data layer can filter out when components would not be valid targets for focus. This means we will need to use the result of React.renderComponent to fire methods based upon current |
👍 Would love to see this land. Either implementation would be fine for me. Using an |
👍 I would also love to see a built-in support for this focus issue. My current workaround is this (in CoffeeScript): render: ->
# other code...
if @state.captchaIsLoading == false
setTimeout( => @refs.captchaAnswerField.getDOMNode().focus())
# other code ... While the above solution solves the problem, it leaves me with a feeling that I have a hack in an otherwise elegant React component. (All in all, thanks for React. It is such a wonderful tool. I love to work with it!) |
also 👍 this, My use case is trying to maintain focus on a single input while you interact with its child elements (such as the the dropdown in a selct widget). I have given up trying to manage focus with state, it has only led to confusing bugs in browsers, and really hard to understand order of execution. I ended up using a timeout in each widget, which is triggered (among other places) onBlur/onFocus. The timeout also helps manage when focus moves within a parent component. I also need a I am not sure on the best implementation, but for me the issue is not setting initial focus, but maintaining it on the correct input, the |
I work pretty hard to create accessible UI and have found React to be a breath of fresh air for focus management. I haven't seen any discussion around using the this.setState(newStuff, () => {
this.refs.email.getDOMNode().focus(); // will be rendered
}); Here's a complex one taken straight out of an app I'm working on: this.setState({ gotEmail: true }, () => {
this.refs.thanks.getDOMNode().focus();
// setTimeout has nothing to do with focus, just wanting to display a "thank you"
// for two seconds
setTimeout(() => {
var focusHasNotMoved =
this.refs.thanks.getDOMNode() === document.activeElement;
this.setState({ gotEmail: false }, () => {
if (focusHasNotMoved)
this.getDOMNode().focus();
});
}, 2000);
}); |
Don't use render, that's for rendering, use the #render: ->
componentDidMount: ->
# other code...
if @state.captchaIsLoading == false
@refs.captchaAnswerField.getDOMNode().focus()
# other code ... But probably you should do it wherever you @setState(captchaIsLoading: no, () => @refs.captcha.getDOMNode().focus()) You only want to focus the captcha the first time the state changes, not every time any data on the view changes and causes a render. |
I never address the OP. All of these scenarios are manageable with the lifecycle hooks provided by React.
do this in the
Same as the last notes, if you use the right hooks, render is guaranteed. If focus is lost by
<ul style="display:none">
<li><button>can't focus</button></li>
</ul>
When are you trying to focus and how does the |
For anyone stumbling around the internet trying to find a fix, @volkanunsal was right on the money. I had a similar situation where the focus() call wasn't hitting the element because it wasn't completely visible when componentDidUpdate ran. As suggested, delaying the focus() with a requestAnimationFrame (or setTimeout) fixed the issue. Although, setting the callback this way
|
There are currently a couple of problems with focus management in React.
current problems
this.getDOMElement().focus()
does not guarantee the node is on a document/visible. visibility can be hard to detect due to other components firing render().
this.refs.x.getDOMElement().focus()
does not guarantee that x has finished any pending renders. if
x
renders, focus is lost.componentDidUpdate
this fires on the child nodes before parents so in the case of:
if we want to show the
<ul/>
and focus the button.the component of the
<button/>
focusing during componentDidUpdate has no affect because the<ul/>
is still hidden.discussion
if looks like some of the PRs to react are using raf or timeouts to achieve fixes to the problems listed. This can cause race conditions, and can be fixed with a lifeCycle addition, or just a hidden lifeCycle purely for focus management.
The issue comes down to not having a lifecycle able to fire a function after all rendering is done, not just an individual component.
I would suggest we add a simple API of component.blur()/component.focus() that queues the requests and fires them after all rendering is done. The fix is fairly simple, but I wonder how people feel about this.
The text was updated successfully, but these errors were encountered: