当面试官问“如何实现三栏布局?”——你会说....

634 阅读6分钟

引言

三栏布局在网页设计中非常普遍,适用于多种类型的网站,能够有效地提升用户体验和信息展示效率。作为前端开发者,在面试桌上遇到“如何实现三栏布局?”这个问题几乎是家常便饭。本文,就是你的布局宝典。在这里,我们将超越基础,不仅探讨常见的浮动(floats)、Flexbox等方法,还会介绍另外几种经典巧妙的布局方法,让你的代码灵活优雅,回答起这个问题来像喝水一样简单。

什么是三栏布局?

首先我们要清楚三栏布局的页面内容被组织成三个水平并排的列或区域,分别是左栏、中栏、右栏。传统的三栏布局中,左右两侧的栏目宽度往往是固定的,用于放置如导航菜单、广告、侧边栏信息等静态宽度的内容。而中间栏则具有自适应性,其宽度随浏览器窗口大小的变化而变化,主要用于展示主要的、动态变化的内容,如文章正文、产品列表等,并且中间栏的主体内容要优先加载

五种布局方法

  • 圣杯布局
  • 双飞翼布局
  • Flexbox布局
  • table布局(表格布局)
  • grid布局(网格布局)

1. 圣杯布局

圣杯布局的实现方式涉及CSS的浮动(floats)、负外边距(negative margins)和相对定位(relative positioning)技巧。通过将中间栏首先渲染,然后利用浮动和负外边距把左右栏“拉”到中间栏的两侧。接下来上代码!

1.1 为了能够让代表主体内容的中间栏能够被浏览器优先加载,块级元素content就要提前至HTML结构中的第一个位置,这样在页面渲染时,浏览器会优先处理和显示中间栏的内容

<body>
    <div class="page">
        <div class="content">主体内容</div>
        <div class="left">广告位</div>  
        <div class="right">广告位</div> 
    </div>
</body>

1.2 为了在两边空出广告栏空间,设置父容器即page的左右内边距200px;同时按照正常文档流来说,left和right应该紧接content之后,但因位置不够被content挤下去了

        * {
            margin: 0;
            padding: 0;
        }
        .page {
            height: 200px;
            padding: 0 200px; /*上边距和下边距为0像素,左边距和右边距为200像素*/
            
        }
        .left,.right {
            height: 200px;
            width: 200px;     /*固定左右栏目宽度*/
            background-color: rgb(124, 215, 124);
        }
        .page div {
            float: left;     /*将三个容器全部浮动去到同一行*/
        }
        .content {
            width: 100%;
            height: 200px;
            background-color: pink;
        }

目前效果如下: 屏幕截图 2024-05-23 100819.png

1.3 接下来再针对left和right进行位置的调整

        .left {
            margin-left: -200px; /*先使它进入父容器里面紧靠右侧*/
            position: relative; 
            left: -100%;         /*左移父容器的100%*/
        } 

         .right {
            margin-left:-200px;  /*与left同理*/
            position: relative; 
            right: -200px;       /*右移200px*/
        } 

最终效果如下: 屏幕截图 2024-05-23 103721.png

2、双飞翼布局

2.1 content还是要放至第一位,但与圣杯布局不同的是content里还有一块inner,这块inner才是用来放主体内容的

    <div class="page">
        <div class="content">
            <div class="inner">主体内容</div>
        </div>
        <div class="left">广告位</div>
        <div class="right">广告位</div>
    </div>

2.2 父容器page不需要再设内边距,content宽度占父容器的100%。但left和right还是因位置不够被content挤下来了

   * {
        margin: 0;
        padding: 0;
    }
    .page {
        height: 200px;
    }
    .left,.right {
        height: 200px;
        width: 200px;
        background-color: rgb(150, 171, 205);
    }
    .content {
        height: 200px;
        background-color: #93b3ab;
        width: 100%;
    }
    .page > div {  /*只有page下一级的div会被float影响,所以inne块不会浮动*/
        float: left;
    }    

目前效果如下: 屏幕截图 2024-05-23 105609.png

