CSS常见的几种布局方式

未知宽高元素的水平垂直居中

<div class="container">
  <div class="ele">hello world</div>
</div>

/* 为了便于观察,先设置一些css样式:  */
.container {
  width: 200px;
  height: 200px;
  background-color: #eee;
}
.ele {
  background-color: rgb(179, 174, 174);
}

将下面任一种方法的css样式分别添加到上面的代码中,都能得到下图这个效果。

方法一:绝对定位 + 平移

.contaienr {
  position: relative;
}
.ele {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  // 若宽高已知,还可以使用margin-left,margin-right代替
}
// 若是ele指定了宽高,还可以设置距上下左右都为0再自动间隔
// 若没有设置宽高就直接使用的话,ele的宽高都会是父元素的100%
.ele {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: auto;
  width: 100px;
  height: 100px;
}

方法二:tabel布局

.container {
  display: table-cell;
  text-align: center;
  vertical-align: middle;
}
.ele {
  display: inline-block;  // 可选,若不加则ele的宽度将是父元素的100%
}

方法三: flex布局

.container {
  display: flex;
  justify-content: center;
  align-items: center;
}

元素宽高成比例

比如说实现一个宽度自适应,高度为宽度一半的矩形。

padding

利用 padding 设置为百分比时是相对于父元素的宽度而言的,所以可以给矩形设置一个父元素,由父元素来控制矩形的大小。而矩形的宽度设置为 100,高度通过 padding-bottom: 50% 撑开就行了。需要注意的是要同时将矩形的 height 设置为 0,以此将矩形的高度完全交给 padding 负责(如果要设置成宽高比成其他比例比如 4:3 的话也是同理,修改 padding-bottom 的百分比就可以了)。

<div class="parent">
  <div class="child"></div>
</div>

.parent {
  background-color: red;
  width: 200px;
  height: 200px;
}
.child {
  width: 100%;
  padding-bottom: 50%;
  height: 0;
  background-color: blue;
}

使用 padding 有个问题是,因为给矩形设置了 height: 0,所以如果这个矩形有子元素并且设置了 height: 100% 的话,子元素的高度也会是 0 而不是预期的 100px。这时需要给子元素绝对定位,给矩形相对定位才可以让子元素的高度保持和矩形一致(此时子元素脱离了文档流,宽高就相对于父元素的宽高来了)。

vw

使用 vw 来给矩形的宽高设置为成比例的 vw 值。这个方法的问题在于 vw 是相对于屏幕宽度而言,不同设备有不同的宽度,所以无法控制矩形具体的宽度值。

rem

给 html 根元素设置一个 font-size 值,矩形通过 rem 单位设置宽高,这样它的宽高就是相对于根元素的字体大小而言的,再给矩形的宽高设置成比例的 rem 值就可以了。

两栏等高

padding内补偿法

<div class="box">
  <div class="left">
    <p>左浮动</p>
    <p>左浮动</p>
    <p>左浮动</p>
  </div>
  <div class="right">
    <p>也是左浮动</p> 
    <p>也是左浮动</p> 
    <p>也是左浮动</p> 
    <p>也是左浮动</p> 
    <p>也是左浮动</p> 
    <p>也是左浮动</p> 
  </div> 
</div>

.box { 
  overflow: hidden;
}
 .left {
  width: 50px;
  float: left;
  background-color: yellow;
  padding-bottom: 2000px;
  margin-bottom: -2000px;
}
.right {
  width: 100px;
  float: left;
  background-color: red;
  padding-bottom: 2000px;
  margin-bottom: -2000px;
}

两栏等高的原理是,给每一栏都设置一个很大的padding-bottom的值,以此来弥补高度较小的那一栏。再使用一个等大的负的margin-bottom来收缩,像在另一篇文章里我解释的,使用负margin-bottom收缩后,虽然元素自身的宽高没有改变,但它的元素框会随着减小,最终元素框的高度取决于两栏中较高一栏。这时如果给它们的父元素添加overflow: hidden,则会隐藏掉溢出元素框的部分内容,于是就达到了高度较低的一栏和较高的一栏等高的效果。

但是使用padding内补偿法有一个缺陷,如果每一栏设置了border的话,因为每一栏的高被padding撑高而且隐藏了超出的内容,所以border-bottom是显示不出来的。解决办法是,再在每一栏的最后一个子元素设置一个div来模仿border-bottom。记得还要给父元素box设置为相对定位,才能让伪border-bottom定位到那一栏的底部。

