# 两列布局，三列布局

## 前言

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

## 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yes-1-am.gitbook.io/blog/css/liang-lie-bu-ju-san-lie-bu-ju.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
