Web布局:浮动

发布于 大漠

把浮动放到今天来聊,可能很多小伙伴会觉得没有任何意义。会说“CSS都发展到这种程度了,Web的布局还会有谁用浮动呢”?那么真的是如此吗?浮动真的要退出CSS的舞台成为历史吗?我的答案是不可能的。不管CSS怎么发展,浮动都还有其存在的意义和作用,而且在一些场景中是离开不浮动的,最起码近几年之内是不可能的。至于为什么,请继续往下阅读。

浮动的介绍

不知道大家是否和我有同样的一个感觉,每当拿起一篇杂志文章,总能发现左边或右边有图片,文字流畅地围着图片,这就是打印世界中看到的浮动:

在Web的世界中,CSS的float设计初衷也是用来处理文本围绕图片排版的,就像在杂志布局中一样。碍于当时Web布局可用方案的局限性,聪明的CSSer才把float用于Web的布局当中,这一用就用了很多年,直到Flexbox的成熟才慢慢的看不到float用于Web布局的影子。但这并不能表明,浮动就将退出历史的舞台。因为在Web中处理文本围绕图片的排版始终是离不开浮动的。当然,如果你愿意的话,在什么时候你都还可以使用浮动来完成Web的布局,只不过在某些场景中使用浮动布局会有一定的缺陷性。

什么是浮动

既然是要聊浮动,那么我们从她的定义开如聊起吧。W3C是这样对浮动定义的:

浮动是在当前行上向左或向右移动框(盒子)。浮动最有趣的特性是内容可能沿着它的侧流动。内容沿着左浮动框的右侧向下流动,并沿着右浮动框的左侧向下流动

浮动的行为和本质

在探讨浮动的行为和本质之前,有几个概念先简单的介绍一下:

  • 文档流:在HTML中文档流即为元素从上至下排列的顺序或自左向右的排列
  • 脱离文档流:元素从正常的排列顺序中被抽离
  • 最左边/最右边:移动到父元素最左和最右是指元素往左或往右移动,直到碰到另一个浮支元素或父元素内容区的边界

在Web中,HTML文档会受到一些规则约束,特别是正常的文档流。在正常的文档流中,每个块级元素(比如divp等)垂直地堆叠在一起,从视图的顶部向下堆叠。但元素要是使用了浮动特性,元素会脱离文档流。

咱们可以这样来理解,把HTML文档当作一张白纸。当我们在一个元素上运用浮动时,就像是在该元素上添加了另一张纸:

浮动元素将会浮动到新图层上:

由于它不再是原始层(正常文档流)的一部分,下面的块元素会向上移动(非块元素表现行为有所差异),就像浮动元素从来没有在文档中存在过一样:

重要的是要理解浮动元素不再完全包含在声明它的CSS框中。它实际上浮在其他HTML元素之上。

事实上,浮动元素首先根据常规流布局,然后从常规流中取出并将其移到父元素的最右侧或最左侧(取决于应用浮动的哪个值,后续会介绍)。也就是说,如果父元素中有足够的空间容纳每个浮动元素,那么之约们就会从一个堆叠到另一个相邻。

刚才提到一个问题:“父元素中有足够的空间容纳每个浮动元素”。那么如果没有足够的之空间容纳呢?

在浮动中,如果容器没有足够空间容纳浮动元素时,那么浮动元素将会在新的一行并排,如上图右侧的图所示。

事实上,浮动的本质是浮动元素自身就是一个带有方位的display: inline-block属性。某种意义上的作用就是包裹,但浮动又无法完全等同display:inline-block,其中原因之一就是浮动的方向性:

  • display:inline-block仅仅一个水平排列方向,从左往右(也有可能从右向左,得看书写模式)
  • 浮动不受书写模式的限制可以从左往右排列,也可以从右往左排列,具体看取值

浮动的基础知识

通过前面的学习,对CSS的浮动概念有了一个基本的认识。在这一节中,我们来学习浮动的基础知识。在CSS中,浮动即是float属性。其值主要有:

float: none | left | right | inherit | inline-start | inline-end

其中leftright属于物理属性,inline-startinline-end属于逻辑属性。每个具体值的含义:

  • none:表示元素不进行浮动
  • left:元素向左浮动,元素会浮动在其所在容器的最左侧
  • right:元素向右浮动,元素会浮动在其所在容器的最右侧
  • inline-start:元素必须浮动在其容器的开始一侧,具体是哪一侧,将根据书写模式或者direction的值来决定,如果取值ltr是左侧,如果取值是rtl是右侧
  • inline-end:元素必须浮动在其容器的结束一侧,和inline-start类似,如果取值ltr是右侧,取值rtl是左侧
  • inherit:继承父元素的float属性的值

