问题
I am attempting to create a search match list that updates as the user types in their query. However, I can't figure out how to maintain focus on the input element. The pop-up always gets focussed. I have tried programmatically setting the focus using refs but I cannot give a stateless function component (I'm assuming this is my TextField input) a ref.
Here is a gif of the behavior. https://imgur.com/a/JVskedr
Notice how the popup steals focus and prevents the user from typing further.
<TextField
id='contact'
label='Contact Name'
className={classes.textField}
margin='normal'
ref={this.nameInput}
onChange={this.handleContactSearch.bind(this)}
value={this.state.contactSearch}
/>
<Popover
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClick={this.handlePopoverClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center'
}}
autoFocus={false}
>
<List>{this.createContactList()}</List>
</Popover>
These are the related functions:
handleContactSearch(event) {
this.handlePopoverClick(event);
this.setState({ contactSearch: handleText(event) });
this.props.filterContacts(
event.target.value,
this.props.accountInfo.AccountName
);
}
handlePopoverClick = event => {
this.setState({
anchorEl: event.currentTarget
});
};
handlePopoverClose = () => {
this.setState({
anchorEl: null
});
};
How can I make the TextField element maintain focus so a user can type their query without interruption?
Sandbox: https://codesandbox.io/s/mjqoj9lxkj
回答1:
The reason why this is happening is that you are calling this.showPopover(event)
every time the onChange={this.handleContactSearch.bind(this)}
event is fired in your <TextField>
.
In order to fix this, you'll need to find a way to call this.showPopover(event)
only once.
I was able to make it work using a combination of autoFocus={true}
and the onFocus={this.showPopover}
event on the <TextField/>
. The only issue with this is that the popover will show up empty when you first open the modal. I used a ref
on the textfield and a conditional to set the opacity of the popover so it only shows once there's a value in the textfield.
Maybe not the ultimate solution, but it works and should at least send you in the right direction.
<div className={classes.paper}>
<TextField
id="contact123"
label="Contact Name"
className={classes.textField}
margin="normal"
onChange={this.handleContactSearch.bind(this)}
value={this.state.contactSearch}
autoFocus={true}
onFocus={this.showPopover}
inputRef={input => (this.tf = input)}
/>
<Popover
open={Boolean(anchorEl)}
anchorEl={document.getElementById("contact123")}
onClick={this.closePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
style={{ opacity: this.tf && !this.tf.value.length ? 0 : 1 }}
>
<List>{this.state.contactSearch}</List>
</Popover>
<div>
<Button color="primary" className={classes.saveButton}>
Save
</Button>
</div>
</div>
Sandbox: Working Demo
回答2:
Pass 'disableAutoFocus', 'disableEnforceFocus' props to your popover. It worked for me!
https://material-ui.com/api/modal/
回答3:
An alternative to this approach is to use Popper
, ClickAwayListener
and Backdrop
components. Using Popper
allows you to preserve focus on the input field and keep typing. The solution would look roughly like:
class Foo extends React.Component {
inputRef = React.createRef(),
render() {
const { open, searchValue } = this.state
<RootRef rootRef={this.inputRef}>
<div className={classes.container}>
// You may be able to use TextField as well
<FormControl
onKeyDown={//set open = false}
onClick={// set open = true (e.g. only when searchValue !== '' }
>
<InputBase
value={searchValue}
onChange={this.handleSearchValueChange}
inputRef={this.inputRef}
/>
</FormControl>
<Popper anchorEl={this.inputRef.current} open={open} >
<ClickAwayListener onClick={//set open = false} onClickAway={//set open = false}>
Popover content
</ClickAwayListener>
</Popper>
</div>
</RootRef>
}
}
Not a working example, but shows how to solve the problem of being able to type in an input while having popover/popper opened.
回答4:
You can add onKeyDown property to the popover that will close it every time the user will type, and onKeyUp property to the search input which pops up the popover again. not the best solution but it's worked for me.
<InputBase
autoFocus={true}
value={searchText}
onChange={(e) => handleSearch(e)}
onKeyUp={e => setAnchorEl(e.currentTarget)}
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ "aria-label": "search" }}
/>
{searchResult && (
<Popover
disableAutoFocus
className={classes.pop}
onKeyDown={handleClose}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
{searchResult.map((song, i) => {
return (
<Link to={`/Songs/${song.unique_id}?Artist=${song.artist_id}`}>
<ListItem>
<IconButton variant="h6" >
<PlayCircleFilledRounded/>
</IconButton>
{song.title}
</ListItem>
</Link>
);
})}
</Popover>
)}
</div>
来源:https://stackoverflow.com/questions/51388205/prevent-auto-focus-of-a-material-ui-popover-element