使用粘性导航栏自动滚动 Flexbox 列

Posted

技术标签:

【中文标题】使用粘性导航栏自动滚动 Flexbox 列【英文标题】:Auto-Scrolling A Flexbox Column With A Sticky NavBar 【发布时间】:2021-07-28 07:04:27 【问题描述】:

因为这个问题最容易用一个例子来解释,这里有一个 CodeSandbox 来说明:https://codesandbox.io/s/github/metal450/react-scroll-sticky。该项目使用 React、TailwindCSS 和 react-scroll 构建,以实现平滑滚动。

我正在尝试使用粘性导航栏创建链接以滚动弹性框中的列/行。只要导航栏相对于 Flexbox 的主轴而不是其横轴是“粘性的”,它就可以很好地工作。在 CodeSandbox 中,如果输出的宽度合适(也就是在大型设备上),您会看到侧边栏出现在左侧,并且每个链接都会滚动右侧的列,将其对应的项目与容器的顶部对齐.

但是,如果您稍微缩小输出直到看到“小型设备”布局,您将能够看到问题所在。现在“侧边栏”位于顶部,如果您单击导航按钮,它将滚动以使所选项目隐藏在导航栏后面。

这确实有些道理:滚动条相对于容器滚动,并且该容器的顶部位于粘性导航栏的后面。但是,我想要的结果是单击“项目 1”并使其滚动,以便项目 1 位于 可滚动区域的顶部(不隐藏在粘性导航栏下方)。

我对弹性盒相当陌生,无论我尝试什么,我似乎都无法弄清楚所需的行为。任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

扩展 @YellowAfterlife 的答案,我会使用 refs 而不是 document.getElementById

此外,您只需要“移动”上的偏移量,因此您可以使用像 react-responsive 这样的包。

import React,  useRef, useState, useEffect  from "react";
import  Link  from "react-scroll";
import  useMediaQuery  from "react-responsive";

function App() 
  const items = ["item1", "item2", "item3", "item4", "item5", "item6", "item7"];
  const container = "scrollContainer";

  const menuRef = useRef(null);
  const [menuHeight, setMenuHeight] = useState(0);

  const isMobile = useMediaQuery(
    query: "(max-width: 767px)"
  );

  useEffect(() => 
    setMenuHeight(menuRef.current.clientHeight);
  , [menuRef]);

  return (
    <main className="flex absolute right-0 bottom-0 left-0 top-0">
      <div
        id=container
        className="md:flex-row flex flex-col overflow-auto border border-box"
        style= flexBasis: "65%", flex: 2 
      >
        <div
          ref=menuRef
          className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box"
        >
          items.map((item) => (
            <div className="cursor-pointer m-2 underline" key=item>
              <Link
                to=item
                smooth=true
                duration=500
                spy=true
                containerId=container
                offset=isMobile ? -menuHeight : null
              >
                item
              </Link>
            </div>
          ))
        </div>

        <div style= flex: 1 >
          items.map((item) => (
            <div key=item id=item className="h-64 border m-2 bg-gray-100">
              item
            </div>
          ))
        </div>
      </div>
    </main>
  );


export default App;

这里是codesandbox

【讨论】:

很好的解决方案!我不知道反应响应,在我的工具箱中肯定很方便:) 一个问题:使用带有 menuRef 的 useEffect 将 clientHeight 放入状态变量有什么好处,而不仅仅是指 menuRef.clientHeight.clientHeight滚动时直接? 我最终做了类似的事情:function getScrollOffset() if (window.getComputedStyle(menuRef.current?.parentElement).flexDirection === 'column') return -menuRef.current?.clientHeight || 0; return -gymCardOffset; 【参考方案2】:

react-scroll supports offset 参数,可用于动态考虑菜单高度:

import  Link  from 'react-scroll';

function App() 

    const items = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'];
    const container = "scrollContainer";
    const menu = "scrollMenu";

    function getScrollOffset() 
        if (true/* insert a condition for navbar being on top */) 
            return -document.getElementById(menu).offsetHeight;
         else return 0;
    

    return (
        <main className="flex absolute right-0 bottom-0 left-0 top-0">

            <div id=container className="md:flex-row flex flex-col overflow-auto border border-box" style= flexBasis: "65%", flex: 2>

                <div className="md:w-64 p-3 border sticky top-0 flex-shrink-0 bg-white border-box" id=menu>
                    items.map((item) => <div className="cursor-pointer m-2 underline" key=item>
                        <Link to=item smooth=true duration=500 spy=true offset=getScrollOffset() containerId=container>item</Link>
                    </div>)
                </div>

                <div style= flex: 1 >
                    items.map((item) => <div key=item id=item className="h-64 border m-2 bg-gray-100" >item</div>)
                </div>

            </div>

        </main>
    );


export default App;

鉴于滚动已经由 javascript 端处理,纯 CSS 的解决方案将远没有那么优雅并且没有特别的好处。

【讨论】:

我想过那样做,但感觉那样更老套,我可能只是忽略了“正确”的纯 CSS 方法(同样没有使用弹性框的经验)。如果纯 CSS 的方式实际上没有你说的那么优雅,那么也许我的第一个想法实际上是正确的方法! ...其实,看起来还是有特点的。单击滚动按钮后,只需将鼠标悬停在浏览器的滚动条上 - 突然当前滚动位置指示器“跳”到不同的位置。又名浏览器认为它已滚动到某个位置,然后当您将鼠标悬停在其滚动条上时,它会更改。让我知道这是 unlcear 或者如果你没有看到它,我会发布一个视频。 我不认为我在 Edge 91 上看到了这个 - 滚动条在制作动画时和之后似乎都按预期工作 有趣。我在 Linux 上的 Firefox 84 上,滚动条肯定表现得很古怪。在“大型设备模式”中它是可以预测的,但在“小型模式”(也就是本期的主题)中,无论我是否使用偏移量,滚动条都会以一种奇怪的方式跳跃,这使得它看起来有问题。

以上是关于使用粘性导航栏自动滚动 Flexbox 列的主要内容,如果未能解决你的问题,请参考以下文章

粘性导航栏可变大小更改滚动位置

当 tableview 使用自动布局滚动时避免粘性标题视图

带有导航栏大标题的 UITableView 奇怪的滚动行为,顶部的弹跳效果在滚动到顶部时自动切断/生涩

js监听网页页面滑动滚动事件,实现导航栏自动显示或隐藏

引导导航栏滚动 - 链接不可用

滚动到带有导航栏和侧边栏的部分