两列布局,三列布局

前言

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

1. 两列布局

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

<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 (左边固定,右边自适应)

<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 (左边固定,右边自适应)

<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 (左或右自适应宽度均可)

<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 左边自适应,右边定宽

.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 左边定宽,右边自适应

<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 布局 (左或右自适应宽度均可)

<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 (左边固定宽度,右边自适应)

<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 伪表格布局 (左边固定宽度,右边自适应)

<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 需要设置在最前面。

<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 双飞翼布局(淘宝对于圣杯布局的改进)

<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 绝对定位

<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 浮动定位

<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 布局

<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 来处理,很灵活

<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. [[布局]聊聊为什么淘宝要提出「双飞翼」布局

    ](https://github.com/zwwill/blog/issues/11)

Last updated