深入理解圣杯布局和双飞翼布局

前言

最近我朋友在群里发了一张图,说为什么main的宽度100%了,sub还是可以覆盖它呢?看来是时候带他回忆一波经典的圣杯布局和双飞翼布局了。正好最近看到好多面试题都有提到。 /笑哭

群内链接如下图:

字面意思

圣呗布局和双飞翼布局从字面意思来看是这样的:

一个像圣杯或者像展翅的禽类这样的布局

通俗的来说就是左右两栏固定宽度,中间部分自适应的三栏布局。

两者本质

圣杯布局

  1. 首先把left、middle、right都放出来
  2. 给它们三个设置上float: left, 脱离文档流;
  3. 一定记得给container设置上overflow: hidden; 可以形成BFC撑开文档
  4. left、right设置上各自的宽度
  5. middle设置width: 100%;

接下来比较重要了:

  1. 给left、middle、right设置position: relative;
  2. left设置 left: -leftWidth, right设置 right: -rightWidth;
  3. container设置padding: 0, rightWidth, 0, leftWidth;

我注意到圣杯布局的left、middle、right都有position: relative;

设:

.left width:200px
.right width:220px

那么下面的这些属性为什么要存在?

.container上面的paddind
.left 的left: -200px;
.right 的right: -220px;

因为不这样设置 会遮挡middle的内容

<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>圣杯布局</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .header,
    .footer {
      height: 100px;
      line-height: 100px;
      background-color: green;
      text-align: center;
      font-size: 30px;
      font-weight: bolder;
    }

    .footer {
      background-color: goldenrod;
    }

    .container {
      padding: 0 220px 0 200px;
      overflow: hidden;
    }

    .left,
    .middle,
    .right {
      position: relative;
      float: left;
      min-height: 130px;
      word-break: break-all;
    }

    .left {
      margin-left: -100%;
      left: -200px;
      width: 200px;
      background-color: red;
    }

    .right {
      margin-left: -220px;
      right: -220px;
      width: 220px;
      background-color: green;
    }

    .middle {
      width: 100%;
      background-color: blue;
    }
  </style>
</head>

<body>
  <div class="header">header</div>
  <div class="container">
    <div class="middle">
      <h4>middle</h4>
      <p>
        middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
        middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
        middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
        middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
        middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
        middlemiddlemiddlemiddlemiddle
      </p>
    </div>
    <div class="left">
      <h4>left</h4>
      <p>
        leftleftleftleftleftleftleftleftleftleftleftleft
        leftleftleftleftleftleftleftleftleftleftleftleft
        leftleftleftleftleftleftleftleftleftleftleftleft
      </p>
    </div>
    <div class="right">
      <h4>right</h4>
      <p>
        rightrightrightrightrightrightrightrightrightright
        rightrightrightrightrightrightrightrightrightright
        rightrightrightrightrightrightright
      </p>
    </div>
  </div>
  <div class="footer">footer</div>
</body>

</html>


🧩 一、整体结构

<body>
  <div class="header">header</div>
  <div class="container">
    <div class="middle">中间内容</div>
    <div class="left">左侧栏</div>
    <div class="right">右侧栏</div>
  </div>
  <div class="footer">footer</div>
</body>

逻辑:

三栏的顺序是 middle → left → right(中间的内容在 DOM 最前)。

.header.footer 是头部和底部;

.container 是内容区,里面有三栏;

🧱 二、布局目标

区块宽度位置颜色
left固定 200px左侧红色
middle自适应宽度中间蓝色
right固定 220px右侧绿色

🎯 三、核心 CSS 分析

1️⃣ .container

.container {
  padding: 0 220px 0 200px;
  overflow: hidden;
}

👉 这行是关键!

它给中间内容预留了:

  • 左边 200px 的内边距
  • 右边 220px 的内边距

所以 .middle(中间栏)虽然宽度是 100%,但不会挡住左右栏的位置。
相当于先给中间栏挖出左右的“空位”。

2️⃣ 三栏都 float: left;

.left, .middle, .right {
  position: relative;
  float: left;
}

这使得三栏都能横向排列。

3️⃣ .middle

.middle {
  width: 100%;
  background-color: blue;
}

中间栏占满容器(因为 float 的 100% 是相对于父容器 .container 的宽度)。
此时会被左右栏挤压,但还没调整左右栏位置。

4️⃣ .left

.left {
  margin-left: -100%; /* 拉回到最左边 */
  left: -200px;       /* 再向左移动 200px 宽度 */
  width: 200px;
  background-color: red;
}

这两句是圣杯布局的“魔法”:

  • margin-left: -100%
    → 把左栏往左推一个“整行宽度”,使它脱离原本 float 排列的顺序;
  • left: -200px
    → 再往左移动 200px,让它真正靠在容器的最左侧;
  • 最终效果:左栏覆盖在 .middle 的左边区域。

5️⃣ .right

.right {
  margin-left: -220px; /* 把右栏往左拉进 middle 区域 */
  right: -220px;       /* 再往右挪出容器 */
  width: 220px;
  background-color: green;
}

