【问题标题】:Scrollable div with absolute content具有绝对内容的可滚动 div
【发布时间】:2022-11-03 10:15:58
【问题描述】:

我有一个Card div,如果内容超过它的高度,它应该显示滚动。我用overflow-y: auto 来做到这一点。我正在尝试在其中使用Select,并且选择菜单应该显示在卡片前面。菜单位置为absolute

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

如果我从卡中删除溢出,它工作正常,但内容超过它。我为它创建了一个沙箱:

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

我尝试过的其他事情

  • 在门户中显示SelectMenu

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

    • 发挥了作用,但使用深度嵌套的 div,很难将其保持在每个元素之外并溢出。
    • 如果 Select 在模态框内则不起作用(因为模态框应该有滚动条)。

更多细节

  • 我正在使用react-select 来创建我的选择,但问题在于css 和html。

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

【问题讨论】:

  • 看到这个也许它会非常有用css-tricks.com/popping-hidden-overflow(它来自一个名为css-tricks的博客)它真正描述了你的问题,按照他们所说的,我希望你能尽快解决它。编码美好的一天!
  • @LaaouatniAnas 很遗憾,它没有用,因为两个卷轴都在同一个方向(y)

标签: html css reactjs


【解决方案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 变换,以便我们可以处理子像素放置(而不是顶部/左侧)
  • 设置宽度,使您的选择菜单跟随其触发元素的宽度
  • 使用上面设置的 CSS 自定义属性计算 CSS 变换的 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 示例:

【讨论】:

    【解决方案2】:

    您只能使用修饰符类覆盖该卡的溢出。那应该会给您想要的结果。

    See Fork here

    【讨论】:

      【解决方案3】:

      好的,这是一个仅使用 CSS 的解决方案,尽可能多地保留 Card 类的功能。

      Code Sandbox Solution

      这是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;
      }
      

      来自这篇帖子Using position:absolute, the nearest positioned ancestor is having no effect 的关于使用position: absolute 的说明:

      从帖子:

      • 绝对定位元素将定位在其包含块中,该包含块由最近的定位祖先定义。但是,如果没有定位的祖先,则包含块是初始包含块(即视口)。
      • 当您将 position: absolute 应用于元素时,您会将其从正常流程中移除。而已。该元素仍将被定位,就好像它在正常流中一样。直到您应用 CSS 偏移属性(左、右、上、下)才能实际定位元素。

      因此,由于在我的解决方案中没有任何定位的祖先,包含块就是视口。这允许absolutely 定位的菜单随视口移动,但如果 div 中有更多内容导致其溢出,则菜单不会随 div 滚动。您可以通过在我的 Code Sandbox fork 中取消注释 App.js 中的某些代码来查看此行为。

      我还更改了一些宽度以使事情看起来不错,但从功能的角度来看并没有什么不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-09-22
        • 1970-01-01
        • 2010-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多