Using the keyboard to choose a select field input causes the page focus to be reset to body. #5921 has
dropNode.focus();
dropArrow.setKeyboardFocus(true);
, but something is calling blur directly afterwards. Wrapping the code that grabs focus in a setTimeout makes it work, but is a pretty rough workaround.
Happens on http://www.material-ui.com/#/components/select-field
Tab over to the first select field, then [enter] [down] [enter]. Focus (verified with document.activeElement) is now on the body tag.
Tried making something on webpackbin, but tabbing doesn't work at all.
I've verified that the behavior is the same for me on:
I'm struggling with this too. However, setting focus in setTimeout isn't working for me.
@gji, what element are you setting focus on?
I'm using this as an editable cell in ag-grid and setting focus on the <button> element works with tabbing, but if I just set focus on <button> it doesn't work. There's likely some extra stuff happening when I tab, but I haven't dug through the material-ui source to figure all that out yet.
To further explain the use case. ag-grid expects a component and will invoke focusIn when tabbing while editing. So I just need to set focus on the proper element when focusIn is invoked. This works by setting focus on <button> however the flow is messed up when the menu is closed because the focus is now on the body and I have to tab like a mad man or use my mouse to select the next cell. I've tried setting focus in the onChange handler with and without the help of setTimeout and neither work.
Also just to note I'm just using basic DOM call to set focus.
e.g. document.querySelector('button').focus()
Edit: The setTimeout does actually set the focus on the button, but I get no focus animations to indicate where focus is. So technically it does work, but the indication of what is focused is lost.
Edit: The setTimeout only seem to work if the timeout is longer than 500ms which makes me think that there might be something listening at a 500ms timeout which causes the body focus.
I've verified that the behavior is the same for me on:
Material-UI: 0.17.0
React: 15.4.1
Also while I'm poking around here; what's the difference between a Select Field and a Drop Down Menu?
@ralphsmith80 SelectField is a form component.
@ralphsmith80, there's actually a string of events fired off by the dropdown box that reset focus to inside the dropdown after the dropdown is closed. Those events are called because the dropdown box has to be persisted for the animation. Disabling the animation on the DropDown component inside the SelectField (requires editing source) seems to fix this bug in its entirety.
What's strange to me is that SelectField has this problem, but creating a bare DropDown (which SelectField uses) does not, which you can test on the material-ui example website as well. I would expect the same problem regarding the animation causing focus to revert.
What's strange to me is that SelectField has this problem, but creating a bare DropDown (which SelectField uses) does not,
That's exactly why I was curious about the difference between the two.
@mbrookes, it looks like they're both using a <button> under the covers for focus and I'm not seeing any mention of 'form components' in the docs. Am I missing something or are you just referring to the semantics of a SelectField and a DropDownMenu?
Hi, I'm the original author of this change. I did actually came up with a work-around for this and not sure why it didn't make it into master. In DropDownMenu, change to look something like the following:
{this.state.open && <Menu maxHeight={maxHeight} desktop={true} ...
It happens in a SelectField specifically because it has a TextField internally, and DropDownMenu on its own does not. There's a focus event / re-render propagated by the TextField that cause issues.
@oliviertassinari, I can see this change i mentioned above is in my branch (and was when you merged it), how did you end up squashing / adding that pull to master? The commit in that pull that fixes this issue is https://github.com/callemall/material-ui/pull/4966/commits/54fea8875d769916c3dfd51e12090e23d374d86d
@caesay I wasn't sure about that change so I removed it. How is that even fixing the issue? Should we have some explicit logic about it? Also, I'm wondering. Isn't that change removing the show/hide animation? I haven't looked in detail. It would be good to take care of that issue.
@oliviertassinari It removes the closing animation of the menu, yes. I don't entirely remember why that fixes the issue - it's been a while. IIRC the problem occurs when the inner TextField updates it's state in response to the blur/focus events. If you disable the focus/blur event handlers in the TextField this loss of focus bug goes away. I think because of something in the TextField the focus is getting set back to the Menu while its in the process of closing, and then when it finishes closing the focus gets set to <body>. If the menu is removed from the dom immediately then the focus correctly goes back to the arrow so you can tab to the next item.
There is probably a better fix to this problem, but I spent several days of work on this specific bug and wasn't able to come up with anything better.
Hi friends,
my workaround was implement my own SelectField... I had only copied few things from the original implementation..
I am using MobX to manage the state...
import React from 'react';
import ReactDOM from 'react-dom';
import TextField from 'material-ui/TextField';
import SelectField from 'material-ui/SelectField';
import DropDownMenu from 'material-ui/DropDownMenu';
import LinearProgress from 'material-ui/LinearProgress';
import { observer } from 'mobx-react';
@observer
export class SelectFieldX extends React.Component {
constructor(props){
super(props);
this.mobxFormField = props.mobxForm.getField(props.name);
}
onChange = (e, i, v) => {
e.preventDefault();
this.mobxFormField.value = v || '';
}
onBlur = (e) => {
e.preventDefault();
const {mobxFormField} = this;
mobxFormField.timer = _.delay(()=>{
mobxFormField.errorText = '';
mobxFormField.loading = true;
mobxFormField.formatField(mobxFormField.value)
// .then((formatted)=>{
// mobxFormField.value = formatted;
// return mobxFormField.validateField();
// })
mobxFormField.validateField()
.then(()=>{
//mobxFormField.errorText = '';
mobxFormField.loading = false;
})
.catch((err)=>{
mobxFormField.errorText = err.message;
mobxFormField.loading = false;
});
},200);
}
onRef = (obj) => {
obj && (this.mobxFormField.element = obj);
//this.textfield = obj;
}
render() {
const {mobxFormField} = this;
return (
<div>
<TextField
floatingLabelFixed
fullWidth
floatingLabelText={mobxFormField.label}
errorText={mobxFormField.errorText}
hintText={mobxFormField.hintText}
onBlur={this.onBlur}
ref={this.onRef}
>
<DropDownMenu
style={{
display: 'block'
}}
labelStyle={{
paddingLeft: 0,
top: 6
}}
underlineStyle={{
borderTop: 'none'
}}
iconStyle={{
right: 0,
top: 8
}}
onChange={this.onChange}
value={mobxFormField.value}
>
{this.props.children}
</DropDownMenu>
</TextField>
{mobxFormField.loading ? (
<LinearProgress mode="indeterminate" />
):(
<div></div>
)}
</div>
)
}
}
I am also seeing this issue in a very simple form. After making a selection with the keyboard my next "Tab" dose not take you to the next form element.
We are using
I've also experienced the issue as described.
I'd like to add that clicking on the option using the mouse also causes the page focus to be reset to body (verified with document.activeElement on http://www.material-ui.com/#/components/select-field)
There was a work-around for this issue in the original PR I sent (see above). I have not looked into it again since I have abandoned <SelectField /> and written my own input that uses the browser <select /> element. It's much quicker and better for large forms requiring quick user input and proper keyboard support. I can share if there is any interest, but the visuals of it are not great as we can't style the drop down portion of it yet due to lacking browser support.
hey @caesay I'm interested if you're willing to share
@BernardoFBBraga here: https://gist.github.com/caesay/131f4c5a985080151e9adfe94d8dbda1
It will probably need some tweaking:
It looks great though:


Works exactly the same as a native <select /> so expect keyboard navigation and type-to-select features to work perfectly.
you can use SelectField and add dropDownMenuProps to turn off animation and focus will work!
<SelectField dropDownMenuProps={{animated: false}} ... >
thanks @jannakha, that fixed the focus issue. But then I get error messages in the console and the value doesn't change until I move the mouse. Seems related to #6260.
Most helpful comment
you can use SelectField and add dropDownMenuProps to turn off animation and focus will work!
<SelectField dropDownMenuProps={{animated: false}} ... >