首页 > 解决方案 > 创建自定义下拉菜单,为什么子元素的宽度比其父元素宽?

问题描述

我正在尝试为 React 构建一个自定义下拉组件。不幸的是,我设置为 100% 的子列表的宽度显示为比其父元素更宽。

DOM:

<div className="dd-wrapper" id={this.state.wrapperID}>
    <div className="dd-header" id={this.state.headerID} onClick={() => this.toggleList()}>
        <div className="dd-header-title"></div>
    </div>
    <ul className="dd-list hidden" id={this.state.listID}>
        <li className="dd-list-item">Test</li>
        <li className="dd-list-item">Test2</li>
        <li className="dd-list-item">Test3</li>
    </ul>
</div>

toggleList函数从列表中删除hidden该类,并向包装器添加一个边框属性类dd-border,该包装器的行为也不正常(边框仅包装标题)。

它在这样的父元素中被调用(我正在使用 Bootstrap 网格):

<div className="col-md-3">
    <label> Dropdown
        <Dropdown id="test-dd"/>
    </label>
</div>

这是相关的CSS:

.dd-wrapper {
  width: 100%;
  margin-top: 8px;
}

.dd-header {
  height: 40px;
  background-color: #E2E8F2;
  background-image: url("assets/images/down-chevron.png");
  background-repeat: no-repeat;
  background-position: 95% 50%;
}

.dd-list {
  list-style-type: none;
  background-color: white;
  position: absolute;
}

.dd-list li {
  height: 40px;
}

.dd-border {
  border: 1px solid #3d70b2;
}

label {
  display: inline-block;
  width: 100%;
  font-size: 14px;
}

.hidden {
  display: none;
}

结果如下所示:

在此处输入图像描述

如何在不手动执行的情况下获得宽度以匹配其父级(以确保它适用于任何大小的下拉菜单?其次,有没有人知道让边框也覆盖孩子的好技巧?

标签: css

解决方案


快速说明:这是一个 CSS 问题,因此您的 React 逻辑只是阻碍任何愿意回答的人。我已根据您的描述将重要部分提取到下面没有 React 的工作片段中。我建议删除对您的问题背景不重要的任何内容,以鼓励更及时的回答。

如何在不手动操作的情况下获得与其父级匹配的宽度(以确保它适用于任何大小的下拉菜单?

我认为您要寻找的主要是position: relative父母。因为绝对定位的元素会根据第一个“定位”的祖先来调整和定位自己。因此,您可以将其与top, left, right, bottom,width和/或height值(并且可能box-sizing: border-box;)结合起来

...有谁知道让边界也覆盖孩子的好技巧?

您可以通过将下拉列表直接放在底部并切换共同父级上的类来隐藏/显示某些边框来伪造它。

这是要演示的代码段:

// The toggle logic in vanilla JS just to make the example work
// This, instead of toggling "hidden" on the list, toggles a "dd-closed" class on the wrapper
const wrapperEl = document.querySelector('.js-wrapper')
const headerEl = document.querySelector('.js-header')
if (wrapperEl && headerEl) {
  const ancestorLabel = headerEl.closest('label')
  const targetEl = ancestorLabel ? ancestorLabel : headerEl
  targetEl.addEventListener('click',
    () => wrapperEl.classList.toggle('dd-closed')
  )
}
.dd-wrapper {
  position: relative;
  width: 100%;
  margin-top: 8px;
}

.dd-header {
  position: relative;
  height: 40px;
  background-color: #E2E8F2;
  background-repeat: no-repeat;
  background-position: 95% 50%;
  border: 1px solid #3d70b2;
  border-bottom-width: 0;
  padding-right: 3em;
}

.dd-header::before {
  position: absolute;
  content: '\25B4';
  right: 0;
  text-align: center;
  line-height: 40px;
  width: 1em;
  top: 0;
  bottom: 0;
  font-size: 2em;
}

.dd-list {
  list-style-type: none;
  background-color: white;
  position: absolute;
  border: 1px solid #3d70b2;
  margin-top: 0;
  left: 0; right: 0;
  padding: 0;
}

.dd-list li {
  vertical-align: middle;
  padding: 1em;
}

.dd-list li:hover {
  background-color: #eee;
}

label {
  display: inline-block;
  width: 100%;
  font-size: 14px;
}

.dd-closed > .dd-header::before {
  content: '\25BE';
  float: right;
}
.dd-closed > .dd-header {
  border-bottom-width: 1px;
}
.dd-closed > .dd-list {
  display: none;
}
<!-- Basically what React would render as your output HTML...plus any necessary changes -->
<label> Dropdown
  <div class="dd-wrapper dd-closed js-wrapper">
    <div class="dd-header js-header">
      <div class="dd-header-title"></div>
    </div>
    <ul class="dd-list">
      <li class="dd-list-item">Test</li>
      <li class="dd-list-item">Test2</li>
      <li class="dd-list-item">Test3</li>
    </ul>
  </div>
</label>
<p>(Some other content for the dropdown to cover)</p>
<button>(I do nothing)</button>


推荐阅读