你是不是有过这样的经历:辛辛苦苦做好的网页,在电脑上看起来整整齐齐,但一放到手机或平板上,布局就变得乱七八糟,要么挤成一团,要么空白一大片。
然后你上网一搜解决办法,全都是让你写一堆看起来超复杂的 @media(媒体查询)代码,不同的屏幕尺寸就要写不同的样式,光是想想就头大。
别担心!其实,只需要 2 行 CSS 代码,就能让你的一排排卡片、图片、内容块自动适应屏幕宽度,再也不用写繁琐的媒体查询了!
这就是 CSS Grid 的 auto-fill 和 auto-fit 属性。
先想再学
假设我们要做一排卡片,每个卡片最小宽度是 200px。我们希望:
-
屏幕很宽时,自动显示很多列,并且每列平均分配剩余空间。
-
屏幕变窄时,卡片自动换行,保证布局不会乱。
以往:可能需要写 3-4 个媒体查询,分别针对手机、平板、电脑。
现在:只需要给卡片的爸爸(父容器)加上 2 行 CSS!
css
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px; /* (可选)增加卡片之间的间距 */
}
把这段代码放到样式里,无论你怎么拉浏览器的窗口,卡片都会自动调整排列

代码拆解:理解每部分的作用
这行核心代码 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 看起来有点复杂,我们把它拆开揉碎了讲,保证你能懂!
display: grid:将元素定义为网格容器
grid-template-columns:定义网格的列数和每列的宽度
repeat() :重复函数,避免重复书写相同的模式
auto-fit:自动调整网格轨道数量以填充容器
minmax(200px, 1fr) :定义每列的最小和最大宽度
minmax 函数详解
-
minmax()函数接受两个参数:最小值和最大值。例如:
minmax(200px, 1fr)表示列宽最小 200px,最大为 1fr(剩余空间的等分)
-
minmax(auto, 300px)表示高度自适应内容,但不超过 300px
fr 单位的作用
- fr(fraction)单位按比例分配剩余空间。例如:
grid-template-columns: 2fr 1fr 1fr 会创建三列,比例为 2:1:1
auto-fill vs auto-fit:关键区别
虽然两者都用于自动创建网格,但它们的行为有重要区别:
特性 | auto-fill | auto-fit |
---|---|---|
行为特点 | 保留空轨道,保持网格结构,内容不拉伸 | 折叠空轨道,让内容拉伸 |
内容拉伸 | 不拉伸内容 | 拉伸内容填满容器 |
适用场景 | 需要固定网格数的布局(日历、表格、固定布局) | 需要内容自适应填满的布局(卡片、图片) |
auto-fill (填充模式) :它会老老实实地生成 10 个位置,5 个有内容块,另外 5 个是空的但占着位置。这样你的 5 个块宽度就是固定的 200px,不会变。
auto-fit (适配模式) :它很聪明!它发现后面 5 个位置是空的,就会把它们折叠掉,然后让已有的 5 个内容块自动拉伸变宽,平分一整行的空间。

