从左图右文到自适应两列布局

2,358 阅读9分钟

错位的左图右文

在网页设计中,常要考虑图文排版,比如设计以下内容:

1.png

表面上,由于左边图片是内联元素,则右边的文字只要设置为inline-block,则应该可以实现左图右文的排版。但是实际上,得出的效果如下:

image-20211205212606365.png

很明显,这不符合预期。产生错位的原因是vertical-align属性影响到行框盒子的对齐,具体可以参考CSS深入理解vertical-align和line-height的基友关系。解决方法比较简单,只要在右边的css中添加vertical-align:top就可以解决。左图右文的设计属于两列布局,那常见的两列布局方式有哪些呢?下面我想总结一下左侧固定宽度,右侧自适应的两列布局方案,以便加深印象。

左侧固定宽度,右侧自适应的两列布局

左侧固定宽度,右侧自适应的两列布局的关键在于如何实现右侧自适应宽度。通常,有以下三种方式实现自适应宽度:

  1. 利用块级元素的流体特性,自动填充可用空间
  2. 利用calc函数,计算出可用空间后设置对应的宽度
  3. flex布局和grid布局中,利用flexgrid布局的空间分配策略确定宽度

下面,我将尝试从正常流布局、浮动布局、定位布局、弹性盒子布局及网格布局阐述左侧固定宽度,右侧自适应的两列布局。涉及的html结构为:

    <div class="container">
        <div class="left">left</div>
        <div class="right">right</div>
    </div>

先来看看正常流布局

正常流布局

在正常流布局中,需要借助inline-block属性实现。

        /* display:inline-block布局 */
​
        .container {
            /* 消除inline-block之间的空格 */
            font-size: 0;
        }
​
        .left {
            display: inline-block;
            /* 解决类似图片错位的问题 */
            vertical-align: top;
            width: 200px;
        }
​
        .right {
            display: inline-block;
            /* 右边宽度=容器宽度-左侧宽度 */
            width: calc(100% - 200px);
            vertical-align: top;
        }

关键点:

  1. 通过在父元素中使用font-size: 0;消除inline-block之间的间隙,还有其他办法,可以参考display:inline-block元素之间空隙的产生原因和解决办法
  2. 通过在左右两侧设置vertical-align: top;解决类似图片错位问题
  3. 通过calc函数计算右侧宽度,实现自适应宽度

这种方案的缺点:

  1. 需要知道左侧盒子的宽度,两个盒子的距离,这点问题就很严重了,特别是当盒子还有外边距时,使用calc就缺乏灵活性了。
  2. 若是有边框和内边距还要设置元素的box-sizing
  3. 需要消除空格字符的影响,若采用font-size: 0;方式,还要重新设置左右两侧的font-size
  4. 需要设置vertical-align: top实现行框盒子顶端对齐。

image.png

浮动布局

浮动布局主要有双float方案、float+BFC方案、float+margin方案。

float方案

        /* 双float布局 */
        .container {
            /* 消除高度塌陷问题 */
            overflow: hidden;
        }
​
        .left {
            width: 200px;
            float: left;
        }
​
        .right {
            width: calc(100% - 200px);
            float: left;
        }

关键点:

  1. 通过calc函数计算右侧宽度,实现自适应宽度
  2. float元素之间不会有空隙,所以不需要设置font-size:0

缺点:

  1. float元素具有高度塌陷的特性,所以父元素应该要消除塌陷,具体方法可以参考Techniques for Clearing Floats
  2. 由于floatinline-block元素一样具有包裹性,所以使用calc函数计算右侧的自适应宽度,缺乏灵活性。

float+BFC方案

        /* float+BFC 布局 */
        .container {
            overflow: hidden;
        }
​
        .left {
            width: 200px;
            float: left;
        }
​
        .right {
            /* 使用table-cell实现BFC */
            display: table-cell;
            width: 9999px;
        } 

关键点:

  1. 使用BFC布局避免和浮动元素重合,然后利用元素的流体特性实现自适应宽度,比calc灵活得多,不用计算确定的宽度
  2. 实现bfc的方式很多,具体可以参考CSS深入理解流体特性和BFC特性下多栏自适应布局
  3. 若在左右两侧中间设置间距,则可以直接在左侧(浮动侧)设置外边距或者在右侧设置内边距。

缺点:

  1. float元素具有高度塌陷的特性,所以父元素应该要消除塌陷

float+margin方案

        /* float+margin 布局 */
        .container {
            overflow: hidden;
        }
​
        .left {
            width: 200px;
            float: left;
        }
​
        .right {
            margin-left: 200px;
        }

关键点:

  1. 直接利用块级元素的流体特性,实现右侧宽度自适应。方案很简洁
  2. 当然,也可以在右侧设置为inline-block,然后配合calc函数计算自适应宽度,只是这种方案比直接利用块级元素的流体特性又增加了一层计算。

缺点:

  1. float元素具有高度塌陷的特性,所以父元素应该要消除塌陷
  2. 右侧使用margin-left要计算出左侧预留空间,类似calc方案一样,缺乏灵活性

其他float方案

