跳到主要内容

常见布局实现

问题

如何实现圣杯布局、双飞翼布局、粘性页脚、瀑布流、等高列等经典布局?

答案

两栏布局(侧边栏 + 主内容)

方案1: Flex(推荐)
.layout {
display: flex;
}
.sidebar {
width: 250px;
flex-shrink: 0; /* 不收缩 */
}
.main {
flex: 1; /* 占据剩余空间 */
}
方案2: Grid
.layout {
display: grid;
grid-template-columns: 250px 1fr;
}
方案3: float(旧方案)
.sidebar {
float: left;
width: 250px;
}
.main {
margin-left: 260px; /* 侧边栏宽度 + 间距 */
}

三栏布局

圣杯布局

中间列在 DOM 中最先出现(优先加载),左右两列用负 margin 和相对定位回到两侧:

<div class="container">
<div class="main">主内容(DOM 最先)</div>
<div class="left">左侧栏</div>
<div class="right">右侧栏</div>
</div>
圣杯布局(经典 float 方案)
.container {
padding: 0 200px; /* 为左右栏留出空间 */
}
.main, .left, .right {
float: left;
}
.main {
width: 100%;
}
.left {
width: 200px;
margin-left: -100%; /* 上移到 main 的最左边 */
position: relative;
left: -200px; /* 移到 container 的 padding 区域 */
}
.right {
width: 200px;
margin-left: -200px; /* 上移到 main 的最右边 */
position: relative;
right: -200px;
}

双飞翼布局

与圣杯布局的目的相同,但用内层 wrapper 的 margin 代替父容器的 padding:

<div class="main-wrapper">
<div class="main">主内容</div>
</div>
<div class="left">左侧栏</div>
<div class="right">右侧栏</div>
双飞翼布局
.main-wrapper, .left, .right {
float: left;
}
.main-wrapper {
width: 100%;
}
.main {
margin: 0 200px; /* 为左右栏留出空间 */
}
.left {
width: 200px;
margin-left: -100%;
}
.right {
width: 200px;
margin-left: -200px;
}

现代三栏布局(推荐)

Flex 三栏
.layout {
display: flex;
}
.left { width: 200px; flex-shrink: 0; }
.main { flex: 1; }
.right { width: 200px; flex-shrink: 0; }
Grid 三栏
.layout {
display: grid;
grid-template-columns: 200px 1fr 200px;
gap: 16px;
}
圣杯/双飞翼的现代意义

这两种布局在面试中仍然高频出现,考察对 float、负 margin、position 的理解。但实际开发中直接用 Flex 或 Grid 更简单、更可维护。

页脚始终在页面底部:内容少时贴在底部,内容多时正常跟在后面。

<body>
<header>Header</header>
<main>Main Content</main>
<footer>Footer</footer>
</body>
方案1: Flex(推荐)
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1; /* 撑满剩余空间 */
}
方案2: Grid
body {
min-height: 100vh;
display: grid;
grid-template-rows: auto 1fr auto;
}
方案3: calc
main {
min-height: calc(100vh - 60px - 80px); /* 减去 header 和 footer 高度 */
}

等高列

多列内容不同但高度一致:

方案1: Flex(天然等高)
.row {
display: flex;
}
.col {
/* Flex 子项默认 align-items: stretch,自动等高 */
padding: 16px;
border: 1px solid #e5e7eb;
}
方案2: Grid
.row {
display: grid;
grid-template-columns: repeat(3, 1fr);
/* Grid 子项默认 stretch,自动等高 */
}

瀑布流布局

CSS 多列布局

column-count 方案
.masonry {
column-count: 3;
column-gap: 16px;
}

.masonry-item {
break-inside: avoid; /* 防止被列截断 */
margin-bottom: 16px;
}
多列布局的局限

column-count 的排列顺序是从上到下,从左到右(纵向),而不是横向。如果需要横向顺序的瀑布流,需要 JS 方案(如 Masonry.js)或 CSS Grid 实验性特性。

Grid 瀑布流(实验性)

Grid masonry(Safari 预览版支持)
.masonry {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: masonry; /* 实验性特性 */
gap: 16px;
}

居中布局

详见 CSS 居中方案

