如何实现左边固定宽度,右边自适应?

13 分钟读完

前言

这是今天(8月29号)在美团面试中遇到的一个问题,这个问题其实很经典,我在准备面试的时候看到这题总是很自然的略过了,因为我之前有在项目中实现过一次,所以很自信。

然而在面试中却暴露出了问题,对清除浮动概念掌握不准确,对float布局的应用不够了解,对flex布局了解不详细。导致我的回答比较糟糕。面试回来后反复在网上搜了几篇介绍这个问题的博客(质量不一),然后自己实现,测试了一下,做个梳理。

解决思路

首先来分析一下问题:左边固定宽度,右边自适应。

我觉得这个问题的关键点在于右边自适应,在CSS布局中,什么情况下可以实现宽度(在一行中)自适应?答案是:

  • 动态地计算宽度
  • 或者display:block + width: auto

动态地计算宽度,然后让两个子元素float,设置父元素清除浮动,这样就可以实现,但是这样需要多一次计算操作,每次页面重流影响到该元素时都会计算一次,所以性能不好,但还是放上实现代码供参考:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .wrapper {
            background-color: blueviolet;
            overflow: hidden;
        }
        .left {
            background-color: crimson;
            width: 300px;
            float: left;
        }
        .right {
            background-color: blue;
            width: calc(100% - 300px);
            float: left;
        }
        .footer {
            background-color: aqua;
            height: 200px;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <div class="left">
            <div style="height: 300px"></div>
        </div>
        <div class="right">
            <div style="height: 200px"></div>
        </div>
    </div>
    </div>
    <div class="footer"></div>
</body>

</html>

如果是使用块级元素自适应,需要考虑一个问题:题目的要求是右边的自适应只占据一部分宽度,而不是整整一行,怎么实现?其实问题很简单,就是在左边留出来一块非内容区,当然,你可以用margin留,也可以用padding留,但是逻辑上来讲,我们想要的其实是右边的盒子宽度等于窗口宽度 - 左边固定宽度。所以margin从逻辑上更能表现这一点,在某些情况下,padding更像是属于盒子内部。

留出来足够的空间后,左边如何设置呢?

首先左边肯定是要在右边之上的,即脱离了右边所在的文档流,脱离当前文档流的方式:float,绝对定位。

然么基本思路确定了就来分别看看两种处理方式:

float + margin-left:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .wrapper {
            background-color: blueviolet;
            overflow: hidden;
        }
        .left {
            background-color: crimson;
            float: left;
            width: 300px;
        }
        .right {
            background-color: blue;
            margin-left: 300px;
        }
        .footer {
            background-color: aqua;
            height: 200px;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <div class="left">
            <div style="height: 300px"></div>
        </div>
        <div class="right">
            <div style="height: 200px"></div>
        </div>
    </div>
    </div>
    <div class="footer"></div>
</body>

</html>

解释一下这段代码:

左边设置固定宽度,浮动,右边用margin-left留出相应的距离,另外为了防止浮动元素高于块级元素,溢出容器,我们可以对父容器设置清除浮动,我这里就使用了overflow设为hidden,创建一个BFC,然后清除浮动的方式。

absoulte+margin-left:

这个就不写代码了,这种方式的缺点在于,你不能向浮动那样写clearfix让左右内容高度相同,除非你给左边设置固定高度限制。

flex方法

flex布局中,flex项(item)有flex-grow属性,这个属性是item放大的比例系数,具体的含义可以看这篇关于flex的文章Flex-弹性布局原来如此简单!!

实现思路是,我们设置左边固定宽度的item,然后设置它不放大也不缩小,右边设置一个放大系数flex-grow属性,这样只有右边的放大了,于是就占满了所有的剩余空间。

实现代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .wrapper {
            display: flex;
            background-color: blueviolet;
        }

        .left {
            background-color: crimson;
            flex: 0 0 300px;
        }

        .right {
            background-color: blue;
            flex-grow: 1;
        }


        .footer {
            background-color: aqua;
            height: 200px;
        }
    </style>
</head>

<body>
    <div class="wrapper">
        <div class="left">
            <div style="height: 300px"></div>
        </div>
        <div class="right">
            <div style="height: 200px"></div>
        </div>
    </div>
    </div>
    <div class="footer"></div>
</body>

</html>

本文的例子都可以复制新建一个html文件,然后查看结果。

更新时间: