> For the complete documentation index, see [llms.txt](https://yes-1-am.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yes-1-am.gitbook.io/blog/css/liang-lie-bu-ju-san-lie-bu-ju.md).

# 两列布局，三列布局

## 前言

一直以来对于布局自适应问题，都没有好好总结，趁着最近面试，忙碌之余也有空闲，便打算开始整理整理相关知识

## 1. 两列布局

### 1.1 float + BFC (左边固定，右边自适应)

```markup
<div>
  <div style="float: left; width: 200px; background: red;">
    left
  </div>
  <div style="background:green; overflow:hidden;">
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
  </div>
</div>
```

单独设置 左 元素 `float: left` 时，该元素脱离文档流。此时 右 元素会占据 左 元素的位置，并被 左 元素遮挡。但文字会环绕 左 元素。

此时，可通过让 右 元素形成 BFC，打破环绕。

### 1.2 float + margin-left (左边固定，右边自适应)

```markup
<div>
  <div style="float: left; width: 200px; background: red;">
    left
  </div>
  <div style="background:green; margin-left: 200px;">
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
  </div>
</div>
```

原理同上，通过 `margin-left` 使得 右 元素打破环绕。

### 1.3 absolute + margin-left  (左边固定，右边自适应)

```markup
<div>
  <div style="position: absolute; width: 200px; background: red;">
    left
  </div>
  <div style="background:green; margin-left: 200px;">
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
    i am right; i am right; i am right;
    <br />
  </div>
</div>
```

同 float + margin-left 的方案，既然 float 可以使得左边元素脱离文档流，那么 absolute 也可以脱离文档流。

这个时候可以使用 margin-left 与左边元素隔开距离，但是使用 overflow: hidden; 不可以。

### 1.4 absolute + absolute (左或右自适应宽度均可)

```markup
<div style="position: relative;">
  <div style="position: absolute; left: 0; width: 200px;background: red;">
    left
  </div>
  <div style="position: absolute; left: 200px; right: 0;background: blue;">
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
  </div>
</div>
```

通过给两个子元素设置为 absolute, 绝对定位来自适应宽度。

### 1.5 float + 负外边距 (左或右自适应宽度均可)

#### 1.5.1 左边自适应,右边定宽

```markup
.clearfix {
    content: "";
    display: block;
    clear: both;
}

<div class="clearfix">
  <div style="float: left; width: 100%;">
    <div style="margin-right: 200px;background: red;">
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
    </div>
  </div>
  <div style="float: left;background: blue; width: 200px; margin-left: -200px;">
    I’m the Sidebar!
  </div>
</div>
```

左右 元素均设置为左浮动，同时 左 元素宽度为 100%, 这时候 右 元素会被 **挤** 到下一行，而通过 margin-left 为 负的自身宽度(-200px), 又使得 左右 元素又在同一行。

但是这个时候， 左 元素会遮挡 右 元素，因此我们将真正内容作为 左 元素的子元素，设置子元素的 margin-right 确保与 右 元素隔开。

#### 1.5.2 左边定宽，右边自适应

```markup
<div class="clearfix">
  <div style="float: left;background: blue; width: 200px; margin-right: -100%;">
    I’m the Sidebar!
  </div>
  <div style="float: left; width: 100%;">
    <div style="margin-left: 200px;background: red;">
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
      Main content in here;
    </div>
  </div>
</div>
```

左右两个元素设置为 float: left, 右 元素设置宽度为 100%, 这时候左右元素分为两行。

设置左 元素 margin-right 为 -100%, 将右 元素又拉回到和 左 元素同一行。

这时候，右 元素会把 左 元素遮挡，那么通过设置 `margin-left` 和 左 元素拉开距离。

### 1.6 flex 布局 (左或右自适应宽度均可)

```markup
<div style="display: flex;">
  <div style="width: 100%;background: red;">
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
  </div>
  <div style="background: blue; flex: 0 0 200px;">
    I’m the Sidebar!
  </div>
</div>
```

缺点:\
1\. 需要注意IE兼容性

通过设置 `flex: 0 0 200px`, 即 不伸长，不收缩，宽度为 200px

### 1.7 table (左边固定宽度，右边自适应)

```markup
<table>
  <tbody>
    <tr>
      <td width="200px" style="background: red;">i am the sidebar</td>
      <td style="background: blue;">
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
        Main content in here;
      </td>
    </tr>
  </tbody>
</table>
```

利用 table 布局，在宽度固定的 td 标签上设置 width，另外一个元素自然会占据剩余宽度。

优点:\
1\. 无需多余的 css 来实现自适应 2. 垂直方向会自动居中

缺点:\
1\. 很多多余的 HTML 结构嵌套

### 1.8 table-cell 伪表格布局 (左边固定宽度，右边自适应)

```markup
<div>
  <div style="display: table-cell;width: 200px;background: red;">
    left
  </div>
  <div style="display: table-cell;background: blue;">
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
    Main content in here;
  </div>
</div>
```

设置子元素为 table-cell, 可以实现和 table 一样的效果

## 2. 三列布局

### 2.1 圣杯布局

为了主内容区最先显示，因此在 dom 结构 middle 需要设置在最前面。

```markup
<div class='clearfix' style="padding: 0 200px 0 100px;">
  <div style="float: left;background: blue;width: 100%;">middle</div>
  <div style="float: left;background: red;width: 100px;margin-left: -100%;position: relative;left: -100px;">left</div>
  <div style="float: left;background: green;width: 200px;margin-left: -200px;position: relative;left:200px;">right</div>
</div>
```

三列元素，都设置为 float: left, 这时候，三个元素会凑在一起。设置 middle 宽度为 100%, 这时候 middle 独占一行，left 与 right 相连，在第二行。

left 元素设置 margin-left 为 -100%, -100% 相对于 父元素宽度，此时 left 与 middle 同一行，且与 middle 左边对齐。

同理 right 元素设置 margin-left 为 (-自身的宽度), 则 right 与 middle 同一行，且与 middle 右边对齐。

三个元素同在一行，但是此时的问题是，left 会遮挡 middle 的左边，right 会遮挡 middle 的右边，即 **middle 的宽度过宽**， 那么我们可以设置 三个元素的共同父元素的 padding。

设置完 padding 之后, middle 的位置合适，但是此时 left 和 right 都有一定的偏移，这时候可以将 left, right 设置为 relative, 并移动对应的距离即可。

缺点:\
1\. 在屏幕变小，middle 宽度小于 left 宽度时，布局会被破坏

### 2.2 双飞翼布局(淘宝对于圣杯布局的改进)

```markup
<div class='clearfix'>
  <div style="float: left;background: blue;width: 100%;">
    <div style="padding: 0 200px 0 100px;">  <!-- 使用 margin 也行 -->
      middle
    </div>
  </div>
  <div style="float: left;background: red;width: 100px;margin-left: -100%;">left</div>
  <div style="float: left;background: green;width: 200px;margin-left: -200px;">right
  </div>
</div>
```

同样是 float + 负 margin 的做法，放弃了 relative 定位的方式。而是通过增加一层 dom 结构，设置 padding 的值， 使得真正的内容显示在中间区域。

优点:\
1\. 解决了圣杯布局出现的，屏幕小时，布局被破坏的问题

缺点: 1. 需要多余的 dom 层级结构

### 2.3 绝对定位

```markup
<div style="position: relative;">
  <div style="background: red;width: 100px;position: absolute; left: 0; top: 0;">left</div>
  <div style="background: blue;margin: 0 200px 0 100px;">  <!-- padding 也行 -->
    middle
  </div>
  <div style="background: green;width: 200px;position: absolute;right: 0; top: 0;">right
  </div>
</div>
```

优点: 1. 左 中 右 dom 结构的位置顺序可以随意调整

缺点:\
1\. 左 中 右 的实际高度由 middle 决定，如果 left / right 高于 middle，此时 left / right 会遮挡 footer 区域

### 2.4 浮动定位

```markup
<div style="position: relative;">
  <div style="background: red;width: 100px; float: left;">left</div>
  <div style="background: green;width: 200px; float: right;">right</div>
  <div style="background: blue;margin: 0 200px 0 100px;"> <!-- padding 也行 -->
    middle
  </div>
</div>
```

同绝对定位一样，也就是使得 左 右 元素脱离文档流，middle 元素使用 padding 或 margin 与两个元素隔出距离。

缺点:\
1\. middle元素必须出现在 left 和 right 元素之后，不利于主题内容的呈现

### 2.5 flex 布局

```markup
<div style="display: flex;">
  <div style="background: red; flex: 0 0 100px;">left</div>
  <div style="background: blue; flex: 1 1 auto;">
    middle
  </div>
  <div style="background: green; flex:0 0 200px;">right
  </div>
</div>
```

优点:\
1\. 左 中 右 元素的 dom 结构顺序可以通过设置 order 来处理，很灵活

```markup
<div style="display: flex;">
  <div style="background: blue; flex: 1 1 auto;">
    middle
  </div>
  <div style="background: red; flex: 0 0 100px; order: -1;">left</div>
  <div style="background: green; flex:0 0 200px;">right
  </div>
</div>
```

缺点: 1. 需要注意 flex 在 IE 下的兼容性

## 参考资料

1. [CSS 布局 - 两栏自适应布局的几种实现方法汇总](https://zhuanlan.zhihu.com/p/54367529)
2. [CSS：两栏布局，三栏布局](https://blog.csdn.net/crystal6918/article/details/55224670)
3. \[\[布局]聊聊为什么淘宝要提出「双飞翼」布局

   ]\(<https://github.com/zwwill/blog/issues/11>)
4. [双飞翼布局介绍-始于淘宝UED-三列布局](http://www.imooc.com/wenda/detail/254035)
