带有绝对定位内容的可滚动div

7
我有一个名为“Card”的div,如果内容超过其高度,它应该显示滚动条。我使用了overflow-y: auto来实现这一点。我试图在其中使用一个Select,并且选择菜单应该显示在卡片的前面。菜单的位置是absolute

问题是,即使使用了position: absolute,菜单也会占用卡片内部的空间,使其可以滚动。

image

如果我从卡片中删除溢出,它就能正常工作,但内容会超出它的范围。我已经为此创建了一个沙盒:

https://codesandbox.io/s/position-absolute-inside-overflow-y-9kppcy?file=/src/App.js

我尝试过的其他方法

  • 在portal中显示SelectMenu

    • 目前为止我得到了更好的结果,但当窗口滚动时,菜单会在屏幕上固定。
  • 从卡片中删除溢出,将其添加到CardBody元素中,并将选择保持在其外部。

    • 取得了好的效果,但是由于有很多嵌套的
      ,所以很难将它保持在每个具有溢出的元素之外。
    • 如果选择菜单位于模态框内,则无法正常工作(因为模态框应该有滚动条)。

更多细节

  • 我使用react-select创建我的选择器,但问题严格涉及CSS和HTML。

这可能是一个常见的问题,但我找不到解决方案。


3
也许这个链接对您非常有用:https://css-tricks.com/popping-hidden-overflow/(它来自一个名为 "css-tricks" 的博客),它描述了您的问题。按照他们所说的去做,希望您能尽快解决问题。祝您编码愉快! - Laaouatni Anas
@LaaouatniAnas 很遗憾,它没有起作用,因为两个滚动条都是在同一个方向(y轴)上。 - Alexandre Aragão
4个回答

1

我认为你使用React Portal的解决方案非常有道理,但你需要一些技巧来定位元素。

{showMenu &&
  createPortal(
    <div className="SelectMenu" style={menuStyle}>
      I'm the menu.
    </div>,
    PORTAL_HOST_NODE
  )}
menuStyle是由useMemo计算出来的值,它包含了我们需要应用到菜单上的CSS样式。你想要的是菜单使用position: fixed,并使用触发元素(在这种情况下是“打开选择”按钮)的topleftwidthheight属性来定位元素。
为了确保在视口滚动时更新位置,您还需要跟踪window.scrollY的值:
const [scrollY, setScrollY] = useState(window.scrollY);

useEffect(() => {
  const onScroll = () => setScrollY(window.scrollY);
  window.addEventListener("scroll", onScroll);

  return () => {
    window.removeEventListener("scroll", onScroll);
  };
}, []);

我看到你想要手动将下拉菜单与触发元素相分离,偏移量为0.5rem。我们可以将其存储为CSS变量:

.SelectMenu {
  --top-offset: 0.5rem;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 99;
  border: 1px solid #bebebe;
  background-color: white;
  border-radius: 0.5rem;
  padding: 2rem 1rem;
  box-sizing: border-box;
}

然后,就是根据之前提到的设置其他代码的问题了。一些注意事项:

  • 使用CSS transform以便我们可以处理子像素定位(而不是top/left)
  • 设置宽度,使您的选择菜单跟随其触发元素的宽度
  • 使用上面设置的CSS自定义属性计算CSS transform的y轴值
const [menuTriggerElementRect, setMenuTriggerElementRect] = useState(null);

const toggleMenu = useCallback((e) => {
  setShowMenu((old) => !old);
  setMenuTriggerElementRect(e.currentTarget.getBoundingClientRect());
}, []);

const menuStyle = useMemo(() => {
  if (menuTriggerElementRect === null) return {};

  const { top, left, width, height } = menuTriggerElementRect;
  return {
    transform: `translate(${left}px, calc(${
      top + height - scrollY
    }px + var(--top-offset)))`,
    width: `${width}px`
  };
}, [menuTriggerElementRect, scrollY]);

在此处查看您更新的 CodeSandbox 示例:

编辑“overflow-y”内部的“position: absolute”(分叉)


0

编辑:原始的 Code Sandbox 链接错误,现在应该可以使用了。

这里有一个仅使用 CSS 的解决方案,尽可能保留了 Card 类的大部分功能。

Code Sandbox 解决方案

以下是 CSS 代码:

.App {
  font-family: sans-serif;
  text-align: center;
}

.Card {
  display: flex;
  flex-direction: column;
  align-items: center;
  border: 1px solid #bebebe;
  background-color: white;
  border-radius: 0.5rem;
  padding: 2rem 1rem;
  margin: 4rem; 
  overflow-y: auto;
  height: 124px;
}

.Select {
  width: 60vw;
}

.SelectButton {
  all: unset;
  background-color: royalblue;
  color: white;
  padding: 1rem;
  box-sizing: border-box;
  cursor: pointer;
  width: 100%;
}

.SelectMenu {
  position: absolute;
  z-index: 99;
  width: 60vw;
  border: 1px solid #bebebe;
  background-color: white;
  border-radius: 0.5rem;
  padding: 2rem 1rem;
  box-sizing: border-box;
  margin-top: 10px;
}

关于使用本文中的position: absolute,请参考此帖子Using position:absolute, the nearest positioned ancestor is having no effect
从这篇文章中可以得知:
  • 绝对定位的元素将被定位在其包含块内,该包含块由最近的已定位祖先定义。但是,如果没有已定位的祖先,则包含块是初始包含块(即视口)。
  • 当您将position: absolute应用于元素时,它将从正常流中移除。就是这样。该元素仍将被定位,就好像它在正常流中一样。只有在应用CSS偏移属性(left、right、top、bottom)后,您才会实际定位该元素。
因此,在这种情况下,由于没有任何已定位的祖先,所以包含块是视口。这使得absolutely定位的菜单可以保持在视口中的位置,但如果div中有更多内容导致溢出,菜单将不会随着div滚动。您可以通过取消Code Sandbox fork中App.js中的一些代码来查看此行为。

我还改变了一些宽度,使事物看起来更美观,但从功能的角度来看,这并没有什么区别。


我曾尝试过使用fixed定位来实现类似的效果。这是一个不错的方法,但遗憾的是没有达到预期的结果。 - Alexandre Aragão

0
你可以使用修改器类仅覆盖该卡片的溢出部分。这样应该可以得到你想要的结果。

查看此处的Fork


这基本上是我尝试使用“CardBody”类方法的内容。就像我说的,在可滚动的模态框内无法正常工作。 - Alexandre Aragão

0
如果您不需要将 Menu 放在 Card 内部,可以添加一个外部包装器并将其相对于该包装器定位。

Code Sandbox 解决方案


如果我们使用React,那就是一种门户式的方法。如果菜单在按钮下方保持固定,那将非常好。 - Alexandre Aragão

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接