css布局篇——双栏布局与三栏布局

12,912 阅读14分钟

近些年,随着前端技术的不断发展,前端所扮演的角色越来越重要,职责也越来越大。前端早已不仅仅做简单的内容展示页面,也不仅仅负责简单的表单交互了。日益繁杂的前端技术下,JavaScript 在我们心中占据着日益重要的地位,而相应的,很多人开始忽略了 html 和 css。

JavaScript 固然重要,但是回过头来想,作为一名前端工作者,难道我们可以避免和页面打交道吗?答案是,否!

那么,假设给你一个设计稿,我们又该如何去着手呢?

本篇章主讲双栏布局和三栏布局的一些较为古老及实用的原理及实现。说它古老主要也是因为我们不会涉及 flex 这一类魔法一般的布局方式,虽然我也知道用 flex 就能轻松实现很多布局。

做这个文章,一方面也是自己做个学习记录,当做温习一番。另一方面,也希望能够帮助看到这篇文章的新手一些启发,开启更加深入的探索之路。

双栏布局

双栏布局非常常见,往往是以一个定宽栏和一个自适应的栏并排展示存在。比如:

双栏布局
双栏布局

实现双栏布局也很简单,接下来介绍简单的 float + margin 实现方法。

假设左边栏固定,右边栏自适应。

思路

  • 使用 float 左浮左边栏
  • 右边模块使用 margin-left 撑出内容块做内容展示

实现

html 内容结构

<body>
    <div class="box">
        <div class="left">左边</div>
        <div class="right">右边</div>
    </div>
    内容内容内容
</body>

css 规则

.left {
    float: left;
    width: 200px;
    background-color: gray;
    height: 400px;
}
.right {
    margin-left: 210px;
    background-color: lightgray;
    height: 200px;
}

效果

双栏布局
双栏布局

Emmm...双栏实现了,但是下面的其他内容怎么跑上去了?答案是我们使用了浮动。

浮动

浮动会导致元素脱离原来普通的文档流。元素可以向左或者向右浮动,直到接触到包含框或者其他框为止。在原来文档流中,体现的就是好像在原来位置被删除了似的。

因此,以上浮动导致了父元素高度塌陷,其他内容块会自动排版上去。解决的办法有:

  1. 增加清除浮动元素或者伪元素清除浮动,相关元素添加清除样式:

    clear:both;
    
  1. BFC(Block Formatting Context),块级格式化上下文。BFC 规定了内部的块级元素的布局方式。使用 BFC 可以用来包含浮动元素。常见的做法是为父元素添加:

    overflow:hidden;
    

创建一个 BFC

消除浮动的其中一个方式就是创建 BFC。然而,创建 BFC 不仅仅只有**“overflow:hidden;”**一种方式,事实上,我们可以通过这几种方法显示触发 BFC:

  • float 的值不为 none。
  • overflow 的值不为 visible。
  • position 的值不为 relative 和 static。
  • display 的值为 table-cell, table-caption, inline-block 中的任何一个。

回归正题,我们的双栏布局(事实上其他涉及浮动的布局也一样)父元素高度塌陷的问题的得以解决,比如,我们只需要为 .box 元素添加样式:

overflow: hidden;

创建 BFC 后的双栏效果

消除了浮动的双栏布局
消除了浮动的双栏布局

三栏布局

三栏布局也是我们常常会使用到的布局之一。它的特点主要是:两边定宽,中间自适应。

通过position + margin实现三栏布局

思路

  1. 父元素相对定位,通过绝对定位将左右两栏固定
  2. 通过 margin 设置左右边距,留出内容块

实现

html 内容结构

<div class="box">
    <div class="left">左边</div>
    <div class="middle">中间</div>
    <div class="right">右边</div>
</div>

设置 css 规则

.box {
    position: relative;
}
.left {
    position: absolute;
    top: 0;
    left: 0;
    width: 200px;
    height: 200px;
    background-color: gray;
}
.right {
    position: absolute;
    top: 0;
    right: 0;
    width: 200px;
    height: 200px;
    background-color: gray;
}
.middle {
    margin-left: 210px;
    margin-right: 210px;
    background-color: lightgray;
    height: 200px;
}

效果

position+margin实现三栏布局
position+margin实现三栏布局

通过浮动实现三栏布局

思路

  1. 左右两栏使用 float 浮动到相应位置
  2. 中间栏通过 margin 属性进行撑开

实现

html 内容结构

<div class="box">
    <div class="left">左边</div>
    <div class="right">右边</div>
    <div class="middle">中间</div>
</div>

设置 css 规则

.box {
    overflow: hidden;
}
.left {
    float: left;
    background-color: gray;
    width: 200px;
    height: 200px;
}
.right {
    float: right;
    background-color: gray;
    width: 200px;
    height: 200px;
}
.middle {
    height: 200px;
    background-color: lightgray;
    margin-left: 210px;
    margin-right: 210px;
}

效果

通过浮动实现三栏架构
通过浮动实现三栏架构

缺点

当然,通过浮动实现的三栏布局有一个很明显的缺点,就是我们的代码层面上来讲 html 内容结构不正确,我们必须把.middle 元素放在最下面而不是中间位置,这是 float 所产生的的布局影响所导致的。

圣杯布局和双飞翼布局

圣杯布局最早源于发表于 2006 年的In Search of the Holy Grail · An A List Apart Article,而双飞翼布局则是源自淘宝 UED。

圣杯布局和双飞翼布局也是两边定宽,中间自适应的三栏布局。并且中间栏要放在父元素的第一儿子位置,以优先渲染。因此,有些人会说这带来了代码层面上 html 内容结构的不正确。然而,这么做的好处就是可以让更为重要的中间部分优先渲染,从这个角度来讲,这或许是更加好的结构。

圣杯布局和双飞翼布局两者既有相似的地方,也有不同的地方,接下来来比较一下。

在此之前,先来看看可爱的负边距。

负边距

为什么要讲到负边距呢?

一方面负边距是我们常常忽略的非常好用的技巧,在我们的布局方面具有非常重要的意义;另一方面这玩意儿兼容性贼好,我们也不用怕兼容性方面的问题。

说起负边距,我们可以想到的就是 margin 在正边距时候的反方向。我们可以来看看。

普通文档流的负边距

实现

html 内容结构

<div class="box">
    <div class="content">
        我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容
        我是内容我是内容我是内容我是内容
        <span class="span red">哈哈哈哈哈</span>我是内容我是内容我是内容我是内容
        我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容
        我是内容我是内容我是内容我是内容我是内容我是内容我是内容我是内容
    </div>
    <div class="other-content">
        我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块
        我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块
        我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块我是另一块
    </div>
    哈哈哈哈哈哈
    </div>

css 规则

.box {
    margin: auto;
    width: 600px;
}
.content {
    margin: -20px;
    background-color: lightgray;
}
.span {
    margin: -10px;
}
.red {
    background-color: red;
}

效果

负边距
负边距
总结

正如上面所示,我们为.content 和.span 这两个分别代表块级元素和行辈元素的东西设置了 margin 为-20px 和-10px。布局也相应产生了变化。

  • 灰色的框子,内容向左上角拉了过去。.content 下方的.other-content 文字也来到了它上面。
  • 红色行内元素,也向左边偏移,右边的元素被拉了过来。
  • 最后面没有任何设置的"哈哈哈哈哈哈"内容也依然紧跟着.other-content 元素显示。

我们不难看出,负值情况下

  • margin-top 和 margin-left 会影响自身向指定方向偏移
  • margin-right 和 margin-bottom 会影响相邻元素向指定方向偏移
  • 通过 margin 偏移后,原先空出来的位置也会由后面元素填不上。
  • 当然,块级元素和行内元素在表现上稍有不同。

