You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React布局问题:实现3列卡片点击后展开整行内容

问题排查与解决方案:每行3个Div+点击展开整行内容布局

核心需求

实现每行3个div的网格布局,点击任意div后,在其下方显示一个占满整行宽度的内容区域。


当前代码梳理

HTML代码

<div className={styles.features_type}>
  {links.map(link => (
    <div key={link.id} className={openTab === link.id ? styles.feature_open : ""}>
      <div className={styles.features_cards}>
        {link.subMenus.map(subMenu => (
          <div key={subMenu.id}>
            <div onClick={() => {openMenu(subMenu.id)}}
              className={`${styles.features_card} ${active ? styles.active : ""}}`}>
                <h5>{subMenu.title}</h5>
            </div>
            {menuOpen === subMenu.id && (
            <div className={styles.features_cardContent}>
              {subMenu.subsubMenu.map(sub => (
              <div key={sub.id}>
                <p>{sub.paragraph}</p>
                <ul>
                  {sub.elements.map((element, index) => (
                    <li key={index}>
                      <p>{element}</p>
                    </li>
                  ))}
                </ul>
                <div>
                  <button>Commander</button>
                  <a href="#">En savoir plus →</a>
                </div>
              </div>
              ))}
            </div>
            )}
          </div>
        ))}
      </div>
    </div>
  ))}
</div>

数据常量

[
  {
    id: 1,
    title: 'Titre',
    subMenus: [
      {
        id: '01',
        title: 'Titre',
        subsubMenu: [
          {
            id: '01',
            paragraph: 'paragraph',
            elements : [
              'elements.',
              'elements.',
            ]
          }
        ],
      },
      {
        id: '02',
        title: 'Titre',
        subsubMenu: [
          {
            id: '01',
            paragraph: 'paragraph',
            elements : [
              'elements.',
              'elements.',
            ]
          }
        ],
      },
      {
        id: '03',
        title: 'Titre',
        subsubMenu: [
          {
            id: '01',
            paragraph: 'paragraph',
            elements : [
              'elements.',
              'elements.',
            ]
          }
        ],
      },
      {
        id: '04',
        title: 'Titre',
        subsubMenu: [
          {
            id: '01',
            paragraph: 'paragraph',
            elements : [
              'elements.',
              'elements.',
            ]
          }
        ],
      },
    ],
  },
]

菜单切换函数

const openMenu = (id) => {
  if (menuOpen === id) {
    setIsMenuOpen(null)
  } else {
    setIsMenuOpen(id)
  }
}

CSS代码

.features_cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  cursor: pointer;
}
.features_card {
  max-width: 310px;
  width: 100%;
  padding: 30px 60px 25px 30px;
  position: relative;
  transition: 50ms;
  margin-bottom: 10px;
  margin-left: 5px;
}

问题根源

展开内容无法占满整行的核心原因是HTML结构层级错误:

  • features_cardContent被嵌套在每个subMenu的容器内,而这个容器是features_cards网格的单列子项,导致展开内容的宽度被限制在单列范围内,无法突破到整行。
  • 额外问题:active样式的判断未关联到当前点击的subMenu,导致激活状态不生效。

分步解决方案

1. 调整HTML结构

将展开内容提升为features_cards网格的直接子元素,确保它能利用网格的跨列特性:

<div className={styles.features_type}>
  {links.map(link => (
    <div key={link.id} className={openTab === link.id ? styles.feature_open : ""}>
      <div className={styles.features_cards}>
        {link.subMenus.map(subMenu => (
          <>
            {/* 卡片容器作为网格单列子项 */}
            <div key={subMenu.id} className={styles.grid_item}>
              <div 
                onClick={() => openMenu(subMenu.id)}
                className={`${styles.features_card} ${menuOpen === subMenu.id ? styles.active : ""}`}
              >
                <h5>{subMenu.title}</h5>
              </div>
            </div>
            {/* 展开内容作为网格直接子项,跨列显示 */}
            {menuOpen === subMenu.id && (
              <div className={styles.features_cardContent}>
                {subMenu.subsubMenu.map(sub => (
                  <div key={sub.id}>
                    <p>{sub.paragraph}</p>
                    <ul>
                      {sub.elements.map((element, index) => (
                        <li key={index}>
                          <p>{element}</p>
                        </li>
                      ))}
                    </ul>
                    <div>
                      <button>Commander</button>
                      <a href="#">En savoir plus →</a>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </>
        ))}
      </div>
    </div>
  ))}
</div>

2. 修改CSS实现跨列与布局优化

.features_cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px 5px; /* 用gap替代margin,统一控制间距 */
  cursor: pointer;
}

.grid_item {
  /* 仅作为卡片的容器,无需额外样式 */
}

.features_card {
  max-width: 310px;
  width: 100%;
  padding: 30px 60px 25px 30px;
  transition: 50ms;
  /* 移除原有margin,避免与gap冲突 */
}

.features_card.active {
  /* 新增激活样式,提升交互反馈 */
  background-color: #f0f0f0;
  border-radius: 4px;
}

.features_cardContent {
  grid-column: 1 / -1; /* 强制占满网格所有列 */
  padding: 20px;
  background-color: #f8f8f8;
  border-radius: 4px;
  margin-top: 5px;
}

3. 优化JS逻辑(可选)

确保激活状态与当前点击的菜单关联,原函数逻辑无需修改,但需确认menuOpen状态是组件内的正确状态变量:

// 确保组件内已声明状态
const [menuOpen, setIsMenuOpen] = useState(null);

const openMenu = (id) => {
  setIsMenuOpen(prev => prev === id ? null : id);
}

效果验证

修改后:

  1. 每行自动排列3个卡片,超出部分换行到下一行
  2. 点击任意卡片,其下方会显示占满整行宽度的内容区域
  3. 再次点击同一卡片,内容区域收起;点击其他卡片,自动收起之前的内容并显示新内容

内容的提问来源于stack exchange,提问作者user22598666

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 19:15:06