reach / router

https://reach.tech/router
MIT License
6.9k stars 326 forks source link

Navigating blurs inputs with `autoFocus` #227

Open michaelwiktorek opened 5 years ago

michaelwiktorek commented 5 years ago

This is related to #158 - navigating to a page with an autofocused input still causes the input to be focused and blurred immediately on navigation. This breaks things like wizard-style form navigation where input validation fires on input blur - the validation improperly fires before a user ever focuses the input.

Here is a sandbox (modified from #158) that provides a minimal demonstration with v1.2.1: https://codesandbox.io/s/3yppojk23q

I made a fork of the router and used that in my project for the time being. The fix that worked for me was changing requestFocus to match the change in a7a1c84e7f518611d0de2fe4606e17d453fd51eb

requestFocus = node => { if (!this.state.shouldFocus && !node.contains(document.activeElement)) { node.focus(); } };

adjourn commented 5 years ago

Workaround for now (if using hooks):

useLayoutEffect(() => {
  elementToFocusRef.current.focus();
}, []);

Don't use useEffect because it will fire after router focus and you'll get two of them. useLayoutEffect fires synchronously right after DOM mutations and prevents router focus.

This can also be used if you need to focus other elements with tabindex, e.g if using custom scrollbar and scrollable content is deeper in hierarchy, wouldn't scroll with arrow keys if router div is focused.

Attribute autofocus is only supported on 4 HTML elements as far as I know.

mnieber commented 5 years ago

I fixed a similar issue by using <Router primary={false}> so that the Router doesn't interfere with focus at all.

However, I do see a problem with this solution: if "primary" is a concept that involves more than focus, then maybe I want my Router to primary. It would be good if the docs explain what "primary" really means. Possibly, it would be an improvement to use a separate "manageFocus" attribute (where manageFocus=false is implied if it's not the primary router).

liqwid commented 4 years ago

It would be good if the docs explain what "primary" really means.

One important thing it does is setting height: 100%. Would be great to have it separate from focus manipulation.

Easy fix is just creating a wrapper since Router passes classNames down to the rendered div.

ShannonLCapper commented 4 years ago

We're experiencing this same issue, specifically when using nested routes. In our case, we want to update the URL to reflect a string that a user types into a text box. This navigation doesn't result in a different page being rendered, but if the page is inside a nested route, focus gets stolen from the text box. If the page is NOT in a nested route, everything works fine

Here's a minimal example: https://codesandbox.io/s/stoic-darkness-s3ejs