另一种情况,负值 margin 也能够影响元素的宽度,前提是该元素 width 属性为 auto 的情况下。这里也给出详细例子,比方我们给一个元素左右边距为-200px。

html 内容结构

<div class="box">
    <div class="container"></div>
</div>

css 规则

.box {
    width: 200px;
    border: 1px solid black;
    margin: auto;
}
.container {
    margin: 0 -200px;
    background-color: lightgray;
    width: auto;
    height: 200px;
}

效果

负边距
负边距

当然,目前为止我们都是在普通文档流里面。

脱离文档流的负边距

绝对定位 + 负边距

假设我们对一个元素进行绝对定位,并且给相应的负边距。

实现

html 内容结构

<div class="box">
    <div class="pos">
        绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位
        绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位绝对定位
    </div>
    <div class="pos2">
        内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容
        内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容
        内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容
    </div>
</div>

css 规则

.box {
    position: relative;
    height: 200px;
    margin: 200px;
    border: 1px solid black;
}
.pos {
    width: 200px;
    height: 200px;
    position: absolute;
    margin: -20px;
    border: 1px solid gray;
}
.pos2 {
    width: 200px;
    height: 200px;
    position: absolute;
    border: 1px solid red;
}

效果图

负边距
负边距
总结

从效果上可以看出来,对于绝对定位而言,负边距在 top 和 left 位置上,会把元素往指定方向拉,而 right 和 bottom 方向则没什么影响,因为脱离了普通文档流,不会引起其他相邻元素变化。

tips: **“绝对定位+负边距”**我们也常常用来实现居中布局。

浮动 + 负边距

思考以下三个场景:

  1. 三个左浮动的块,分别设置负边距,几个浮动块会是怎样?
  2. 两个左浮动的块,左边的块左边距设置为-100%,右边的块会左移吗?
  3. 两个左浮动的块,第一个 width 占满,第二个左边距设置负值为自身 width 大小,会如何?
实现

html 内容结构

<!-- demo1 -->
<div class="box">
    <div class="float float1">1</div>
    <div class="float float2">2</div>
    <div class="float float3">3</div>
</div>
<!-- demo2 -->
<div class="box">
    <div class="float4">1</div>
    <div class="float5">2</div>
</div>
<!-- demo3 -->
<div>
    <div class="row">6</div>
    <div class="float6">2</div>
</div>

css 规则

.box {
    width: 800px;
    height: 210px;
    margin: 50px auto 10px auto;
    border: 1px solid black;
}
/* 第一个demo */
.float {
    float: left;
    margin: -50px;
    width: 200px;
    height: 200px;
}
.float1 {
    background-color: gray;
}
.float2 {
    background-color: yellow;
}
.float3 {
    background-color: red;
    margin: 0;
}

/* 第二格demo */ .float4 { background-color: gray; float: left; width: 200px; height: 200px; margin-left: -200px; } .float5 { background-color: lightgray; width: 200px; height: 200px; float: left; }

/* 第三个demo */ .row { width: 100%; height: 200px; background-color: lightgray; float: left; } .float6 { background-color: gray; float: left; width: 200px; height: 200px; margin-left: -200px; }

效果

总结

可以看出:

  1. demo1 中,1 和 2 两个元素设置了所有边距为负值,都会往左上方偏移。同时,元素 2 会在水平方向占元素 1 一半。元素 3 则会被元素 2 拉向左边,占元素 2 的四分之一。
  2. demo2 中,元素 1 左偏移了自身 width 的距离,原先位置空着,元素 2 紧接着补上。
  3. demo3 中,两个元素都是左浮动,元素 6 的 width 为 100%,当元素 2 左偏移自身 width 的距离以后,完全到了上一行。

因此,我们也可以看出,负边距对浮动的元素具有和普通文档流中同样类似的效果。并且,在后边的元素也可以通过负边距实现覆盖前边元素。

