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); }
效果验证
修改后:
- 每行自动排列3个卡片,超出部分换行到下一行
- 点击任意卡片,其下方会显示占满整行宽度的内容区域
- 再次点击同一卡片,内容区域收起;点击其他卡片,自动收起之前的内容并显示新内容
内容的提问来源于stack exchange,提问作者user22598666
相关产品推荐
相关产品推荐

