滚动或选择项目时,MultiSelect框弹出框会持续跳转
Codepen https://codesandbox.io/s/material- demo-e5j8h
import React from "react"; import { makeStyles } from "@material-ui/core/styles"; import Input from "@material-ui/core/Input"; import InputLabel from "@material-ui/core/InputLabel"; import MenuItem from "@material-ui/core/MenuItem"; import FormControl from "@material-ui/core/FormControl"; import ListItemText from "@material-ui/core/ListItemText"; import Select from "@material-ui/core/Select"; import Checkbox from "@material-ui/core/Checkbox"; const useStyles = makeStyles(theme => ({ formControl: { margin: theme.spacing(1), minWidth: 120, maxWidth: 300 }, chips: { display: "flex", flexWrap: "wrap" }, chip: { margin: 2 }, noLabel: { marginTop: theme.spacing(3) } })); const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { PaperProps: { style: { maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, width: 250 } } }; const names = [ "Oliver Hansen", "Van Henry", "April Tucker", "Ralph Hubbard", "Omar Alexander", "Carlos Abbott", "Miriam Wagner", "Bradley Wilkerson", "Virginia Andrews", "Kelly Snyder" ]; export default function MultipleSelect() { const classes = useStyles(); const [personName, setPersonName] = React.useState([]); const handleChange = event => { setPersonName(event.target.value); }; return ( <div> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> <FormControl className={classes.formControl}> <InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel> <Select labelId="demo-mutiple-checkbox-label" id="demo-mutiple-checkbox" multiple value={personName} onChange={handleChange} input={<Input />} renderValue={selected => selected.join(", ")} MenuProps={MenuProps} > {names.map(name => ( <MenuItem key={name} value={name}> <Checkbox checked={personName.indexOf(name) > -1} /> <ListItemText primary={name} /> </MenuItem> ))} </Select> </FormControl> </div> ); }
跳跃的原因与的“内容锚定”功能有关Menu。
Menu
从https://material-ui.com/components/menus/#selected-menus(重点是我):
如果用于项目选择,则打开后,简单菜单会 尝试将当前所选菜单项 与锚元素 垂直对齐 ,并且初始焦点将放置在所选菜单项上。使用selected属性(来自ListItem)设置当前选择的菜单项。要使用选定的菜单项 而不影响 菜单 的初始焦点或垂直位置 , 请将variant属性设置为menu。
selected
variant
menu
您还可以在变体prop的文档中找到类似的注释。
文档的其他相关部分是Popover的getContentAnchorEl道具的描述:
调用此函数是为了检索内容锚点元素。与anchorEl道具相反。内容锚点元素应该是弹出框内的元素。它用于正确滚动和设置弹出框的位置。定位策略试图使内容锚点元素刚好位于锚点元素上方。
anchorEl
Select元素的默认行为是使用Select输入元素(关闭时显示选定项目的部分)作为“锚定元素”,最后选择的菜单项用作“内容锚定元素”。这意味着当Popover打开时,它将尝试将最后选择的菜单项(位于内Popover)与Select输入元素(位于后面Popover)对齐。
Select
Popover
如果使用上的multiple属性,则Select有可能在Popover打开时更改最后选择的项目(对于单选,通常会在选择某项后立即关闭)。此外,由于并非所有菜单项都适合一次,因此有时会将最后选择的项目滚动出视线,这迫使菜单项Popover使用略微不同的逻辑进行垂直对齐。
multiple
所有这些的最终结果就是沙箱中显示的怪异跳跃。您可以通过指定以下内容来强制Popover使用contentAnchorOffset为零来解决此问题getContentAnchorEl: null:
getContentAnchorEl: null
const MenuProps = { PaperProps: { style: { maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, width: 250 } }, getContentAnchorEl: null };
您可能还需要添加variant: "menu"以消除一些自动聚焦行为,这将导致它自动滚动到最后一个选定的项目。对于单选而言,这是很好的行为,但在多选中却不太有用,而且有些混乱。
variant: "menu"
设置variant: "menu"不足(没有getContentAnchorEl: null)来摆脱跳跃。这将导致它始终使用第一个菜单项作为内容锚,这将导致较少的跳转,但是由于更改选择时有时会滚动出第一个菜单项,因此它仍会进行一些跳转。
以下是修改后的沙箱版本的完整代码,该代码不再有任何奇怪的跳跃(唯一的变化是MenuProps):
MenuProps
import React from "react"; import { makeStyles } from "@material-ui/core/styles"; import Input from "@material-ui/core/Input"; import InputLabel from "@material-ui/core/InputLabel"; import MenuItem from "@material-ui/core/MenuItem"; import FormControl from "@material-ui/core/FormControl"; import ListItemText from "@material-ui/core/ListItemText"; import Select from "@material-ui/core/Select"; import Checkbox from "@material-ui/core/Checkbox"; const useStyles = makeStyles(theme => ({ formControl: { margin: theme.spacing(1), minWidth: 120, maxWidth: 300 }, chips: { display: "flex", flexWrap: "wrap" }, chip: { margin: 2 }, noLabel: { marginTop: theme.spacing(3) } })); const ITEM_HEIGHT = 48; const ITEM_PADDING_TOP = 8; const MenuProps = { PaperProps: { style: { maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, width: 250 } }, variant: "menu", getContentAnchorEl: null }; const names = [ "Oliver Hansen", "Van Henry", "April Tucker", "Ralph Hubbard", "Omar Alexander", "Carlos Abbott", "Miriam Wagner", "Bradley Wilkerson", "Virginia Andrews", "Kelly Snyder" ]; export default function MultipleSelect() { const classes = useStyles(); const [personName, setPersonName] = React.useState([]); const handleChange = event => { setPersonName(event.target.value); }; return ( <div> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> long text <br /> <FormControl className={classes.formControl}> <InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel> <Select labelId="demo-mutiple-checkbox-label" id="demo-mutiple-checkbox" multiple value={personName} onChange={handleChange} input={<Input />} renderValue={selected => selected.join(", ")} MenuProps={MenuProps} > {names.map(name => ( <MenuItem key={name} value={name}> <Checkbox checked={personName.indexOf(name) > -1} /> <ListItemText primary={name} /> </MenuItem> ))} </Select> </FormControl> </div> ); }