在布局中,我们就常常使用到**“float + 负边距”**的魔力。

圣杯布局

思路

  1. 左中右三个元素分别左浮动。
  2. 中间元素占据第一位置优先渲染,设置该元素 width 为 100%
  3. 左元素设置左边距为-100%以使得左元素上升一行并且处于最左位置,右元素设置左边距为自身宽度的负值使得右元素上升一行处于最右位置。
  4. 设置父元素的左右 padding 为左右两个元素留出空间,以展示中间元素内容。
  5. 设置左右元素为相对定位,左元素的 left 和右元素的 right 为内边距的宽度的负值。

实现

html 内容结构

<div class="box">
    <div class="middle">中间</div>
    <div class="left">左边</div>
    <div class="right">右边</div>
</div>

css 规则

.box {
    overflow: hidden;
    padding: 0 210px;
}
.middle {
    float: left;
    width: 100%;
    height: 200px;
    background-color: lightgray;
}
.left {
    float: left;
    width: 200px;
    height: 200px;
    background-color: gray;
    margin-left: -100%;
    position: relative;
    left: -210px;
}
.right {
    float: left;
    width: 200px;
    height: 200px;
    background-color: gray;
    margin-left: -200px;
    position: relative;
    right: -210px;
}

效果

圣杯布局
圣杯布局

双飞翼布局

思路

  1. 左中右三个元素分别左浮动。
  2. 中间元素占据第一位置优先渲染,设置该元素 width 为 100%
  3. 左元素设置左边距为-100%以使得左元素上升一行并且处于最左位置,右元素设置左边距为自身宽度的负值使得右元素上升一行处于最右位置。
  4. 设置中间元素的子元素左右边距为左右元素留空位,以展示中间元素内容。

实现

html 内容结构

<div class="box">
    <div class="middle">
        <div class="content">中间</div>
    </div>
    <div class="left">左边</div>
    <div class="right">右边</div>
</div>

css 规则

.box {
    overflow: hidden;
}
.middle {
    float: left;
    width: 100%;
}
.middle .content {
    margin: 0 210px;
    height: 200px;
    background-color: lightgray;
}
.left {
    float: left;
    width: 200px;
    height: 200px;
    background-color: gray;
    margin-left: -100%;
}
.right {
    float: left;
    width: 200px;
    height: 200px;
    background-color: gray;
    margin-left: -200px;
}

效果

双飞翼布局
双飞翼布局

总结

圣杯布局跟双飞翼布局的实现上,在前部分是一样的。同样都是左右栏定宽,中间栏自适应。采用浮动和负边距使左右栏与中间栏并排。不同之处大部分在于中间元素的的展示方式上。

圣杯布局采用父元素设置边距的方法,左右元素设置相对定位辅助。而双飞翼布局在中间采用嵌套子元素方法,通过设置子元素外边距来展示。

对比看来,双飞翼比圣杯多了一个嵌套元素,但是少了左右元素的定位。

flex 布局

在 flex 之前,无论是我们的单栏布局也好,双栏布局也罢,甚至更为复杂的三栏布局、垂直居中等等,我们一般都会借助浮动 (float)、定位(position)、边距(margin)来实现我们各种各样的布局样式,直到我们遇到了 flex。

flex(弹性布局)给了我们在布局方面更多的可能性,它可以更加简便、响应的布局我们的页面。我们常常可以利用它魔法一般的属性来打造我们想要的布局样式。并且,flex 现在几乎得到了所有的浏览器的支持,兼容性方面已经越来越不需要太过担忧了。

更多关于 flex 兼容性的问题可以查看can I use

然而,本篇章并不打算写 flex,仅仅讲解以往实现双栏和三栏布局的一些方式。于我而言,我更愿意用单独一个篇章来写 flex 这个主题,而不是放在这个主题来讲。并且之后我也会专门来写这个主题。

不多废话,这一次的分享就到这里,我们下期再见!