在本文只会聊leftright,而逻辑属性inline-startinline-end在本文中不会涉及到。如果感兴趣的话,可以回过头阅读CSS盒模型那章内容。

先来看两个简单的示例。假设我们有一段这样的HTML结构:

<p>
    <img class="bitey-img" src="apex-predator.jpeg"/> The alligator is a crocodilian in the genus Alligator of the family Alligatoridae. The two living species are...
</p>

分别在img上运用float:leftfloat:right,你将看到的效果如下图所示:

从效果上可以看到文本会围绕着img排版。这个效果是float最初的设计效果。

接下来再来看另一个常见的使用场景。在HTML中有多个元素在一起,并且都使用了浮动:

<div class="box-1">…</div>
<div class="box-2">…</div>
<div class="box-3">…</div>

.box-1 {
    float: left;
}

.box-2 {
    float: right;
}

.box-3 {
    float: right;
}

你将看到的效果会是如下图这样的效果:

上面这个示例也验证了我们前面提到过的,如果容器没有足够空间的容纳浮动元素时,那么浮动元素就会另起新的一行排列。比如上面这个示例,.box-1.box-2的宽度之和小于它们的父元素的宽度,所以排列在同一行,而且一个排在最左侧,另一个排在最右侧。但是.box-3加上的话,其父容器已无足够空间了,就自动移动新的一行。

浮动造成的影响

CSS的浮动是一个非常奇特的属性,它一方面给我们带来了足够的灵活性,但同时他也给我们带来很多影响元素的奇怪行为。正因为如此,很多CSSer非常讨厌浮动,特别是对浮动不了解的同学更是如此。这也是为什么浮动难于被掌握的原因之一。为了让大家能更好的掌握浮动,为后面使用浮动来给Web布局打下基础,这里有必要来了解浮动给元素带来的不良影响。

父容器的塌陷

前面多次提到过,浮动元素会从正常文档流中脱离出来,不会继续停留在其父元素内。如果一个元素只有一个子元素,那么父元素就会塌陷,就像空的一样。其表现形为就有点类似于子元素做了绝对定位

.parent { 
    padding: 10px; 
    border: 1px dashed;
} 
.child { 
    float: left 
}

其实该现象如果要说的严谨一点的话:容器中的所有元素都浮动的话,容器元素就会塌陷;如果有任何一个非浮动元素存在,那么容器的高度将与非浮动元素高度等同

前面的元素会将浮动元素向下推

虽然浮动元素会尽量靠近父元素的顶部,然而文档中浮动元素前面的兄弟元素会把浮动元素向下推。无论前面的元素是内联元素还是块元素。也就是说如果我们在浮动元素之前或之后有一个段落,将会得到不同的效果:

前面的浮动元素将得到更好的位置

在浮动中,“最佳”的位置将会给第一个被定义为浮动的元素。比如说,有多个元素都使用了float:right,那么HTML中的第一个定义了float:right的元素会最靠近容器(父元素)的最右边,而这个位置也是最佳位置:

<div class="container"> 
    <div class="right">1</div> 
    <div class="right">2</div> 
    <div class="right">3</div> 
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p> 
</div>

接近于父元素的顶部优先于左边/右边

当有多个浮动元素向同一个方向浮动时,随后的元素为了更接近父元素的顶部,将会选择远离父元素左边/右边的位置。这就意味着多个浮动元素将尽可能并排排列,只有当父元素的宽度不能容纳它们,它们才会移动到下面。

浮动元素不能延伸到它的父元素外面

左浮动的元素不会延伸到父元素的左边缘外。左浮动的元素不应该延伸到父元素的右外边缘外,除非父元素没有剩余的空间。

对非浮动兄弟元素的影响

如果浮动元素的兄弟元素(非浮动)是一个块级元素,那么该元素会忽视浮动元素,而占据浮动元素的位置,并且元素会处在浮动元素的下层(并且无法通过z-index属性改变他们的层叠位置),但它的内部文字和其他行内元素都会环绕浮动元素。

如果非浮动的块元素在浮动元素的前面,其行为又将会有所不同:

如果非浮动元素不是块级元素,而是内联级元素,则元素会环绕浮动元素排列。如下图所示:

上图用到的示例代码如下所示:

<!-- HTML -->
<div class="container">
    <p>Lorem Ipsum is simply dummy...</p>
    <div class="float left">float: left</div>
</div>

<div class="container">
    <div class="float right">float: right</div>
    <p>Lorem Ipsum is ss...</p>
</div>

<div class="container">
    <div class="float left">float: left</div>
    Lorem Ipsum is simpl500s...
</div>

