【问题标题】:Trying to emulate Bootstrap navbar collapse/expand with React State尝试使用 React State 模拟 Bootstrap 导航栏折叠/展开
【发布时间】:2021-05-16 09:56:13
【问题描述】:

单击汉堡图标时,我正在尝试使用 Bootstrap 展开和折叠导航栏。问题是我正在使用 React,而且我很确定 Bootstrap 的 DOM 操作与 React 的 DOM 操作有冲突。因此,我试图将所有操作从 Bootstrap 中移开,并完全依赖它们的样式并使用 React 来更新类。这对我来说是正确的方法。现在我意识到像 React Bootstrap 这样的东西存在,但我已经在我的项目中集成了 vanilla Bootstrap,看起来 React Bootstrap 仍然在版本 4 上,而我正在使用 5。

据我所知,Bootstrap 正在操纵这个 div 上的类:<div id="navbarSupportedContent" style="" class="navbar-collapse collapse">。因此,当菜单展开或折叠时,他们似乎正在添加类 collapsing 并删除类 collapse。然后,一旦完全显示,类变为 navbar-collapse collapse show 和折叠:navbar-collapse collapse

Bootstrap 将过渡时间设置为 0.35 秒。所以这是我的 React 代码:

const handleNavCollapse = () => {
        if (isNavCollapsed) {
            setNavbarClasses('navbar-collapse collapsing');
            //setTimeout(  <-- I learned this way does not work correctly
            setTimeout(() => 
                setNavbarClasses('navbar-collapse collapse show'),
                350
            );
        } else {
            setNavbarClasses('navbar-collapse collapse');
        }
        setIsNavCollapsed(!isNavCollapsed);
    };

另外,如果这可以帮助某人找出答案,这里是 Bootstrap 与 collapsing 类一起应用的 CSS:

.collapsing {
    height: 0;
    overflow: hidden;
    transition: height 0.35s ease;
}

它可以在单击汉堡图标时正确显示和隐藏导航栏菜单。但是没有过渡。当我像在 Bootstrap 站点上一样在检查器中查看 DOM 时,我没有看到添加了 collapsing 类。我只看到它添加show 并删除show。也许我没有正确设置 setTimeout。

更新: 所以我从这个 SO 答案中发现:https://stackoverflow.com/a/42650143/571723 实际上我做错了 setTimeout。我切换到箭头功能。现在我看到 collapsing 类被立即添加,然后在 350 毫秒超时后切换到 show。问题是仍然没有过渡动画,它只是在 350 毫秒后显示菜单。

更新 2: 我正在进一步调查 Bootstrap 是如何做到这一点的。当 Bootstrap 在他们的网站上为导航栏设置动画时,我设法截取了控制台的屏幕截图。 所以你可以看到高度设置为一个特定的数字并且变化非常快(实际上这个数字没有改变,它被设置为 206px 并保持在那里直到它完全打开)。您还可以看到 height: 0 被覆盖。这些事情在我的网站检查器中没有发生。

【问题讨论】:

    标签: javascript css reactjs twitter-bootstrap


    【解决方案1】:

    首先让我们从一个小建议开始:不要存储类 作为状态。你真正想要的是存储一些更原始的值 可用于在渲染时派生最终的 UI、类 包括在内。

    类似:

    const [isCollapsed, setCollapsed] = React.useState(false);
    
    const className = `navbar-collapse ${isCollapsed ? '' : 'show'}`;
    
    return <div className={className}> … </div>;
    

    您还可以使用非常常用的实用程序,例如 clsx(通常首选)或 classnames 构建它。


    如果浏览器会在 auto 和带有过渡的 CSS 属性(例如 height)的实际单位值(例如 12px)之间进行插值,那么开发可折叠组件将是微不足道的。但他们,你不能auto转换或转换到auto,但你可以在单位值之间进行插值。

    这确实是每个可折叠(或手风琴)UI 小部件在幕后所做的事情,它在已知和明确的单位尺寸之间进行动画处理。我们来看看original collapsible implementation (which is Vanilla JS, no jQuery) of Bootstrap v5.x

    让我们重写那些 show()hide() 方法,不要有任何噪音,例如添加那些 collapsing 类(它们专门用于帮助一些额外的样式)。 (以下是伪代码,绝对与 React 无关!)

    class Collapsible extends BaseComponent {
    
      show() {
        this.element.style.height = '0px';
        this.element.style.transition = 'height 300ms';
        this.element.style.height = this.element.scrollHeight + 'px';
      }
    
      hide() {
        this.element.style.height = this.element.getBoundingClientRect().height + 'px';
        this.element.style.transition = 'height 300ms';
        this.element.style.height = '0px';
      }
    
    }
    

    首先我们分析show()方法:

    1. this.element.style.height = '0px';

      此行将可折叠元素的height 强制为0px

      这对于下一个技巧很重要。

    2. this.element.style.transition = 'height 300ms';

      现在我们才设置过渡配置。这允许仅在此行之后height 进行更改时开始转换。 (其实没那么容易,这段代码应该不会像预期的那样工作,但这或多或少是这样的想法)。

    3. this.element.style.height = this.element.scrollHeight + 'px';

      我们现在使用HTMLElement.scrollHeight 来检索元素应具有的高度。这是有效的,因为scrollHeight 返回可用于滚动的总高度(以及“滚动”和“高度”),对于具有overflow: hiddenheight: 0px 的元素来说,这是内容的总高度。

    hide() 方法的作用相同,但相反:

    1. this.element.style.height = this.element.getBoundingClientRect().height + 'px';

      设置初始高度,但这次我们使用HTMLElement.getBoundingClientRect() 来获取元素的当前尺寸(widthheight),因为访问.style.height 将给我们简单的"auto",我们需要一个单位值(其中包含“px”)。

    2. this.element.style.transition = 'height 300ms';

      启用过渡。

    3. this.element.style.height = '0px';

      通过“隐藏”元素开始过渡。


    我们如何在 React 中完成这一切?简短的回答是“这并不简单”。

    您可以纯粹通过状态转换来实现它,如果您要学习 React,这将是一个很好的练习。

    我可以建议的是实际使用一些过渡/动画库来为你做这件事,代价是添加几 KB 的依赖项(尽管彻底评估它)。您需要检查该库是否支持 auto 值之间的转换。

    一个符合描述的库是react-spring,它还有一个与您想要达到的目标或多或少相同的示例:https://codesandbox.io/embed/q3ypxr5yp4

    【讨论】:

    • 谢谢!这都是非常有用的信息。我可能最终会选择 react-spring。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-25
    • 2015-12-07
    相关资源
    最近更新 更多