原理与左栏相似,只不过方向相反:

  • 先通过 margin-left: -220px 把自己往左“插入”;
  • 再通过 right: -220px 拉到容器右侧。

🔍 四、工作原理小结(顺序推演)

  1. middle 先浮动,占据整行宽度;
  2. left 紧跟在后,用 margin-left: -100% 把自己推到最左边;
  3. right 再用 margin-left: -220pxright: -220px 推到最右;
  4. container 通过 padding 预留中间空间,使中间栏内容不会被左右遮住。

最终:

| 左栏(200px) | 中栏(自适应) | 右栏(220px) |

🧩 五、为什么要这么复杂?

因为早期(CSS2 时代)还没有 flexgrid
只能靠 floatmargin 技巧实现三栏自适应布局。

圣杯布局的优点:

  • 中间栏内容优先;
  • 左右宽度固定;
  • 不使用绝对定位;
  • IE6+ 时代也兼容。

⚙️ 六、现代写法替代(推荐)

现在我们用 Flexbox 实现只需几行代码👇:

.container {
  display: flex;
}

.left {
  width: 200px;
  background: red;
}

.middle {
  flex: 1;
  background: blue;
}

.right {
  width: 220px;
  background: green;
}

✅ 代码简单、语义清晰、完全兼容现代浏览器。


🧠 总结一句话

圣杯布局通过 float + 负 margin + relative 位移 + padding 预留空间 实现“三栏布局”,
在现代 CSS 出现之前,它是网页结构中最经典的布局技巧之一。


双飞翼布局

双飞翼布局和圣杯布局很类似,不过是在middle的div里又插入一个div,通过调整内部div的margin值,实现中间栏自适应,内容写到内部div中。

这样可以先做好主体部分,然后再将附属部分放到合适的位置!

  1. 首先把left、middle、right都放出来, middle中增加inner
  2. 给它们三个设置上float: left, 脱离文档流;
  3. 一定记得给container设置上overflow: hidden; 可以形成BFC撑开文档
  4. left、right设置上各自的宽度
  5. middle设置width: 100%;

接下来与圣杯布局不一样的地方:

  1. left设置 margin-left: -100%, right设置 right: -rightWidth;
  2. container设置padding: 0, rightWidth, 0, leftWidth;
<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>双飞翼布局</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .header,
    .footer {
      height: 100px;
      line-height: 100px;
      background-color: green;
      text-align: center;
      font-size: 30px;
      font-weight: bolder;
    }

    .footer {
      background-color: goldenrod;
    }

    .container {
      overflow: hidden;
    }

    .left,
    .middle,
    .right {
      float: left;
      min-height: 130px;
      word-break: break-all;
    }

    .left {
      margin-left: -100%;
      width: 200px;
      background-color: red;
    }

    .right {
      margin-left: -220px;
      width: 220px;
      background-color: green;
    }

    .middle {
      width: 100%;
      height: 100%;
      background-color: blue;
    }

    .inner {
      margin: 0 220px 0 200px;
      min-height: 130px;
      background: blue;
      word-break: break-all;
    }
  </style>
</head>

<body>
  <div class="header">header</div>
  <div class="container">
    <div class="middle">
      <div class="inner">
        <h4>middle</h4>
        <p>
          middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
          middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
          middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
          middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
          middlemiddlemiddlemiddlemiddlemiddlemiddlemiddle
          middlemiddlemiddlemiddlemiddle
        </p>
      </div>
    </div>
    <div class="left">
      <h4>left</h4>
      <p>
        leftleftleftleftleftleftleftleftleftleftleftleft
        leftleftleftleftleftleftleftleftleftleftleftleft
        leftleftleftleftleftleftleftleftleftleftleftleft
      </p>
    </div>
    <div class="right">
      <h4>right</h4>
      <p>
        rightrightrightrightrightrightrightrightrightright
        rightrightrightrightrightrightrightrightrightright
        rightrightrightrightrightrightright
      </p>
    </div>
  </div>
  <div class="footer">footer</div>
</body>

</html>

总结

圣杯布局在DOM结构上显得更加直观和自然;

双飞翼布局省去了很多css,而且由于不用使用定位,可以获得比圣杯布局更小最小宽度;

说到这里需要注意一下 由于双飞翼布局会一直随着浏览器可视区域宽度减小从而不断挤压中间部分宽度。

所以需要设置给页面一个min-width > LeftWidth + RightWidth;


还有一件事就是他们在单独部分内容扩充的时候,童鞋们可能发现了 底部会参差不齐。

在我的老师那里知道了最简单的解决办法 / 笑哭

给left、middle、right设置上 padding-bottom: 9999px; margin-bottom: -9999px;

就让他变得无限高,但是又给他送回去了。真的是让我脑洞大开!!!

padding-bottom: 9999px; margin-bottom: -9999px;

是通过 巨大的内边距撑出背景 + 负外边距抵消布局高度
来实现浮动列视觉上底部对齐、背景等高的经典 CSS Hack。

✅ 看起来底部对齐,❌ 实际内容高度没变。

Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注