<div class="container">
    <img src="https://s.cdpn.io/profiles/user/1061/80.jpg?1" alt="">
    <div class="float left">float: left</div>
</div>

<div class="container">
    <div class="float right">float: right</div>
    <img src="https://s.cdpn.io/profiles/user/1061/80.jpg?1" alt="">
</div>

// SCSS
body {
    width: 100vw;
    min-height: 100vh;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: flex-start;
    padding: 2vw;
}
.container {
    padding: 10px;
    max-width: 30vw;
    border: 1px dashed;
    padding: 1vw;
    margin: 1vw;
    border-radius: 5px;
}

.float {
    width: 100px;
    height: 100px;
    background: #f36;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    
    &.left {
        float: left;
    }
    
    &.right {
        float: right;
    }
}

p {
    background: #380ABC;
    color: #fff;
    padding: 5px;
    line-height: 1.625;
    margin: 0;
}

img {
    padding: 5px;
    border: 2px dotted #f36;
    border-radius: 3px;
}

浮动对父元素之外的元素的影响

当一个元素浮动时,在没有清除浮动的情况之下,父元素会塌陷,父元素的高度有可能是0(如果没有设置paddingborder)。并且其父元素之外的非浮动元素也会无视该浮动元素,浮动元素仿佛到了另外一个世界里。如果浮动元素的父元素之外的元素是浮动元素,他们仿佛又会回到同一个世界中。

<section>
    <div class="container">
        <div class="float left">float: left</div>
    </div>
    <div class="wrapper"></div>
</section>

<section>
    <div class="container">
        <div class="float left">float: left</div>
    </div>
    <div class="wrapper float right">float: left</div>
</section>

效果如下:

清除浮动

在CSS浮动中有与之匹配的另一个概念,那就是清除浮动。清除浮动的属性是clear,该属性用来指定一个元素是否必须移动(清除浮动后)到在它之前的浮动元素下面。

clear属性适用于浮动和非浮动元素。其对应的值主要有:

clear: none | left | right | both | inline-start | inline-end | inherit
  • none:元素不会向下移动清除之前的浮动。
  • left:元素被向下移动用于清除之前的左浮动。
  • right:元素被向下移动用于清除之前的右浮动。
  • both:元素被向下移动用于清除之前的左右浮动。
  • inline-start:该关键字表示该元素向下移动以清除其包含块的起始侧上的浮动。即在某个区域的左侧浮动或右侧浮动。
  • inline-end:该关键字表示该元素向下移动以清除其包含块的末端的浮点,即在某个区域的右侧浮动或左侧浮动。

当应用于非浮动块时,它将非浮动块的边框边界移动到所有相关浮动元素外边界的下方。这个非浮动块的垂直外边距会折叠。

另一方面,两个浮动元素的垂直外边距将不会折叠。当应用于浮动元素时,它将元素的外边界移动到所有相关的浮动元素外边界的下方。这会影响后面浮动元素的布局,后面的浮动元素的位置无法高于它之前的元素。如下图所示:

在介绍浮动的时候,我们看到了浮动元素会对其他元素造成一定的影响,比如说浮动元素父元素塌陷。为了避免一些你无法预估的影响,在使用浮动的时候,建议配合使用清除浮动。

在CSS中清除浮动常见的方式主要有:

  • 使用块级格式化上下文(BFC)来清除浮动,比如在浮动父元素上使用floatoverflowdisplay等,详细的可以参阅读视觉格式化一文
  • 借助伪元素::before::after,在该元素上添加clear:both样式
  • display中最新属性之一flow-root用来清除浮动

来看看这三种清除浮动的示例代码:

<!-- HTML -->
<div class="box clearfix">
    <img src="/images/police.svg" width="150" alt="Police!">
    Alice was beginning to get very tired of sitting by her sister on the
    bank...
</div>

// BFC方式
.clearfix {
    overflow: hidden;
}

// 伪元素方式
.clearfix::after {
    content: '';
    display: table;
    clear: both;
}

// flow-root方式
.clearfix {
    display: flow-root
}

小结

虽然浮动使用的场景越来越少,但这并不代表说浮动就失去了其意义,没有存在的必要。在Web布局中还是有很多场景离不开CSS的浮动特性,比如文本围绕图片,比如CSS Shapes进行的不规则布局等。那么在这一章节中主要介绍了浮动的一些基本知识和基础使用,其中最重要的是浮动给不同元素造成的不良影响。最后在结尾之处顺带介绍了清除浮动,因为在CSS中使用浮动就得记住清除浮动。这样结合使用会让你减少很多烦恼。

这一章有关于浮动的基础学习,是为了后面有关于使用浮动来布局打基石。这章是否理解清楚,直接会影响到后面对浮动布局学习的效果。