html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>auto-fit 和 auto-fill 对比演示</title>
<style>
.container {
display: grid;
gap: 16px; /* 设置网格间隙 */
}
/* auto-fill 效果 - 保留空位 */
.container.fill {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
background-color: #ffe6e6; /* 浅红色背景,方便看容器范围 */
}
/* auto-fit 效果 - 拉伸填充 */
.container.fit {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
background-color: #e6ffe6; /* 浅绿色背景 */
}
.item {
background-color: rgb(141, 141, 255);
height: 100px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
</style>
</head>
<body>
<h2>auto-fill (保留空轨道,适合日历/表格)</h2>
<div class="container fill">
<div class="item">卡片1</div>
<div class="item">卡片2</div>
<div class="item">卡片3</div>
</div>
<h2>auto-fit (拉伸填充,适合卡片/相册)</h2>
<div class="container fit">
<div class="item">卡片1</div>
<div class="item">卡片2</div>
<div class="item">卡片3</div>
</div>
</body>
</html>
下次当你再需要做响应式布局时,别第一时间就想着一堆媒体查询了。试试这个,让它帮你自动搞定,省时又省力!
Auto-Fit vs Auto-Fill 演练场

大家复制代码时,可能会因格式转换出现错乱,导致样式失效。建议先少量复制代码进行测试,若未能解决问题,私信回复源码两字,我会发送完整的压缩包给你。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Auto-Fit vs Auto-Fill 可视化对比</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 3rem;
padding: 2rem;
background: rgba(255, 255, 255, 0.8);
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: #2c3e50;
}
.subtitle {
font-size: 1.2rem;
color: #7f8c8d;
max-width: 800px;
margin: 0 auto;
}
.comparison {
display: flex;
gap: 2rem;
margin-bottom: 3rem;
}
@media (max-width: 768px) {
.comparison {
flex-direction: column;
}
}
.panel {
flex: 1;
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.panel-header {
padding: 1.5rem;
background: #3498db;
color: white;
text-align: center;
}
.auto-fill .panel-header {
background: #e74c3c;
}
.auto-fit .panel-header {
background: #2ecc71;
}
.panel-content {
padding: 2rem;
border: 2px dashed #ddd;
margin: 1rem;
border-radius: 8px;
transition: all 0.3s ease;
position: relative;
min-height: 300px;
}
.auto-fill .panel-content {
background: rgba(231, 76, 60, 0.05);
}
.auto-fit .panel-content {
background: rgba(46, 204, 113, 0.05);
}
.grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.auto-fit .grid {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.card {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
color: white;
border-radius: 8px;
padding: 1.5rem;
text-align: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 120px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.auto-fill .card {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
}
.auto-fit .card {
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
}
.card-number {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.controls {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
}
.control-group {
margin-bottom: 1.5rem;
}
.control-group h3 {
margin-bottom: 0.5rem;
color: #2c3e50;
}
.slider-container {
display: flex;
align-items: center;
gap: 1rem;
}
.slider {
flex: 1;
-webkit-appearance: none;
height: 8px;
border-radius: 4px;
background: #ddd;
outline: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
}
.auto-fill .slider::-webkit-slider-thumb {
background: #e74c3c;
}
.auto-fit .slider::-webkit-slider-thumb {
background: #2ecc71;
}
.value-display {
min-width: 60px;
text-align: center;
font-weight: bold;
}
.button-group {
display: flex;
gap: 1rem;
}
button {
padding: 0.8rem 1.5rem;
border: none;
border-radius: 4px;
background: #3498db;
color: white;
font-weight: bold;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: #2980b9;
}
.explanation {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.explanation h2 {
margin-bottom: 1rem;
color: #2c3e50;
}
.key-differences {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-top: 2rem;
}
@media (max-width: 768px) {
.key-differences {
grid-template-columns: 1fr;
}
}
.difference {
padding: 1.5rem;
border-radius: 8px;
}
.auto-fill-difference {
background: rgba(231, 76, 60, 0.1);
border-left: 4px solid #e74c3c;
}
.auto-fit-difference {
background: rgba(46, 204, 113, 0.1);
border-left: 4px solid #2ecc71;
}
.difference h3 {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
.difference-icon {
margin-right: 0.5rem;
font-size: 1.5rem;
}
.grid-track-visual {
height: 4px;
background: rgba(255, 255, 255, 0.5);
margin-top: 0.5rem;
border-radius: 2px;
}
.grid-track-indicator {
height: 100%;
background: white;
border-radius: 2px;
width: 100%;
}
.code-snippet {
background: #2d3436;
color: #dfe6e9;
padding: 1rem;
border-radius: 4px;
font-family: 'Fira Code', monospace;
margin: 1rem 0;
overflow-x: auto;
}
.property {
color: #81ecec;
}
.value {
color: #fab1a0;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Auto-Fit vs Auto-Fill 可视化对比</h1>
<p class="subtitle">通过实时演示直观了解CSS Grid中auto-fit和auto-fit属性的区别,以及它们如何影响响应式布局</p>
</header>
<div class="controls">
<div class="control-group">
<h3>容器宽度控制</h3>
<div class="slider-container">
<span>窄</span>
<input type="range" min="200" max="1000" value="800" class="slider" id="width-slider">
<span>宽</span>
<span class="value-display" id="width-value">800px</span>
</div>
</div>
<div class="control-group">
<h3>卡片数量</h3>
<div class="slider-container">
<span>少</span>
<input type="range" min="1" max="12" value="6" class="slider" id="card-slider">
<span>多</span>
<span class="value-display" id="card-value">6 卡片</span>
</div>
</div>
<div class="button-group">
<button id="reset-btn">重置演示</button>
<button id="toggle-cards-btn">切换卡片可见性</button>
</div>
</div>
<div class="comparison">
<div class="panel auto-fill">
<div class="panel-header">
<h2>Auto-Fill 布局</h2>
<p>保留空轨道,适合需要固定网格结构的布局</p>
</div>
<div class="panel-content" id="auto-fill-container">
<div class="grid" id="auto-fill-grid">
<div class="card"><div class="card-number">1</div>卡片</div>
<div class="card"><div class="card-number">2</div>卡片</div>
<div class="card"><div class="card-number">3</div>卡片</div>
<div class="card"><div class="card-number">4</div>卡片</div>
<div class="card"><div class="card-number">5</div>卡片</div>
<div class="card"><div class="card-number">6</div>卡片</div>
</div>
</div>
</div>
<div class="panel auto-fit">
<div class="panel-header">
<h2>Auto-Fit 布局</h2>
<p>折叠空轨道,内容拉伸填充可用空间</p>
</div>
<div class="panel-content" id="auto-fit-container">
<div class="grid" id="auto-fit-grid">
<div class="card"><div class="card-number">1</div>卡片</div>
<div class="card"><div class="card-number">2</div>卡片</div>
<div class="card"><div class="card-number">3</div>卡片</div>
<div class="card"><div class="card-number">4</div>卡片</div>
<div class="card"><div class="card-number">5</div>卡片</div>
<div class="card"><div class="card-number">6</div>卡片</div>
</div>
</div>
</div>
</div>
<div class="explanation">
<h2>理解 Auto-Fit 和 Auto-Fill</h2>
<p>CSS Grid布局中的<code>auto-fit</code>和<code>auto-fill</code>关键字允许我们创建灵活的响应式布局,而无需编写复杂的媒体查询。它们都用于<code>repeat()</code>函数中,但与<code>minmax()</code>结合使用时,行为有所不同。</p>
<div class="code-snippet">
<span class="property">grid-template-columns</span>: <span class="value">repeat(<select id="code-type">
<option value="auto-fill">auto-fill</option>
<option value="auto-fit" selected>auto-fit</option>
</select>, minmax(150px, 1fr))</span>;
</div>
<div class="key-differences">
<div class="difference auto-fill-difference">
<h3><span class="difference-icon">↔️</span> Auto-Fill 行为</h3>
<p>Auto-fill会尽可能多地创建网格轨道,即使没有网格项填充它们。空轨道仍然占用空间,影响布局。</p>
<ul>
<li>保持网格结构完整</li>
<li>空轨道保持最小宽度</li>
<li>适合需要严格对齐的布局</li>
<li>在内容数量变化但网格结构需要保持不变时非常有用</li>
</ul>
</div>
<div class="difference auto-fit-difference">
<h3><span class="difference-icon">↕️</span> Auto-Fit 行为</h3>
<p>Auto-fit会折叠任何空轨道,并拉伸现有网格项以填充可用空间。它更注重内容的填充而非网格结构的保留。</p>
<ul>
<li>折叠空轨道</li>
<li>内容拉伸填满容器</li>
<li>适合卡片、画廊和内容块布局</li>
<li>在希望内容充分利用可用空间时非常有用</li>
</ul>
</div>
</div>
</div>
</div>
<script>
// 获取DOM元素
const widthSlider = document.getElementById('width-slider');
const widthValue = document.getElementById('width-value');
const cardSlider = document.getElementById('card-slider');
const cardValue = document.getElementById('card-value');
const autoFillContainer = document.getElementById('auto-fill-container');
const autoFitContainer = document.getElementById('auto-fit-container');
const autoFillGrid = document.getElementById('auto-fill-grid');
const autoFitGrid = document.getElementById('auto-fit-grid');
const resetBtn = document.getElementById('reset-btn');
const toggleCardsBtn = document.getElementById('toggle-cards-btn');
const codeType = document.getElementById('code-type');
// 初始化卡片
const initialCards = Array.from(autoFillGrid.children).map(card => card.outerHTML);
// 更新容器宽度
function updateContainerWidth() {
const width = widthSlider.value;
widthValue.textContent = `${width}px`;
autoFillContainer.style.width = `${width}px`;
autoFitContainer.style.width = `${width}px`;
}
// 更新卡片数量
function updateCardCount() {
const count = parseInt(cardSlider.value);
cardValue.textContent = `${count} 卡片`;
// 更新auto-fill网格
autoFillGrid.innerHTML = '';
for (let i = 0; i < count; i++) {
autoFillGrid.innerHTML += initialCards[i % initialCards.length];
}
// 更新auto-fit网格
autoFitGrid.innerHTML = '';
for (let i = 0; i < count; i++) {
autoFitGrid.innerHTML += initialCards[i % initialCards.length];
}
}
// 切换卡片可见性
function toggleCards() {
const cards = document.querySelectorAll('.card');
const isVisible = cards[0].style.opacity !== '0';
cards.forEach(card => {
card.style.opacity = isVisible ? '0' : '1';
card.style.visibility = isVisible ? 'hidden' : 'visible';
});
toggleCardsBtn.textContent = isVisible ? '显示卡片' : '隐藏卡片';
}
// 重置演示
function resetDemo() {
widthSlider.value = 800;
cardSlider.value = 6;
updateContainerWidth();
updateCardCount();
const cards = document.querySelectorAll('.card');
cards.forEach(card => {
card.style.opacity = '1';
card.style.visibility = 'visible';
});
toggleCardsBtn.textContent = '切换卡片可见性';
}
// 初始化
updateContainerWidth();
updateCardCount();
// 事件监听器
widthSlider.addEventListener('input', updateContainerWidth);
cardSlider.addEventListener('input', updateCardCount);
resetBtn.addEventListener('click', resetDemo);
toggleCardsBtn.addEventListener('click', toggleCards);
codeType.addEventListener('change', function() {
if (this.value === 'auto-fill') {
autoFillGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(150px, 1fr))';
autoFitGrid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(150px, 1fr))';
} else {
autoFillGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(150px, 1fr))';
autoFitGrid.style.gridTemplateColumns = 'repeat(auto-fit, minmax(150px, 1fr))';
}
});
</script>
</body>
</html>
各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!