日拱一卒:CSS 布局

133 阅读7分钟

flex布局

flex基本使用

弹性布局如何完成

假设我们有一个父级容器,设置了固定的高度和宽度,分别为100px和100px,我们先不给子盒子设置宽度、高度... 总之是任何盒模型该有的东西:

.outer {
    width: 100px;
    height: 100px;
    background-color: pink;
}
.outer div {
    background-color: lightblue;
}
 <div class = "outer">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
    <div>7</div>
    <div>8</div>
    <div>9</div>
</div>

那么我们会得到效果是:

image.png

可以看到,100px * 100px的父级盒子被完全遮盖。

  • 块级盒子在内联方向(主轴)扩展,并占据父元素在该方向上所有的可用空间。
  • 每个盒子都会换行展示。
  • 在高度(交叉轴方向)上,默认由内容撑开。

默认情况下,块级盒子有width: auto;和height:auto两个属性。如果想设置宽、高、内外边距、可以对块级元素通过设置width和height属性来完成;如果子元素超出了父元素的宽高范围,可以通过设置overflow:hidden或者overflow:auto来完成。

如果仅仅为父级元素设置display:flex,会产生什么效果呢?

image.png

这是因为,display:flex默认的主轴是水平方向,它按照你的浏览器的默认语言方向,即从左到右,排成一排。如果仅设置display:flex,不设置任何其它flex属性配合,那么仅会导致容器在主轴方向上排列子元素。

超过了父级元素的范围,在flex中怎么处理

在进一步学习flex属性之前,我们可以先想一个问题,如果我们给子元素设置了盒属性,子元素的宽、高、margin使得它超过了父级元素的范围,在flex中,是如何处理呢?确实,我们也可以像上面说的一样,设置overflow:auto或者overflow:hidden属性。但在flex中,还提供了一个属性,可以使我们在垂直于主轴方向上,对多余的元素进行排列:

.outer {
    display: flex;
    flex-wrap: wrap;
    width: 100px;
    height: 100px;
    background-color: pink;
}
.outer div {
    background-color: lightblue;
    margin: 2px;
}

image.png

  1. 对于定高宽的父元素,子元素的高、宽、margin不定,由内容撑开。对父元素设置flex属性,可以让子元素在垂直于主轴方向

image.png 2. 如果子元素的宽、高、margin定下来,那么很可能子元素超过父元素的范围。此时可以使用flex-wrap:wrap。

.outer {
    display: flex;
    flex-wrap: wrap;
    width: 100px;
    height: 100px;
    background-color: pink;
}
.outer div {
    background-color: lightblue;
    margin: 2px;
}

左:不设置flex-wrap; 右:设置flex-wrap. image.png

flex属性

  1. justify-content: 在主轴方向上,元素该如何排列。常用的有:space-between、space-around、flex-start、flex-end、center。
  2. align-items: 在交叉轴方向,元素该如何排列。
  3. flex-direction: row是按行书写,column是按列书写。

总结

为什么使用flex?

  • 子元素超过父级元素宽高:overflow -> flex-wrap
  • 子元素在主轴方向排列:内容撑开 -> 设置width和height和margin -> justify-content
  • 子元素水平居中:margin:auto -> justify-content: center
  • 子元素垂直居中:
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%)

-> align-items: center;

flex常见场景

flex: 1 三栏布局

在前端面试中,经常会被问到这样一个问题:三栏布局如何实现?flex:1是什么属性的集合?

三栏布局,就是左边和右边宽度固定,中间自适应的一种布局方式。 我们先快刀阔斧,把左右宽度定下来:

 .box {
    display: flex;
    justify-content: space-between;
    width: 100%;
    height: 200px;
    background-color: pink;
}
 .left,.center,.right {
    background-color: lightblue;
}
.left,.right {
    width:50px;
}
 <div class = "box">
    <div class = "left">11</div>
    <div class = "center">11</div>
    <div class = "right">11</div>
</div>

会发现效果是这样的:

image.png 中间宽度怎么自适应呢?其实答案就在上方:为中间元素设置flex:1。这是一个无单位的比例值,表示每个 flex 项沿主轴的可用空间大小。 怎么理解这个比例呢?如果给左右各设置flex:1,中间设置flex:2,那么会按照比例,将可用空间分配给这三个元素,中间占1/2,左右各占1/4

.center {
    background-color: white;
    flex:2;
}
.left,.right {
    flex:1; 
    background-color: lightblue;
}