.border {
  width: 52px;
  height: 2px;
  background-color: blue;
  position: absolute;
  bottom: 0;
}

使用div模仿border-bottom还是有一个限制,就是必须先设置好那一栏的宽度,才好给border设置width。使用width: calc(100% + 2px)是没用的,因为每一栏都没有相对定位,所以100%是参考box而言的。如果要给每一栏设置相对定位,则border的绝对定位是相对于每一栏而言的,显示的边框又会被隐藏掉了。

tabel布局

利用表格(table)中每个单元格(table-cell)等高的特性

// IE8以下不支持
.box { 
  display: table;
  overflow: hidden;
}
.left {
  width: 50px;
  background-color: yellow;
  display: table-cell;
}
.right {
  width: 100px;
  background-color: red;
  display: table-cell;
}

flex布局

.box {
  display: flex;
}
.left {
  background-color: blue;
  width: 100px;
}
.right {
  background-color: yellow;
}

js / jq动态设置

在 js 代码里去获取box里每一栏的高度并取它们的最大值,再将每一栏的高度设置为该最大值。但这不可避免会操作到DOM,难免对性能造成影响。

两栏布局

左栏固定宽度,右栏自适应。

利用负margin

实现逻辑请看盒模型和负margin这篇文章里的解释。

<!-- content放在slider后面 -->
<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.box {
  overflow: hidden
}
.slider {
  float: left;
  width: 100px;
  height: 200px;
  background-color: blue;
}
.content {
  float: left;
  width: 100%;
  height: 200px;
  background-color: yellow;
  margin-right: -100px;
}
.content p {
  margin-right: 100px;
}
<!-- content放在slider前面 -->
<div class="box">
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
  <div class="slider">
    <p>slider</p>
  </div>
</div>

.slider {
  float: left;
  width: 100px;
  height: 200px;
  background-color: blue;
  margin-left: -100%;
}
.content {
  float: left;
  width: 100%;
  height: 200px;
  background-color: yellow;
}
.content p {
  margin-left: 100px;
}
// 或者更简单点,使用BFC 
<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.slider {
  float: left;
  width: 100px;
  height: 200px;
  background-color: rgba(0, 0, 255, 0.2);
}
.content {
  width: calc(100% - 100px);
  height: 200px;
  background-color: yellow;
  // 使用BFC阻止content被浮动元素覆盖
  overflow: hidden;
}

绝对定位

两栏都绝对定位并使用calc计算自适应那一栏的宽度

<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.box {
  position: relative;
}
.slider {
  width: 100px;
  height: 200px;
  background-color: blue;
  position: absolute;
  left: 0;
}
.content {
  width: calc(100% - 100px);
  height: 200px;
  background-color: yellow;
  position: absolute;
  left: 100px;
}

tabel布局

利用表格的宽度等于所有单元格宽度之和的特性

<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.box {
  display: table;
  width: 100%;
}
.slider {
  width: 100px;
  display: table-cell;
  background-color: blue;
}
.content {
  display: table-cell;
  background-color: yellow;
}

flex布局

flex大法真香

<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.box {
  display: flex;
}
.slider {
  background-color: blue;
  width: 100px;
}
.content {
  background-color: yellow;
  flex: 1;
}

grid布局

<div class="box">
  <div class="slider">
    <p>slider</p>
  </div>
  <div class="content">
    <p>content</p>
    <p>content</p>
    <p>content</p>
    <p>content</p>
  </div>
</div>

.box {
  display: grid;
  grid-template-rows: 100px;
  grid-template-columns: 100px auto;
}
.slider {
  background-color: blue;
}
.content {
  background-color: yellow;
}

三栏布局

跟两栏布局差不多,三栏布局是左右两栏固定宽度,中间栏自适应宽度(即固比固布局)。

浮动

<div class="box">
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
</div>
<!-- 注意,中间元素要放最后,使左右两栏先渲染,否则右栏会被挤压在下面 -->

.content {
  background-color: blue;
}
.inner-content {
  margin-left: 100px;
  margin-right: 100px;
}
.left {
  float: left;
  width: 100px;
  background-color: red;
}
.right {
  float: right;
  width: 100px;
  background-color: yellow;
}

从效果图中可以看出,使用浮动实现三栏布局在中间栏容纳得下内容时是可以正常工作的,但内容一旦超出,中间栏就会向下扩展。如果给它们指定 height 的话自然就不存在这问题了,不过内容还是会溢出来的(见下图)。当然我们可以选择给外层容器box设置overflow: hidden来隐藏,但如果我们要显示全部内容的话要怎么办?答案是给左中右三个元素使用padding内补偿法,使它们三栏等高。(当浏览器宽度小于左右两栏的宽度和时,右栏会被挤下来,这时要也只能去给 body 设置min-width了,这点跟圣杯布局一样)。

绝对定位

<div class="box">
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

.box {
  position: relative;
}
.content {
  background-color: blue;
}
.inner-content {
  margin-left: 100px;
  margin-right: 100px;
}
.left {
  width: 100px;
  background-color: red;
  position: absolute;
  left: 0;
  top: 0;
}
.right {
  width: 100px;
  background-color: yellow;
  position: absolute;
  right: 0;
  top: 0;
}

也存在内容大于中间栏宽度,中间栏会向下扩展的问题。

flex布局

<div class="box">
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

.box {
  display: flex;
}
.content {
  background-color: blue;
  flex: 1;
}
.left {
  width: 100px;
  background-color: red;
}
.right {
  width: 100px;
  background-color: yellow;
}

对高度会自适应,内容超出时三栏会自动调整高度。

tabel布局

<div class="box">
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

.box {
  display: table;
  width: 100%;
}
.content {
  background-color: blue;
  display: table-cell;
}
.left {
  width: 100px;
  background-color: red;
  display: table-cell;
}
.right {
  width: 100px;
  background-color: yellow;
  display: table-cell;
}

对高度会自适应,内容超出时三栏会自动调整高度。

grid布局

<div class="box">
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

.box {
  display: grid;
  grid-template-columns: 100px auto 100px;
}
.content {
  background-color: blue;
}
.left {
  background-color: red;
}
.right {
  background-color: yellow;
}

对高度会自适应,内容超出时三栏会自动调整高度。

双飞翼布局

<div class="box">
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

.left {
  width: 100px;
  height: 100px;
  background-color: red;
  float: left;
  margin-left: -100%;
}
.right {
  width: 100px;
  height: 100px;
  background-color: yellow;
  float: left;
  margin-left: -100px;
}
.content {
  width: 100%;
  height: 100px;
  float: left;
  background-color: blue;
}
.inner-content {
  margin-left: 100px;
  margin-right: 100px;
}

同样是使用浮动,但双飞翼布局是对负margin的应用,实现详情请看负margin实现两栏布局。同样的是,在HTML代码部分也是把content放到left和right前面的优先渲染 ,否则left放前面的话会造成left被content覆盖或是内容超出视图,也存在前面的那两个问题。听说双飞翼布局是淘宝创造的?

圣杯布局

<div class="box">
  <div class="content">
    <div class="inner-content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div>
  <div class="left">
    <p>我是左导航栏我是左导航栏我是左导航栏</p>
  </div>
  <div class="right">
    <p>我是右导航栏我是右导航栏我是右导航栏</p>
  </div> 
</div>

body {
  min-width: 400px;
}
.box { 
  padding: 0 100px 0 100px;
}
.left {
  width: 100px;
  height: 100px;
  background-color: red;
  float: left;
  margin-left: -100%;
  position: relative;
  left: -100px;
}
.right {
  width: 100px;
  height: 100px;
  background-color: yellow;
  float: left;
  margin-left: -100px;
  position: relative;
  right: -100px;
}
.content {
  width: 100%;
  height: 100px;
  float: left;
  background-color: blue;
}

圣杯布局和双飞翼布局同样是对负margin的应用,区别在于解决中间栏内容不被左右侧边栏遮挡的方法不同。双飞翼布局是直接给content设置一个子div,利用margin-left和margin-right来间隔开彼此。而圣杯布局给父容器box设置padding来间隔开content主体内容和左右的距离,再给左右两栏分别相对定位设置left和right使其回到原先的位置。圣杯布局潜在的问题是,但浏览器缩小到一定程度后,左右两栏会被挤下来,不过可以给body设置min-width来解决这个问题。个人比较推荐双飞翼布局,两者实现的效果是一样的,而圣杯布局又要用到相对定位甚至min-width,为什么不简单点直接用margin解决同样的问题呢,不仅思路简单些代码也简洁。

三栏布局总结

  • 普通的浮动布局和双飞翼布局圣杯布局:
    在 HTML 代码上普通的浮动是把中间栏放到最后,左右两栏分别左右浮动后中间空出来的空间给中间栏。而双飞翼布局和圣杯布局是把中间栏放在最前面使其先渲染,让三栏都左浮动后再利用左栏margin-left: -100%,右栏margin-left: -100px使其都并排布局。因为普通浮动里是把中间栏放到最后才渲染的,所以当页面内容较多时可能会影响用户体验。

  • 对高度的适应性:
    如果没有设置高度,flex布局,table布局,grid布局对三栏的高度变化有适应性,即使内容超出了三栏会都自动扩大自身高度来适应。而浮动和绝对定位两种方法内容一旦超出,中间栏则会向下扩展,双飞翼布局也一样有这问题,可以直接设置height来避免这问题。或者使用padding内补偿法使三栏都等高。

  • 局限性:
    浮动和绝对定位脱离文档流,需要处理好和其他元素的位置关系,不过兼容性比较好。

  flex布局和grid布局是新生的布局方式,功能强大对高度的适应性好,但对浏览器的兼容性不是很好。

  table布局适应性好,对浏览器的兼容性也好,但table布局好像一直受人诟病,似乎毛病很多?我没在项目里用到过,也不知道真假。

三栏布局之固定高度中间自适应

上下固定高度,中间自适应。都需要使用overflow: scroll来使中间栏溢出的部分在内部滚动,否则中间栏会扩展自身高度把底栏挤出视图,这我也不知道有什么更好的解决办法了。

绝对定位

<div class="box">
  <div class="top">
    <p>我是头部</p>
    <p>我是头部</p>
    <p>我是头部</p>
    <p>我是头部</p>
  </div>
  <div class="content">
    <div class="inner_content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div> 
  <div class="bottom">
      <p>我是底部</p>
      <p>我是底部</p>
      <p>我是底部</p>
  </div>
</div>

.box {
  width: 100%;
  height: 100vh;
  position: relative;
}
.content {
  width: 100%;
  height: 100%;
  background-color: blue;
  overflow: scroll;
}
.top {
  width: 100%;
  height: 100px;
  background-color: red;
  position: absolute;
  left: 0;
  top: 0;
}
.bottom {
  width: 100%;
  background-color: yellow;
  position: absolute;
  left: 0;
  bottom: 0;
}
.inner_content {
  padding-top: 100px;
}

此处使用绝对定位有一个局限,需要事先知道顶部栏的高度,才好给中间栏的内容部分设置padding-top,否则内容会被顶部栏遮住。对于中间栏需要使用overflow: scroll来使溢出部分在中间栏里滚动,否则中间栏会向下扩展。

flex布局

<div class="box">
  <div class="top">
    <p>我是头部</p>
    <p>我是头部</p>
    <p>我是头部</p>
  </div>
  <div class="content">
    <div class="inner_content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div> 
  <div class="bottom">
      <p>我是底部</p>
      <p>我是底部</p>
      <p>我是底部</p>
  </div>
</div>

.box {
  width: 100%; 
  height: 100vh;
  display: flex;
  flex-direction: column;
}
.content {
  width: 100%;
  flex: 1;
  background-color: blue;
  overflow: scroll;
}
.top {
  width: 100%;
  background-color: red;
}
.bottom {
  width: 100%;
  background-color: yellow;
}

中间栏同样需要使用overflow: scroll

grid布局

<div class="box">
  <div class="top">
    <p>我是头部</p>
    <p>我是头部</p>
    <p>我是头部</p>
  </div>
  <div class="content">
    <div class="inner_content">
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
      <p>我是内容主题部分</p>
    </div>
  </div> 
  <div class="bottom">
      <p>我是底部</p>
      <p>我是底部</p>
      <p>我是底部</p>
  </div>
</div>

.box {
  width: 100%;
  height: 100vh;
  display: grid;
  grid-template-rows: 100px auto 100px;
  grid-template-columns: 100%;
}
.content {
  background-color: blue;
}
.top {
  background-color: red;
}
.bottom {
  background-color: yellow;
}

中间栏同样需要使用overflow: scroll


 上一篇
盒模型和负margin 盒模型和负margin
盒模型  每一个HTML标签元素都是由四个部分组成的,分别是内容(content)、内边距(padding)、边框(border)、外边距(margin)。其中盒子的大小只由 content + padding + b
2019-03-12
下一篇 
移动端点击的300ms延迟 移动端点击的300ms延迟
有300ms延迟的原因  之所以移动端click会有300ms的延迟,主要是为了让用户在双击屏幕时可以对视图进行放大缩小,而这300ms就是用于判断在这时间范围之内是否有第二次点击,有则双击,否则就是单击。若每次单击屏
2019-03-03
  目录