本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

在弹性行包装容器中悬停时展开 div 会导致子元素移动

发布于2023-03-24 16:17     阅读(884)     评论(0)     点赞(14)     收藏(3)


我在编写一些 css/html 元素时遇到了这个问题。一直在网上搜索潜在的解决方案,但似乎没有一个能解决我的问题。

我有一个动态数量的子元素需要适合显示的父元素(弹性容器)。子元素有点像徽章,您可以在其中显示信息。悬停时,子元素应展开宽度过渡并显示一个实用程序图标供我单击。到目前为止一切都很好。问题是悬停时,因为孩子会展开,如果它是 flex 中行的“最后一个”孩子,它将移动到下一行并完全展开,我无法再到达图标。

有什么解决办法吗?我尝试对孩子使用负边距以欺骗它停留在它所在的同一行,但是孩子本身在溢出时被父容器切断了整个身体(它看起来也不太好)。

我还尝试过强制每行只包含 n 个子元素(例如每行 2 个子元素),但这只会减少问题发生的可能性,并不能有效地消除它。

我希望寻找的是保持宽度过渡并允许动态数量的子元素填充到父容器元素中并每行填充尽可能多的子元素(因此 flex 行换行),同时所有子元素都被刷新到左边。

演示(下方)中的“&”是图标;请注意某些行末尾的某些子元素如何在悬停时无法到达它们进入下一行。

.flex-container {
  height: 100px;
  width: 400px;
  overflow-y: auto;
  display: flex;
  flex-flow: row wrap;
}

.flex-badge {
  display: inline-flex;
  margin-right: 0.5vw;
  margin-bottom: 0.5vh;
  background-color: lightblue;
  border: 1px solid black;
  cursor: pointer;
}

.hiddenIconTray {
  display: flex;
  overflow: hidden;
  width: 0;
  transition: width 0.5s;
}

.flex-badge:hover > .hiddenIconTray {
  width: 20px;
}

.flex-badge:hover {
  background-color: yellow;
}
<div class="flex-container">

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>
  
  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

</div>

谢谢!


解决方案


在这种情况下,您可以使用一种巧妙的解决方法,即在需要之前分配您想要使用的间距量。

这里需要几个步骤……

  • 我们知道这些flex-badge元素在展开后可能会换行到下一行,因此我们可以预期可能会添加一个margin-right我们知道宽度将增加的确切大小的值,您将其设置为20px. 因为您已经有一些边距来将这些 flex 元素分开,所以我将您的原始边距移动到gap父级上设置的属性,这是该属性的预期用途。所有现代浏览器 ( caniuse.com )都正式支持这一点
  • 为了进行此margin更改,我们还必须考虑这样一个事实,即添加margin-right: 20px到所有flex-badge元素会在每个元素之间添加一个不需要的间隙。margin-left: -20px我们可以通过先发制人地添加所有元素来抵消这种副作用flex-badge
  • 在悬停时,我们希望将margin-right悬停元素上的这个值转换为0随着元素的子元素填充的空间增加以填充hiddenIconTray空白而流畅地减少预分配的空间。
  • 由于这也会导致行中第一个元素也从父容器的左侧流出,因此我们将其与padding-left: 20px应用于父元素的 a 配对。

就是这样!一个功能齐全的容器,其中元素提前换行到下一行,如果它们在悬停时这样做的话。瞧 ✨

.flex-container {
  height: 100px;
  width: 400px;
  overflow-y: auto;
  display: flex;
  gap: 0.5vw 0.5vh;
  flex-flow: row wrap;
  padding-left: 20px;
}

.flex-badge {
  display: inline-flex;
  background-color: lightblue;
  border: 1px solid black;
  cursor: pointer;
  transition: margin-left 0.5s, margin-right 0.5s;
}

.flex-badge {
  margin-left: -20px;
}

.flex-badge {
  margin-right: 20px;
}

.hiddenIconTray {
  display: flex;
  overflow: hidden;
  width: 0;
  transition: width 0.5s;
}

.flex-badge:hover {
  margin-right: 0;
  background-color: yellow;
  z-index: 1;
}

.flex-badge:hover > .hiddenIconTray {
  width: 20px;
}
<div class="flex-container">

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>
  
  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

</div>

There's one last piece of this that still begs more attention. The items now overlap each other rather than pushing and pull each other as they grow and shrink. This is unfortunately unavoidable using only CSS as it is a result of the margin tweaking we performed.

However, if you are open to using a little JavaScript, we can get this working perfectly with the help of a simple loop to add a class to the first element in each flex row so we can style identify those and apply custom styles based on flex-rows rather than flex items. This is a feature not yet supported in CSS, but hopefully down the row, we'll be given with pseudo-classes for flex and grid rows and columns. This paired with the upcoming :has() pseudo selector would make this possible using only CSS.

We can also make use of the built-in ResizeObserver API, which is supported across all modern browsers (caniuse.com).

Here it is in action:

const flexBadgesContainer = document.querySelector('.flex-container');
const flexBadges = document.querySelectorAll('.flex-badge');
const markFirstOfFlexRows = () => {
  let currentOffsetTop = null;
  for (const badge of flexBadges) {
    if (badge.offsetTop !== currentOffsetTop) {
      currentOffsetTop = badge.offsetTop;
      badge.classList.add('flex-row-first');
    }
  }
}
markFirstOfFlexRows();
new ResizeObserver(() => {
  for (const badge of flexBadges) badge.classList.remove('flex-row-first');
  markFirstOfFlexRows();
}).observe(flexBadgesContainer);
.flex-container {
  height: 100px;
  width: 400px;
  overflow-y: auto;
  resize: both;
  border: 1px solid #000;
  display: flex;
  gap: 0.5vw 0.5vh;
  flex-flow: row wrap;
  padding-left: 20px;
}

.flex-badge {
  display: inline-flex;
  background-color: lightblue;
  border: 1px solid black;
  cursor: pointer;
  transition: margin-left 0.5s, margin-right 0.5s;
}

.flex-badge {
  margin-left: -20px;
}

.flex-badge {
  margin-right: 20px;
}

.hiddenIconTray {
  display: flex;
  overflow: hidden;
  width: 0;
  transition: width 0.5s;
}

.flex-badge:hover {
  margin-right: 0;
  background-color: yellow;
  z-index: 1;
}

.flex-badge:hover ~ .flex-badge:not(.flex-row-first) {
  margin-left: 0;
  margin-right: 0;
}

.flex-badge:hover ~ .flex-badge.flex-row-first + .flex-badge {
  margin-left: -20px;
}

.flex-badge:hover > .hiddenIconTray {
  width: 20px;
}
<div class="flex-container">

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem

    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>
  
  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">ipsum dolor
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

  <div class="flex-badge">Lorem ipsum dolor sit amet
    <div class="hiddenIconTray">
      &nbsp;
      <i class="icon">&</i>
    </div>
  </div>

</div>

I added resize: both to the .flex-container element in the example above so you can experiment with this and see that it works even as the parent element .flex-container changes in size.




所属网站分类: 技术文章 > 问答

作者:黑洞官方问答小能手

链接:http://www.qianduanheidong.com/blog/article/516810/5020a334f7beb94d12fb/

来源:前端黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

14 0
收藏该文
已收藏

评论内容:(最多支持255个字符)