image.png

这时候仍然存在问题,就是:当中间内容过多的时候,会挤压两边边框,而不是自动换行:

image.png 这是因为连续字符串没有断点,即使换行也没有地方可以换,对纯英文文本,需要设置空格或者换行符号:

image.png

对中文文本,一个字就可以进行一次换行: image.png

这是文本的特性,即没有换行符出现时,会一直排列下去;当且仅当超过父元素的边框且有换行符时候,进行换行。

image.png

再回答第二个问题:flex:1是什么的缩写?

  • flex-grow: 1; 是上述自适应场景中,使用到的无单位比例。
  • flex-shrink: 1; 为0时,不能进行压缩,适用于固定宽高的场景。
  • flex-basis: 1;

flex实现九宫格

.outer {
    display: flex;
    justify-content: space-around;
    align-items: center;
    flex-wrap: wrap;
    width: 100px;
    height: 100px;
    background-color: pink;
    margin-bottom: 10px;
}
.outer div {
    background-color: lightblue;
    width: 30px;
    height:30px;
}

image.png

圣杯布局和双飞翼布局

耳熟能详了有没有!那为什么有了flex这两种布局方式仍然存在呢?原来,除了能够两边宽度固定,这两种布局方式还能保证中间主体内容优先加载(flex肯定要放在第二个!) 话不多说,来实现一下吧!

圣杯布局
  1. 首先我们先实现简陋的架构,把左右两侧定宽300px,中间内容宽度定为100%:
.holyGail .header, .holyGail .footer {
    background-color: yellow;
    width: 100%;
}

.holyGail .center {
    background-color: pink;
    width: 100%;
}

.holyGail .left, .holyGail .right {
    background-color: lightblue;
    width: 300px;
}

 <div class="holyGail">
    <div class="header">导航</div>
    <div class="content">
        <div class = "center">内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</div>
        <div class = "left column">左边</div>
        <div class = "right column">右边</div>
    </div>
    <div class="footer column">尾部</div>
</div>

image.png

  1. 然后给left,right,center都设置float,一点点魔法:
.column {
    float: left;
}

结果是:

image.png 尾部内容这么多,是因为大小被撑开 image.png

文档流是相对于盒子模型的;文本流是相对于文字段落的。文本会认同浮动元素所占据的区域,因此文字会出现在浮动元素右边,并且撑起整个尾部盒子。

  1. 给尾部盒子清除浮动带来的影响
 <div class="footer" style="clear:both">尾部</div>

image.png

  1. 在content里面留出left和right:
.content {
    padding-left: 300px;
    padding-right: 300px
}

image.png

  1. 将left移到center左边
.holyGail .left{
    background-color: lightblue;
    width: 300px;
    margin-left: -100%;
}

image.png

设置margin-left : -100%,将元素的左侧外边距设置为其父容器宽度的100%的负值。 正常来讲,如果说center不设置宽度为父元素的100%,左边和右边的元素一定会在center元素的右方浮动,如图是设置center为50%的时候: image.png 而将外边距设置为-100%,就是让left向左移动一个父元素宽度的内容.(如果center50%的话,移动肯定是要50%)

在之后,再将元素相对自己本身的位置左移300px:

 .holyGail .left{
    background-color: lightblue;
    width: 300px;
    margin-left: -100%;
    position: relative;
    left: -300px
}

在不设置position的时候,元素默认是static的定位是正常文档流进行排列,设置relative之后,可以相对本身的位置进行左右移动。

image.png

  1. 将right移到center右边
.holyGail .right {
    background-color: lightblue;
    width: 300px;
    margin-right: -300px
}

image.png

最终代码为:

<div class="holyGail">
    <div class="header">导航</div>
    <div class="content">
        <div class = "center column">内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</div>
        <div class = "left column">左边</div>
        <div class = "right column">右边</div>
    </div>
    <div class="footer" style="clear:both">尾部</div>
</div>
.holyGail .header, .holyGail .footer {
    background-color: yellow;
    width: 100%;
}

.holyGail .content {
    padding-left: 300px;
    padding-right: 300px
}

.holyGail .center {
    background-color: pink;
    width: 100%;
}

.holyGail .left{
    background-color: lightblue;
    width: 300px;
    margin-left: -100%;
    position: relative;
    left: -300px
}
.holyGail .right {
    background-color: lightblue;
    width: 300px;
    margin-right: -300px
}

.column {
    float: left;
}