2.3 接下来调整inner、left和right的位置:设置inner外边距,再移动left和right盖在content上,但不会盖住inner

    .inner {
        margin: 0 200px;  /*外边距上下0,左右200px*/
        height: 200px;
    }
    .left {
        margin-left: -100%; /*原本紧挨父容器外边右侧,向左移动父容器的100%*/
    }
    .right {
        margin-left: -200px; /*left走了之后,原本紧挨父容器外边右侧,向左移动200px*/
    }

最终效果如下: image.png

3、flex弹性布局

3.1 body部分:这部分与1.1一样,不再赘述

3.2 给父容器page设置成弹性布局,那么子容器content、left、right全都排一行,且默认和父容器一样高

        * {
            margin: 0;
            padding: 0;
        }
        .page {
            height: 200px;
            display: flex; /*父容器设置为弹性容器*/
        }
        .left,.right {
            width: 200px;
            background-color: #f1d370;
        }
        .content {
            background-color: #c2ec92;
        }

目前效果如下:

屏幕截图 2024-05-23 113033.png

3.3 那么问题来了,主体内容content宽度这么窄,而且放的位置还不是中间,这我们该怎么解决?

既然我们已经将父容器page设置成了弹性容器,那就可以接着通过flex属性允许content放大,直至父容器宽度被撑满;再通过order属性设置left、content和right的渲染次序分别为0、1、2,这样就能使left先被渲染放在最左边了。(注意!由于content在HTML结构上还是处于第一位,所以content还是会被优先加载,order只是把left在页面上的位置提到前面了)

补充flex属性小知识:flex属性综合了flex-grow、flex-shrink和flex-basis三个单独属性。 flex-grow默认值为0,意味着不增长;flex-shrink默认值为1,意味着如果父容器空间不足,子容器会等比例缩小;flex-basis分配初始大小。

flex: 1 简写形式,相当于 flex: 1 1 0; 表示可以等比例增长和缩小,初始大小为0,实际上会根据内容自动调整

        .content {
            background-color: #c2ec92;
            flex: 1; /*允许放大*/
            order: 1; 
        }
        .left {
            order: 0; /*弹性布局中,告诉浏览器优先渲染,即把它位置排到前面*/
        }
        .right {
            order: 2;
        }

最终效果如下: 屏幕截图 2024-05-23 120707.png

4、table布局

4.1 注意表格布局body部分的content不能提升至HTML结构上的第一位

<body>
    <div class="page">
        <div class="left">广告位</div>
        <div class="content">主体内容</div>
        <div class="right">广告位</div>
    </div>
</body>

4.2 display: table将父容器page转换成一个表格结构, table-layout: fixed限制单元格列宽不能自动根据单元格内容决定,而是由已经定好的宽度属性width决定

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .page {
        height: 200px;
        width: 100vw;
        display: table;    /*将page转换成一个表格结构*/
        table-layout: fixed; /*表示列宽由自身决定*/
    }
    .page > div {
        display: table-cell; /*设置成表格单元*/
    }
    .left,.right {
        width: 200px;
        height: 200px;
        background-color: #f1d370;
    }
    .content {
        width: 100%;
        height: 200px;
        background-color: #a4deb7;
    }
    </style>

最终效果如下: image.png

5、grid布局

5.1 body部分:这部分与4.1一样,不再赘述

5.2 网格布局的css代码特别优雅,直接把父容器display: grid,再通过属性grid-template-columns设置left、right为200px,中间content自适应就结束了

<style>
    * {
        margin: 0;
        padding: 0;
    }
    .page {
        height: 200px;
        width: 100vw;
        display: grid;
        grid-template-columns: 200px auto 200px;
    }
    .left,.right {
        height: 200px;
        background-color: #f1d370;
    }     
    .content {
        height: 200px;
        background-color: #c2ec92;
    }
</style>

最终效果如下: image.png

总结

①圣杯布局和双飞翼布局的区别:圣杯是靠content的内边距留出左右两边广告栏位置,而双飞翼是靠inner块的外边距留位置 ②Flexbox布局才是最推荐使用的 ③表格布局和网格布局都无法实现主体内容优先加载,但是网格布局胜在代码精简优雅,若要生成九宫格之类的布局是很推荐的