首页 > 解决方案 > 可变宽度的弹性项目之间的等距分隔符

问题描述

我需要设计一个由 CMS 动态呈现的导航(所以我无法控制 HTML 标记)。菜单有一个未知的、可变数量的项目,并且所有项目都有一个可变的、未知的宽度。我需要将这些菜单项布置​​在一行中,它们之间的间距是等距的(很容易用 flexbox 完成),并且在间距的中间有一个视觉分隔符(这是我未解决的问题)。

下面是我到目前为止的代码。HTML 只是一个示例输出,我无法修改它。我也不允许使用 JS。CSS来自我自己,我可以完全控制它。

.mainnav {
  width: 100%;
  display: flex;
  justify-content: space-between;
  list-style-type: none;
  margin: 0;
  padding: 0;
  border: 1px solid black;
}

.mainnav__item {
  flex: 0 0 auto;
  background-color: yellow;
  position: relative;
  white-space: nowrap;
}

.mainnav__item + .mainnav__item::before {
  content: '';
  width: 1px;
  height: 100%;
  background-color: red;
  position: absolute;
  left: -50%;
  top: 0;
}
<ul class="mainnav">
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Short</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Looooooooooooooong</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Mediuuum</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">whatever else</a>
  </li>
</ul>

我的问题是红色分隔线。它应该正好在中间,但我不知道如何计算水平位置。

谁能告诉我分隔符放置的唯一 CSS 解决方案?

该解决方案应该适用于常见的最新浏览器(当前版本的 [Android|Win] Chrome、[macOs|iOs] Safari、Firefox、Edge),但也适用于 IE11

标签: cssflexbox

解决方案


一种想法是在元素中考虑display:contents;1li并保持伪元素流入(删除position:absolute)。这将使伪元素和a成为弹性项目而不是li元素,因此space-between将完成这项工作。

只需注意浏览器支持,因为它是一项新功能。

.mainnav {
  width: 100%;
  display: flex;
  justify-content: space-between;
  list-style-type: none;
  margin: 0;
  padding: 0;
  border: 1px solid black;
}

.mainnav__item {
  flex: 0 0 auto;
  position: relative;
  white-space: nowrap;
  display:contents;
}
.mainnav__item a {
  background:yellow;
}

.mainnav__item + .mainnav__item::before {
  content: '';
  width: 1px;
  display:block;
  background-color: red;
}
<ul class="mainnav">
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Short</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Looooooooooooooong</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Mediuuum</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">whatever else</a>
  </li>
</ul>

1元素本身不会生成任何 box,但它的子元素和伪元素仍然会生成 box 并且文本会正常运行。出于框生成和布局的目的,该元素必须被视为 已在元素树中被其内容替换(包括其源文档项及其伪元素,例如 ::before 和 ::after伪元素,通常在元素的子元素之前/之后生成)。参考

另一个想法是调整flex-grow和使用边框而不是依赖space-between

.mainnav {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
  border: 1px solid black;
}

.mainnav__item {
  flex: 0 0 auto;
  position: relative;
  white-space: nowrap;
  flex-grow:2; /*middle elements need to grow twice that edge element*/
  text-align:center;
  border-left:1px solid red;
  border-right:1px solid red;
}
.mainnav__item a {  
background:yellow;
}
.mainnav__item:first-child {
  flex-grow:1;
  text-align:left;
  border-left:none;
}
.mainnav__item:last-child { 
  flex-grow:1;
  text-align:right;
  border-right:none;
}
<ul class="mainnav">
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Short</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Looooooooooooooong</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">Mediuuum</a>
  </li>
  <li class="mainnav__item">
    <a class="mainnav__link" href="#">whatever else</a>
  </li>
</ul>


推荐阅读