上述的float方案都是基于左侧浮动元素,当然,你也可以使用右侧浮动元素配合calc实现自适应宽度。比如左侧设置为inline-block配合右侧设置为flaot方案,但是通常来说,浮动元素是具有包裹性,不具有流动性,所以尽量放在固定宽度的位置,而自适应的位置通过具有流体特性的元素填充。

        /* 左侧inline-block,右侧为float 布局 */
        .container {
            overflow: hidden;
        }
​
        .right {
            float: right;
            width: calc(100% - 200px);
        }
​
        .left {
            width: 200px;
            display: inline-block;
        }

定位布局

定位布局主要是借助绝对定位实现的,因为绝对定位和浮动元素一样,具有包裹性而没有流体特性,所以类似浮动布局一样,使用绝对定位代替浮动元素填充左侧空间。但是值得注意的绝对定位之间不会互相排斥,直接仿照双float布局实现的双绝对定位布局是没用的,同时,绝对定位完全脱离文档流,和BFC也不会相互排斥,因此直接仿照float+bfc方案实现绝对定位+bfc方案也不行。首先,我们来看看双绝对定位布局:

双绝对定位布局

        /*左右均是绝对定位布局 */
        
        .container {
            position: relative;
            height: 200px;
        }
​
        .left {
            width: 200px;
            position: absolute;
        }
​
        .right {
            /* 要使用定位属性确定右侧起始位置 */
            left: 200px;
            width: calc(100% - 200px);
            position: absolute;
        } 

注意点:

  1. 首先要确定左侧布局的宽度,然后右侧绝对定位要使用定位属性确定起始位置
  2. 父元素要使用相对定位布局确定好参照

缺点:

  1. 父元素需要设置为相对定位,代码繁杂些
  2. 绝对定位无法清除高度塌陷的问题,所以只能在父元素上设置好高度。有时候无法直接计算出高度,就要借助js动态计算了。
  3. 右侧布局需要获得左侧布局宽度,然后使用定位属性确定起始位置
  4. 使用calc计算出右侧自适应宽度,增加计算量。

总的来说,这种布局没啥用,类似绝对定位+BFC方案也是类似上述操作,基本不会用。

绝对定位+margin方案

​
        /* 绝对定位+margin布局 */
        .container {
            position: relative;
            height: 200px;
        }
​
        .left {
            width: 200px;
            position: absolute;
        }
​
        .right {
            margin-left: 200px;
        } 

注意点基本和双绝对定位一样,只是不需要设置右侧为绝对定位,而是使用块元素流动性实现自适应宽度

缺点:这种布局对应float+margin方案没啥优点,一是要设置父元素为相对定位,第二高度塌陷问题无法直接解决,一旦左侧空间布局高度超过父元素设定高度,就会造成布局错乱。我觉得用这种布局还不如直接用flaot的那几种布局方式。

弹性盒子布局

弹性盒子布局在我的文章flex布局是真的香!中讨论过这种方案的一些布局策略,相当灵活。在实现自适应两列布局中,可以直接利用flex-grow属性灵活做到。

        /* flex布局 */
​
        .container {
            display: flex;
        }
​
        .left {
            /* 无收缩伸张特性 */
            flex: 0 0 200px;
        }
​
        .right {
            /* 占据所有剩余空间 */
            flex: 1 1 0;
        }

注意点:

  1. 在父元素上设置display:flex启动弹性盒子布局
  2. 左侧取消收缩伸张

缺点:

  1. 兼容不了IE6-9

image-20211206120246558.png

  1. 弹性盒子相比上述三种方案较为复杂

优点:

  1. 对比inline-block方案,无需设置vertical-align: top;,而且flex布局下子元素之间不会形成空隙,因此也不用font-size: 0;,最后也不用借助calc函数实现自适应宽度
  2. 对比float系列布局方案和绝对定位布局方案,无需考虑高度塌陷问题,且不用考虑左侧空间宽度。

网格布局

网格布局和弹性盒子布局一样,都是利用两种布局的空间分配策略实现自适应。但是网格布局相比于弹性盒子,父元素先设置好网格模板,让子元素填充即可,所以代码集中在父元素上,实现起来更加简洁。

        /* grid布局 */
​
        .container {
            display: grid;
            /* 设置两列,左边固定宽度,右边占据剩余所有空间 */
            grid-template-columns: 200px 1fr;
        }

缺点:

  1. 这种方案就是兼容性不太好,不兼容主流低版本的浏览器

image-20211206121208843.png

总结

本文主要讨论了两列自适应布局的多种常见方案,主要是利用三种方式实现自适应宽度:

  1. 利用块级元素的流体特性,自动填充可用空间:对应布局有float+margin方案、float+BFC方案和绝对定位+margin方案。
  2. 利用calc函数,计算出可用空间后设置对应的宽度:对应布局有双inline-block方案、双float方案、双绝对定位布局方案。
  3. flex布局和grid布局中,利用flexgrid布局的空间分配策略确定宽度:对应的布局有flex布局和grid布局。

总的来说,flex布局和grid布局是最高效灵活的布局方案,在兼容性允许的情况下可以优先考虑,其次,float+BFC方案也是较为灵活的,可以在兼容性允许的时候重点考虑使用。