如何在响应式CSS Grid(非表格)中实现无重复的自适应边框?
响应式Grid网格边框的正确实现
我有一个React-Tailwind组件,代码如下:
{/* <div className='grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] border-t border-l border-gray-300 my-4'> */} <div className='grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] my-4'> {productsToRender.map((product, i) => ( <div key={i} onClick={() => console.log(`product [${product.title}] clicked`)} // className='flex flex-col justify-between items-center cursor-pointer border-r border-b bg-white border-gray-300' className={`flex flex-col justify-between items-center cursor-pointer border-r border-b bg-white border-gray-300`} style={{ borderTop: i % 2 === 0 ? '1px solid #ccc' : 'none', borderLeft: i === 0 || i % 2 === 0 ? '1px solid #ccc' : 'none', }} > <img src={product.img_url} alt={product.title} className='w-[13rem]' /> <div className="flex flex-col p-[1rem]"> <span className='font-semibold'>{product.title}</span> <span style={{ height: '4rem', lineHeight: '20px', wordBreak: 'break-all', display: '-webkit-box', WebkitBoxOrient: 'vertical', MozBoxOrient: 'vertical', msBoxOrient: 'vertical', boxOrient: 'vertical', WebkitLineClamp: NUM_OF_DESC_LINES, MozLineClamp: NUM_OF_DESC_LINES, msLineClamp: NUM_OF_DESC_LINES, lineClamp: NUM_OF_DESC_LINES, overflow: 'hidden', }}>{product.description}</span> <span className='mt-[1rem]'>{formatter.format(product.price)}</span> </div> </div> ))} </div>
这个网格用grid-cols-[repeat(auto-fill,minmax(230px,1fr))]实现响应式列数,列数随屏幕尺寸变化。原本的方案是网格容器只渲染顶部和左侧边框,每个单元格渲染右侧和底部边框,第一行单元格填满时显示正常,但最后一行单元格数量不足一行时,容器的底部边框会铺满整个宽度,不符合需求。请问有没有无需大量计算代码的简单方法,实现无重复、仅覆盖实际单元格区域的正确边框?
解决方案1:使用Tailwind的Divide工具类
直接用Tailwind内置的divide-x和divide-y类处理网格边框,无需手动判断索引或写内联样式:
- 给网格容器添加
divide-x divide-y divide-gray-300,移除容器原有的边框类 - 删除每个单元格的
border-r border-b以及内联的borderTop、borderLeft样式
修改后的代码:
<div className='grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] divide-x divide-y divide-gray-300 my-4'> {productsToRender.map((product, i) => ( <div key={i} onClick={() => console.log(`product [${product.title}] clicked`)} className='flex flex-col justify-between items-center cursor-pointer bg-white' > <img src={product.img_url} alt={product.title} className='w-[13rem]' /> <div className="flex flex-col p-[1rem]"> <span className='font-semibold'>{product.title}</span> <span style={{ height: '4rem', lineHeight: '20px', wordBreak: 'break-all', display: '-webkit-box', WebkitBoxOrient: 'vertical', MozBoxOrient: 'vertical', msBoxOrient: 'vertical', boxOrient: 'vertical', WebkitLineClamp: NUM_OF_DESC_LINES, MozLineClamp: NUM_OF_DESC_LINES, msLineClamp: NUM_OF_DESC_LINES, lineClamp: NUM_OF_DESC_LINES, overflow: 'hidden', }}>{product.description}</span> <span className='mt-[1rem]'>{formatter.format(product.price)}</span> </div> </div> ))} </div>
该方案优势:
- 完全适配响应式列数,自动匹配不同屏幕尺寸
- 无重复边框,仅在单元格之间及最外层显示边框
- 无需任何索引判断或计算逻辑,代码简洁易维护
解决方案2:调整单元格边框逻辑(不依赖Divide类)
若不想使用Divide类,可调整单元格边框规则:仅给非第一行的单元格加顶部边框,非第一列的单元格加左侧边框,同时移除容器边框:
<div className='grid grid-cols-[repeat(auto-fill,minmax(230px,1fr))] my-4'> {productsToRender.map((product, i) => ( <div key={i} onClick={() => console.log(`product [${product.title}] clicked`)} className={`flex flex-col justify-between items-center cursor-pointer bg-white border-gray-300 border-b border-r ${i >= document.querySelector('.grid').children.length / Math.ceil(productsToRender.length / document.querySelector('.grid').children.length) ? 'border-t' : ''} ${i % Math.floor(document.querySelector('.grid').offsetWidth / 230) !== 0 ? 'border-l' : ''}`} > {/* 内容保持不变 */} <img src={product.img_url} alt={product.title} className='w-[13rem]' /> <div className="flex flex-col p-[1rem]"> <span className='font-semibold'>{product.title}</span> <span style={{ height: '4rem', lineHeight: '20px', wordBreak: 'break-all', display: '-webkit-box', WebkitBoxOrient: 'vertical', MozBoxOrient: 'vertical', msBoxOrient: 'vertical', boxOrient: 'vertical', WebkitLineClamp: NUM_OF_DESC_LINES, MozLineClamp: NUM_OF_DESC_LINES, msLineClamp: NUM_OF_DESC_LINES, lineClamp: NUM_OF_DESC_LINES, overflow: 'hidden', }}>{product.description}</span> <span className='mt-[1rem]'>{formatter.format(product.price)}</span> </div> </div> ))} </div>
注:该方案需要实时计算当前屏幕下的列数,不如第一种方法省心,优先推荐使用Divide工具类方案。
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

