滚动或选择项目时,MultiSelect框弹出框保持跳跃 [英] MultiSelect box popover keeps jumping when scroll or select items

查看:43
本文介绍了滚动或选择项目时,MultiSelect框弹出框保持跳跃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在滚动或选择项目时,MultiSelect框弹出窗口会不断跳转

Codepen

MultiSelect box popover keeps jumping when scroll or select items

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>
  );
}

解决方案

The cause of the jumping is related to the "content anchor" functionality of the Menu.

From https://material-ui.com/components/menus/#selected-menus (emphasis mine):

If used for item selection, when opened, simple menus attempt to vertically align the currently selected menu item with the anchor element, and the initial focus will be placed on the selected menu item. The currently selected menu item is set using the selected property (from ListItem). To use a selected menu item without impacting the initial focus or the vertical positioning of the menu, set the variant property to menu.

You can also find a similar note in the documentation of the variant prop.

The other relevant portion of the documentation is the description of the getContentAnchorEl prop of Popover:

This function is called in order to retrieve the content anchor element. It's the opposite of the anchorEl prop. The content anchor element should be an element inside the popover. It's used to correctly scroll and set the position of the popover. The positioning strategy tries to make the content anchor element just above the anchor element.

The default behavior of the Select element is to use the Select input element (the part that shows the selected item(s) when closed) as the "anchor element" and the last selected menu item as the "content anchor element". This means that when the Popover is open, it tries to align the last selected menu item (within the Popover) with the Select input element (behind the Popover).

In the case of using the multiple property on the Select, you have the potential to change the last selected item while the Popover is open (for single select, it would typically close immediately after selecting something). Additionally, since not all the menu items fit at once, the last selected item is sometimes scrolled out of view which forces the Popover to use slightly different logic for the vertical alignment.

The net effect of all this is the weird jumping demonstrated in your sandbox. You can fix this by forcing Popover to use a contentAnchorOffset of zero by specifying getContentAnchorEl: null as follows:

const MenuProps = {
  PaperProps: {
    style: {
      maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
      width: 250
    }
  },
  getContentAnchorEl: null
};

You may also want to add variant: "menu" to get rid of some auto focus behavior which will cause it to automatically scroll to the last selected item. This is nice behavior for single select, but less useful and somewhat disorienting in a multi-select.

Setting variant: "menu" is not sufficient (without getContentAnchorEl: null) to get rid of the jumping. This would cause it to always use the first menu item as the content anchor which would result in less jumping, but it would still do some jumping due to the first menu item sometimes being scrolled out of view when changing selections.

Below is the full code for the modified version of your sandbox that no longer has any weird jumping (the only changes are to the 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>
  );
}

这篇关于滚动或选择项目时,MultiSelect框弹出框保持跳跃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