响应式导航栏

响应式 Navbar
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
height: 60px;
}

.nav-links {
display: flex;
gap: 24px;
list-style: none;
}

/* 移动端:隐藏导航,显示汉堡菜单 */
@media (max-width: 768px) {
.nav-links {
display: none;
position: fixed;
top: 60px;
left: 0;
right: 0;
flex-direction: column;
background: white;
padding: 16px;
}

.nav-links.open {
display: flex;
}

.hamburger {
display: block;
}
}

/* 桌面端:隐藏汉堡菜单 */
@media (min-width: 769px) {
.hamburger {
display: none;
}
}

卡片网格布局

自适应卡片网格
.card-grid {
display: grid;
/* 自动填充,最小 280px,最大 1fr */
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
}

不需要任何媒体查询,自动根据容器宽度调整列数。

固定宽高比

aspect-ratio(推荐)
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}

.square {
aspect-ratio: 1; /* 正方形 */
}
padding-top hack(旧方案)
.video-wrapper {
position: relative;
padding-top: 56.25%; /* 9 / 16 = 56.25% */
height: 0;
}

.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

常见面试问题

Q1: 圣杯布局和双飞翼布局的区别?

答案

特性圣杯布局双飞翼布局
HTML 结构3 个并列元素main 需要额外 wrapper
占位方式父容器 paddingmain 内部 margin
定位方式负 margin + position: relative只需负 margin
主内容优先✅ DOM 最先✅ DOM 最先
复杂度较高稍低

两者目的相同:中间列 DOM 优先加载,左右固定宽度。现代方案直接用 Flex/Grid。

Q2: 如何实现粘性页脚?

答案

最简单的方案:

body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
main { flex: 1; }

mainflex: 1 会撑满剩余空间,footer 自然被推到底部。

Q3: 如何实现自适应的卡片网格?

答案

.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}

auto-fill + minmax() 让浏览器自动计算列数,无需媒体查询。

  • auto-fill:尽量多放列,多余空间保留为空轨道
  • auto-fit:尽量多放列,多余空间让现有列拉伸

Q4: 瀑布流布局怎么实现?

答案

方案排列方向优缺点
column-count纵向(从上到下)纯 CSS,但顺序不是横向的
JS Masonry横向需要库,计算位置
CSS Grid masonry横向实验性,仅 Safari

如果纵向顺序可以接受,column-count 是最简单的纯 CSS 方案。需要横向顺序则用 Masonry.js 等 JS 方案。

Q5: aspect-ratio 怎么用?和 padding-top hack 比有什么优势?

答案

/* 推荐 */
.box { aspect-ratio: 16 / 9; }

/* 旧方案 */
.box { padding-top: 56.25%; height: 0; position: relative; }

aspect-ratio 的优势:

  • 代码更简洁,语义清晰
  • 不需要 position: relativeheight: 0 hack
  • 子元素不需要绝对定位
  • 兼容性已经很好(Chrome 88+、Firefox 89+、Safari 15+)

Q6: Flex 布局中如何实现最后一行左对齐?

答案

当使用 justify-content: space-between 且最后一行不满时,元素会被分散对齐。解决方案:

方案1: 使用 Grid(推荐)
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
justify-content: start;
gap: 16px;
}
方案2: Flex + 占位元素
.list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}

/* 添加与 item 等宽的占位元素 */
.list::after {
content: '';
width: 200px; /* 与 item 等宽 */
}

Q7: 如何实现一个 1:1 正方形的自适应元素?

答案

/* 方案1: aspect-ratio(推荐) */
.square {
width: 100%;
aspect-ratio: 1;
}

/* 方案2: padding-top */
.square {
width: 100%;
padding-top: 100%; /* 百分比 padding 相对于宽度 */
height: 0;
}

/* 方案3: vw 单位 */
.square {
width: 50vw;
height: 50vw;
}

Q8: 如何实现文字环绕效果?

答案

这是 float 的本职工作:

.float-img {
float: left;
margin: 0 16px 16px 0;
shape-outside: circle(50%); /* 自定义环绕形状 */
}

shape-outside 可以实现圆形、多边形等非矩形的文字环绕效果。

相关链接