繁体   English   中英

尝试使用 React State 模拟 Bootstrap 导航栏折叠/展开

[英]Trying to emulate Bootstrap navbar collapse/expand with React State

单击汉堡图标时,我正在尝试使用 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"> 因此,当菜单展开或折叠时,他们似乎正在添加 class collapsing并删除 class collapse 然后,一旦完全显示,类变成navbar-collapse collapse show和折叠: navbar-collapse collapse

Bootstrap 将转换时间设置为 35 秒。 所以这是我的反应代码:

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);
    };

此外,如果这有助于某人找出答案,这里是 CSS,Bootstrap 正在应用collapsing class:

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

它工作一半,因为它在单击汉堡图标时正确显示和隐藏导航栏菜单。 但是没有过渡。 当我像在 Bootstrap 站点上那样在检查器中观看 DOM 时,我没有看到collapsing的 class 被添加。 我只看到它 add show和 remove show 也许我没有正确执行 setTimeout。

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

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

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

就像是:

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

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


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

这确实是每个可折叠(或手风琴)UI 小部件在幕后所做的事情,它在已知和明确的单位尺寸之间进行动画处理。 让我们看一下Bootstrap v5.x 的原始可折叠实现(是 Vanilla JS,没有 jQuery)

让我们在没有任何噪音的情况下重写这些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 中完成这一切? 简短的回答是“这并不简单”。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM