CSS布局之三栏布局

2,860 阅读7分钟

前言

面试高频考点,之前就记了个比较有名的圣杯布局,没想到面试官问了我上下栏固定、中间自适应的布局,措手不及。借此弥补下CSS布局方面的知识。

(以下代码我都在浏览器上跑过,基本能够实现布局要求。)

上下固定,中间自适应

在网上找了下关于上中下布局的文章,发现资料很少,所以总结的不是很好,如果大家有更完整、更优美的办法可以在评论区留言。

<div class="top-center-bottom">
        <div class="top">top</div>
        <div class="center">center</div>
        <div class="bottom">bottom</div>
</div>

position: absolute 定位

.top-center-bottom>div{
  position:absolute;
}
.top{
  top:0;
  height:100px;
  width:100%;
  background:red;
}
.bottom{
  bottom:0;
  height:100px;
  width:100%;
  background:red;
}
.center{
  bottom:100px;
  top:100px;
  width:100%;
  overflow: auto;
  background:green;
}

overflow 属性规定当内容溢出元素框时发生的事情。

overflow: auto; 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。 overflow: hidden; 内容会被修剪,并且其余内容是不可见的。

使用弹性盒子flex

.top-center-bottom{
    display: flex;
    height: 100%; /*要指定高度*/
    flex-direction: column; /*布局从上到下排列*/
}
.top,.bottom{
    display: flex;  /*这是为居中 文字的*/
    justify-content: center;  /*文字 水平居中*/
    align-items: center;   /*文字垂直居中*/
}
.top{
    height: 100px;
    background: #665aa4;
}
.center{
    flex-grow: 1; /*不知道和 flex: 1 有啥区别*/
    text-align: center;
    background: #3dc95d;
}
.bottom{
    height: 100px;
    background: #fc430b;
}

flex-direction:colum; 使整体布局从上到下排列

flex-grow:1; 应用于main,使得main自动填充剩余空间

flex-grow的使用避免了当main内容过少时footer部分会被提升到页面上方的问题(传统方式上可能需要靠绝对定位来解决了~)

其他可做参考的写法:

  <div class="container">
    <div class="flex-item">
      <div style="height: 100px;line-height: 100px;">top</div>
    </div>
    <div class="flex-item">
      <div style="height: 800px;color: #fff;">
        <div class="center">center</div>
      </div>
    </div>
    <div class="flex-item">
      <div style="height: 100px;line-height: 100px;">bottom</div>
    </div>
  </div>
html, body{
  height:100%;
  margin: 0;
  padding: 0;
}
.container{
  height: 100%;
  display: flex;
  flex-direction: column;
  text-align: center;
}
.flex-item:nth-child(1), flex-item:nth-child(3){
  flex-grow:0;
  flex-shrink: 0;
  background-color: #ababab;
}
.flex-item:nth-child(2){
  flex-grow:1;
  flex-shrink: 1;
  background-color: #000;
  overflow-y: auto;
}
.center{
  position: fixed;
  top: 50%;
  left: 50%;
  margin-top: -10px;
  margin-left: -24px;
}

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 autoflex-basis指定了 flex 元素在主轴方向上的初始大小。

该属性有两个快捷值: auto (1 1 auto):元素会根据自身的宽度与高度来确定尺寸 none (0 0 auto):元素会根据自身宽高来设置尺寸

table / grid

因为太多了我也记不住,记住前两个常用的应该就可以了。

左右固定,中间自适应

绝对定位法

<div class="left">Left</div>
<div class="main">Main</div>
<div class="right">Right</div>
<!--简单的进行CSS reset-->
body,html{
    height:100%;
    padding: 0px;
    margin:0px;
}
<!--左右绝对定位-->
.left,.right{
    position: absolute;
    top:0px;
    background: red;
    height:100%;
}
.left{
    left:0;
    width:100px;
}
.right{
    right:0px;
    width:200px;
}
<!--中间使用margin空出左右元素所占据的空间-->
.main{
    margin:0px 200px 0px 100px;
    height:100%;
    background: blue;
}

缺点:如果中间栏含有最小宽度限制,或是含有宽度的内部元素,当浏览器宽度小到一定程度,会发生层重叠的情况。

圣杯布局

    <div class="container">
        <!-- 优先渲染 -->
        <div class="center">
            center
        </div>
        <div class="left">
            left
        </div>
        <div class="right">
            right
        </div>
    </div>

使三栏浮动,并相对定位,给左右两个容器设置200px的宽度、中间的容器设置100%的宽度。

body,html {
    height:100%;
    padding: 0;
    margin: 0
}
    .container {
        padding: 0 200px;
        height: 100%;
    }
    .container > div {
        position: relative;
        float: left;
        height: 100%;
    }
    .center {
        width: 100%;
        background-color: red;
    }
    .left {
        width: 200px;
        left: -200px;
        margin-left: -100%;
        background-color: green;
    }
    .right {
        width: 200px;
        right: -200px;
        margin-left: -200px;
        background-color: blue;
    }

步骤:

  1. 由于浮动的关系,给 left 设置margin-left: -100%即可使左侧栏浮动到 center 上面,并位于 center 左侧之上。
  2. 同样为 right 设置margin-left: -200px,这里的长度等于 right 的长度
  3. 这时 center 的两侧被 left 和 right 覆盖了,因此给 container设置padding: 0 200px
  4. 由于 left 和 right 也同时往中间缩,因此给 left 和 right 分别设置left: -200px; right: -200px,往两侧分别偏移自身的宽度去覆盖掉 contaniner 使用padding后的空白位置。

相关解释如下:

  1. 中间部分需要根据浏览器宽度的变化而变化,所以要用100%,这里设左中右向左浮动,因为中间100%,左层和右层根本没有位置上去
  2. 把左层margin负100后,发现left上去了,因为负到出窗口没位置了,只能往上挪
  3. 按第二步这个方法,可以得出它只要挪动窗口宽度那么宽就能到最左边了,利用负边距,把左右栏定位
  4. 但由于左右栏遮挡住了中间部分,于是采用相对定位方法,各自相对于自己把自己挪出去,得到最终结果

缺点:在窗口变小到限定值时布局会乱掉,需要给它加上一个宽度限制min-width。而且圣杯布局实际看起来是复杂的后期维护性也不是很高,在淘宝UED的探讨下,出来了一种新的布局方式就是双飞翼布局。

双飞翼布局

双飞翼布局给center 部分包裹了一个main,通过设置margin主动地把页面撑开,为左右两栏div留出位置。这时窗口宽度过小时就不会出现混乱的情况了。

<!-- 优先渲染 -->
    <div class="center">
        <div class="inner">
            center
        </div>
    </div>
body,html {
    height:100%;
    padding: 0;
    margin: 0
}
.container {
    height: 100%;
}
.container > div{
        float: left;
        height: 100%;
    }
    .center {
        width: 100%;
        background-color: red;
    }
    .left {
        width: 200px;
        margin-left: -100%;
        background-color: green;
    }
    .right {
        width: 200px;
        margin-left: -200px;
        background-color: blue;
    }
    /*新增*/
    .inner {
        margin: 0 200px;
    }

圣杯和双飞翼布局的不同在于解决“中间栏div内容不被遮挡”问题的思路不一样:

圣杯布局为了中间div内容不被遮挡,将父容器设置了左右padding-leftpadding-right后,将左右两个div用相对布局position:relative并分别配合right和left属性,以便左右两栏div移动后不遮挡中间div;

双飞翼布局为了中间div内容不被遮挡,直接在中间div内部创建子div用于放置内容,在该子div里用margin-leftmargin-right为左右两栏div留出位置。增加多一个div就可以不用相对布局了,只用到了浮动和负边距。

浮动

//注意元素次序
<div class="left">Left</div>
<div class="right">Right</div>
<div class="main">Main</div>
body,html {
    height:100%;
    padding: 0;
    margin: 0
}
<!--左栏左浮动-->
.left {
    background: red;
    width: 100px;
    float: left;
    height: 100%;
}
<!--中间自适应-->
.main {
    background: blue;
    height: 100%;
    margin:0px 200px 0px 100px;
}
<!--右栏右浮动-->
.right {
    background: red;
    width: 200px;
    float: right;
    height: 100%;
}

flex布局

<div class="flex">
    <div class="left">left</div>
    <div class="main">middle</div>
    <div class="right">right</div>
</div>
.flex {
    display: flex;
    flex-flow: row;
}
.left{
    width: 180px;
    height: 100px;    
    background-color: red;
}
.main{
    flex: 1; 
    height: 100px;
    background-color: blue;
}
.right {
    width: 200px;
    height: 100px;
    background-color: green;
}

总结

左中右布局的话记住三种应该就没问题了,圣杯和双飞翼布局需要理解,别像我面试的时候只憋出来一个圣杯,还忘了具体怎么实现......都是泪。

还有浏览器的适配问题,因为被问到了,头疼......大家有时间的话自己可以搜一下,之后有时间的话我再补充吧,应付面试的话以上方法应该就够了。