Автоматически изменять размер выпадающего списка css, чтобы он соответствовал самому большому элементу

Я хотел бы сделать раскрывающийся список для выбора из списка элементов, которые всегда имеют ширину самого широкого элемента. Выбранный элемент — это элемент, который всегда отображается, а другие параметры отображаются при наведении.

Это простой раскрывающийся список CSS, и в идеале я хотел бы видеть чистое решение CSS. Поскольку я использую React, а этот раскрывающийся список является компонентом, решение js было бы приемлемым (без использования jQuery или других библиотек, если это возможно).

const Dropdown = (props) => (
	<div className = "dropdown">
	  <div className = "dropdown-item">{props.active}</div>
    <div className = "dropdown-body">
      {props.items
        .filter(x => x !== props.active)
        .map(x => <div className = "dropdown-item">{x}</div>)}
    </div>
	</div>
)

var items = [
	"abc", "abcdcdssd", "a"
]

ReactDOM.render(
	<div>Hello <Dropdown items = {items} active = {"abc"} /> world.</div>,
  document.querySelector("#app")
)
.dropdown {
  display: inline-block;
  background-color: blue;
  text-align: center;
}

.dropdown .dropdown-item {
  background-color: red;
  width: auto;
  padding: 0 6px;
}

.dropdown .dropdown-body {
  display: none;
  position: absolute;
  z-index: 1000;
}

.dropdown:hover .dropdown-body {
  display: block;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id = "app"></div>

В приведенном выше примере я хотел бы, чтобы элемент "abc" имел ширину самого большого элемента "abcdcdssd".

Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
CSS: FlexBox
CSS: FlexBox
Ранее разработчики использовали макеты с помощью Position и Float. После появления flexbox сценарий полностью изменился.
0
0
301
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чистое CSS-решение

Чистым решением css было бы присвоить вашему контейнеру width: auto и использовать стиль visibility вместо display для ваших элементов, чтобы скрыть их:

const Dropdown = (props) => (
	<div className = "dropdown">
	  <div className = "dropdown-item">{props.active}</div>
    <div className = "dropdown-body">
      {props.items
        .filter(x => x !== props.active)
        .map(x => <div className = "dropdown-item">{x}</div>)}
    </div>
	</div>
)

var items = [
	"abc", "abcdcdssd", "a"
]

ReactDOM.render(
	<div>Hello <Dropdown items = {items} active = {"abc"} /> world.</div>,
  document.querySelector("#app")
)

var items = [
	"abc", "abcdcdssd", "a"
]

ReactDOM.render(
	<div>Hello <Dropdown items = {items} active = {"abc"} /> world.</div>,
  document.querySelector("#app")
)
.dropdown {
  display: inline-block;
  text-align: center;
  vertical-align: top;
  width: auto;
}

.dropdown .dropdown-item {
  background-color: red;
  padding: 0 6px;
}

.dropdown .dropdown-body {
  visibility: hidden;
  z-index: 1000;
}

.dropdown:hover .dropdown-body {
  visibility: visible;
}
<script crossorigin src = "https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src = "https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id = "app"></div>

Предупреждение заключается в том, что, поскольку элементы скрыты, только если вы выберете текст, ваши скрытые элементы также будут выбраны.

JS-решение

Вы можете измерить ширину раскрывающегося списка после первоначального рендеринга и применить его к активному элементу раскрывающегося списка. Чтобы это работало, вам изначально нужно использовать visibility: hidden вместо display: none, потому что можно измерить только ширину скрытых элементов. Вы можете скрыть их, как только они будут измерены изначально.

В приведенном ниже примере используются реагирующие хуки, для которых требуется версия >16.8, но это также может быть достигнуто с помощью компонента на основе класса, использующего componentDidMount.

const {useRef, useState, useLayoutEffect} = React;

const Dropdown = props => {
    const bodyRef = useRef();
    const [bodyWidth, setWidth] = useState(null);
    const [itemsHidden, setItemsHidden] = useState(false);
    useLayoutEffect(() => {
        setWidth(bodyRef.current.clientWidth);
        setItemsHidden(true);
    }, []);

    return (
        <div style = {{width: bodyWidth || 'auto'}} className = "dropdown">
            <div className = "dropdown-item">{props.active}</div>
            <div ref = {bodyRef} className = {`dropdown-body${itemsHidden ? ' hidden' : ''}`}>
                {props.items
                    .filter(x => x !== props.active)
                    .map((x, idx) => (
                        <div key = {idx} className = "dropdown-item">{x}</div>
                    ))}
            </div>
        </div>
    );
};

var items = [
	"abc", "abcdcdssd", "a"
]

ReactDOM.render(
	<div>Hello <Dropdown items = {items} active = {"abc"} /> world.</div>,
  document.querySelector("#app")
)
.dropdown {
    display: inline-block;
    background-color: blue;
    text-align: center;
}

.dropdown .dropdown-item {
    background-color: red;
    width: auto;
    padding: 0 6px;
}

.dropdown .dropdown-body {
    position: absolute;
    z-index: 1000;
}

.dropdown .dropdown-body.hidden {
  display: none;
  position: absolute;
  z-index: 1000;
}

.dropdown:hover .dropdown-body {
    display: block;
}
<script crossorigin src = "https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src = "https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id = "app"></div>

Я пытаюсь использовать реагирующее решение, но я не уверен, как должны работать setWidth или setItemsHidden. Они нигде не определены.

Maroš Beťko 28.05.2019 11:19

@MarošBeťko Их возвращают хуки useState. Вам нужно react>=16.8 использовать хуки.

trixn 28.05.2019 11:21

О, теперь я вижу, что вы используете 2 состояния в основном, одно для ширины и одно для скрытых элементов. Я внедрил ваше решение в свой проект, но по какой-то причине первый элемент не занимает ширину самого широкого элемента списка.

Maroš Beťko 28.05.2019 11:26

@MarošBeťko Если вы поделитесь своим кодом в codeandbox, я могу взглянуть на него. Возможно, вы просто что-то упустили из моего ответа. Дважды проверьте, что у вас одинаковый код и css.

trixn 28.05.2019 11:28

Когда вызывается обратный вызов useLayoutEffect, ширина клиента равна 0. В приложении, когда это раскрывающееся меню впервые добавляется, оно не отображается и скрыто под элементом display: none.

Maroš Beťko 28.05.2019 11:50

@MarošBeťko Хорошо, если это 0, это означает, что у элемента body нет содержимого. В обязательном порядке необходимо изначально визуализировать отображаемые элементы, чтобы можно было измерить их ширину. Вот почему я только скрыл их, применив класс hidden css после измерения. Также при динамическом обновлении элементов вам придется снова применять эффект макета. В моем примере он будет запущен только один раз после монтирования компонента.

trixn 28.05.2019 13:09

а что, если выпадающее меню нужно сначала скрыть? как я могу обновить ширину после того, как она станет видимой?

Maroš Beťko 28.05.2019 13:27

@MarošBeťko Я обновлю свой ответ более сложным примером, который может обрабатывать обновления и крайние случаи, как только у меня будет на это время. Наверное, вечером.

trixn 28.05.2019 13:34

Другие вопросы по теме