重塑你的CSS世界观——浮动魔鬼float

栏目: CSS · 发布时间: 5年前

内容简介:由于从工作到现在,我的主要工作都是写JavaScript,几乎没怎么碰CSS,通常都是别人写好界面,然后我来开发JavaScript逻辑代码,这导致了严重的偏科,CSS弱得很,所以我决定要重新学习CSS,从打好CSS2的基础开始。最近我开始在看一本关于CSS的书,这本书叫《CSS世界》,是张鑫旭这个CSS大神写的,质量十分有保障。这本书几乎完全颠覆了我对于CSS的认知,几乎等于重塑我的CSS世界观。子曰:学而不思则罔,思而不学则殆。

为什么要写《重塑你的CSS世界观》系列文章

由于从工作到现在,我的主要工作都是写JavaScript,几乎没怎么碰CSS,通常都是别人写好界面,然后我来开发JavaScript逻辑代码,这导致了严重的偏科,CSS弱得很,所以我决定要重新学习CSS,从打好CSS2的基础开始。最近我开始在看一本关于CSS的书,这本书叫《CSS世界》,是张鑫旭这个CSS大神写的,质量十分有保障。

这本书几乎完全颠覆了我对于CSS的认知,几乎等于重塑我的CSS世界观。

子曰:学而不思则罔,思而不学则殆。

所以我决定把从这本书学习到的知识结合我看到的一些文档来总结一下,以便以后复习使用,所以文章中的见解也有可能是错的,不追求完全正确,而是希望能够作为方法论来指导之后的CSS开发。如有错误,请诸君指正。

浮动的本质是什么?

浮动的本质就是为了实现文字环绕效果 ——《CSS世界》第六章 流的破坏和保护

熟悉张鑫旭老师的人,应该在他的博客和一些视频教程中都有听到上面那句话,可能和我一样,咋一看觉得懂,然后就不以为然就过去了,然后使用在 float 的时候还是错误百出。

撇开我们是前端开发者这个身份,让我们作为一个普通用户的角度来思考有哪些场景我们会用到文字环绕效果。相信很多人都是使用过 Word 这个软件,在我们编辑图文信息的时候,希望文字可以围绕图片来排列,也就是文字环绕,也就是从图1变为图2:

重塑你的CSS世界观——浮动魔鬼float

<center>图1</center>

重塑你的CSS世界观——浮动魔鬼float

<center>图2</center>

普通用户视角,做到文字环绕的的步骤

还是一样,让我们作为一个普通用户来思考如何实现这一效果。为了方便,我在这里定义一些词汇,将文本、段落称之为 跟随内容 ,将图片等需要被环绕的称之为 目标元素

首先然后我们将这两者简化一下,分成两个区块,如图:

重塑你的CSS世界观——浮动魔鬼float

而所谓的环绕,那就是把两个区块重叠在一起, 目标元素 悬浮于重叠区上面,而且重叠区不能有 跟随内容 的内容,如图:

重塑你的CSS世界观——浮动魔鬼float

所以要使得 跟随内容 能够环绕 目标元素 我们需要做到以下步骤:

  1. 使 跟随内容 的顶部能够上升到与 目标元素 顶部同一水平线上;
  2. 跟随内容目标元素 的重叠区域不能有文字,不重叠区域按照原来的排版方式。

如何浮动?

脱离文档流

浮动最初是为了实现文字环绕效果,经过上面的讨论我们意识到了需要让浮动元素和跟随元素的顶部上升到同一水平线上。而在文档流中,如果浮动元素和跟随元素都是Div元素,它们两在默认情况下都将占据一行。所以我们要让浮动元素脱离文档流,这个时候跟随元素才能根据文档流的要求进行上移,从而达到视觉上的重叠效果。

而由于浮动元素脱离了文档流,如果父元素没有指定高度或者其他元素撑起,也就出现了所谓的浮动元素的父元素高度塌陷。

确定“包含块”,在“当前行”进行浮动

在进行原理分析前,让我们先来看一段代码,并想象它的渲染结果

<style>
    .box {
        border:1px solid red; 
        width: 200px;
        margin-left:50px;
    }
    
    .text {
        background-color:gray;
        color:white;
        display:inline-block;
    }
    
    .float {
        float:left;
        color:blue;
    }
    
</style>

<div class='box'>
    我就是
    <span class='text'>一段文本<em class='float'>浮动</em></span>
</div>

此时,你脑海里想象的结果可能如下图:

重塑你的CSS世界观——浮动魔鬼float

但实际运行效果却是这样的:

重塑你的CSS世界观——浮动魔鬼float

为什么会出现这种情况呢?让我们从CSS的标准中找寻答案。W3C的CSS标准文档关于浮动的描述中有这样一段话:

A floated box is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float

翻译成中文就是:

一个浮动盒会向左或向右移动,直到其外边界挨到 包含块 边界或者另一个浮动盒的外边界。

OK,先撇开有多个浮动元素的情况,让我们只考虑一个浮动元素的情况。注意到上述引用文本中的高亮词没有? 包含块(containing block) ,没错,之所以刚才我们对代码运行的渲染效果的想象与实际浏览器运行的效果产生偏差,就是因为我们没有意识到 包含块(containing block) ,或者识别错了浮动元素的 包含块 ,而这个 包含块 就是元素 浮动的参考位置 !

什么是包含块?如果判断一个元素的包含块?

在W3C的CSS标准文档中关于包含块的描述中有这样一段话:

The position and size of an element's box(es) are sometimes calculated relative to a certain rectangle,called the containing block of the element

翻译成中文就是:

元素(生成的)盒的位置和大小有时是根据一个特定矩形计算的,叫做该元素的包含块(containing block)

关于如何确定一个元素的包含块,标准也给出了定义,由于英文的较长,阅读起来比较吃力,在此我放一段中文的定义:

    1. 根元素所在的包含块是一个被称为初始包含块的矩形。对于连续媒体,尺寸取自视口的尺寸,并且被固定在画布开始的位置;对于分页媒体就是页区(page area)。初始包含块的'direction'属性与根元素的相同
    1. 对于其它元素,如果该元素的position是'relative'或者'static',包含块由其最近的块容器祖先盒的内容边界( the content edge )形成
    1. 如果元素具有'position: fixed',包含块由连续媒体的视口或者分页媒体的页区建立
    1. 如果元素具有'position: absolute',包含块由最近的'position'为'absolute','relative'或者'fixed'的祖先建立,按照如下方式:
      1. 如果该祖先是一个行内元素,包含块就是环绕着为该元素生成的第一个和最后一个行内盒的内边距框的边界框(bounding box)。在CSS 2.1中,如果该行内元素被跨行分割了,那么包含块是未定义的
      1. 否则,包含块由该祖先的内边距边界形成

如果没有这样的祖先,包含块就是初始包含块

根据上面关于包含块的定义,我们可以发现 .float 元素符合第2条,那么它的包容块就是 .box 这个 div内容边界 ,注意是内容边界( the content edge ),而不是整个盒子,所以不包含padding、border、margin。

让我们先来看一张关于盒模型图:

重塑你的CSS世界观——浮动魔鬼float

W3C的CSS规范关于 content edge 的定义是:

The content edge surrounds the rectangle given by the width and height of the box, which often depend on the element's rendered content The four content edges define the box's content box.

翻译成中文就是:

内容边界环绕着盒的width和height指出的矩形,通常取决于元素的呈现(rendered)内容。4条内容边界定义了盒的内容框(content box)

所以如果我们给 .box 类添加一个 padding 为10px的话,

.box {
    padding: 10px;
}

浮动元素由于是贴着包容块在浮动,而此时的包容块仅仅是 .box 元素的内容区,不包含 padding ,所以就出现了,浮动元素与 .box 元素盒子之间的间距,效果如下:

重塑你的CSS世界观——浮动魔鬼float

OK,那如何通过只调整CSS的方式就达到我们一开始脑海里想象的渲染效果呢?答案就是让 .text span元素成为 .float 元素的 包含块 就行了,让 inline level element 变成 block level element ,只需添加一句 dispaly:inline-blockdispaly:block 即可,这里我们还想保持 .text 元素的内联性,所以我们添加:

.text {
    display:inline-block;
}

此时的效果就是我们一开始想要的了

重塑你的CSS世界观——浮动魔鬼float

在“当前行”浮动

W3C的CSS标准规范中关于浮动的定义,一开头就有这么一句:

A float is a box that is shifted to the left or right on the current line .

翻译成中文就是:

浮动(盒)就是一个在 当前行 向左或向右移动的盒。

大家注意高亮词 当前行(the current line) ,一定要理解这个 当前行 ,如果说 包含块(containing block) 决定了浮动元素浮动的范围的话,那这个 当前行(the current line) 就决定了在浮动范围的哪个垂直位置进行浮动。

同样,我们通过一个小场景来了解这个 当前行 的重要性。

想象一下,你接到了一个任务,要求实现一个三列布局,无论左右两列的宽度怎么变化,中间列的宽度都要自适应,效果大概如下图(为了突出效果,给三列都添加了背景颜色):

重塑你的CSS世界观——浮动魔鬼float

聪明的你可能已经想到了办法,让左右两列浮动,中间列的左右 margin 值为 auto ,这样就可以实现中间列自适应了。没错你觉得十分机智,然后按照设计图的视觉你从左到右写了三个 div

<style>
    .container {
        width: 600px;
        color:white;
        text-align:center;
        font-size:30px;
    }
    .left {
        float:left;
        background-color:yellow;
        width:200px;
        height:200px;
    }
    .right {
        float:right;
        background-color:red;
        width:200px;
        height:200px;
    }    
    
    .middle {
        height: 200px;
        margin: 0 auto;
        background-color:blue;
    }
    
</style>
<div class='container'>
    <div class='left'>Left</div>
    <div class='middle'>Middle</div>
    <div class='right'>Right</div>
</div>

然而你运行代码,你看到的效果却是这样的:

重塑你的CSS世界观——浮动魔鬼float

然后你的心情奔溃了:

重塑你的CSS世界观——浮动魔鬼float

遇事不慌,首先让我们分析一下,浮动元素 .right 的包含块是什么?没错,就是 .container内容边界(content edge) ,所以虽然有点差错,但好歹还在里面浮动着。

那接下来就是确定 当前行(current line) 了。

让我们还原到最开始的时候的布局,去除 .left.right 的浮动属性,此时会有"三行"

重塑你的CSS世界观——浮动魔鬼float

首先我们让 .left 浮动起来,在 .container 的内容边界范围中、“第一行”中向左浮动,由于其脱离了文档流, .middle 开始上移到“第一行”。

此时由于 .middle 的上移, .right 元素也向上移动到了“第二行”,然后让它浮动,此时 .right 的浮动范围是 .container 的内容边界范围中,而浮动的 当前行 是“第二行”,所以就出现了 .right 元素 掉下去浮动 的效果了,因为它的“当前行”就是“第二行”,它其实没有掉下去过!

那怎么样才能解决,很简单,让 .left.right “同一行”不就好了。什么意思呢?

.left 浮动的当前行是“第一行”,然后由于脱离了文档流,后续的元素会往上移动一行,那我们让 .right 紧跟在 .left 之后不就行了, .left 浮动后, .right 就会到“第一行”,此时浮动不就到了“第一行”的右边了么。然后由于 .right 也脱离文档流, .middle 也上移到了“第一行”,具体代码如下:

<div class='container'>
    <div class='left'>Left</div>
    <div class='right'>Right</div>
    <div class='middle'>Middle</div>
</div>

然后就是运行之后就是我们一开始想要的效果了:

重塑你的CSS世界观——浮动魔鬼float

看到这里相信你已经理解了许多,让我们通过相关下面这段代码的运行效果,来检验你是否真的理解了吧,提示:标题中的文字已经超出了 .title 的宽度了

<style>
    .title {
        width:160px;
    }
    
    .more {
        float:right
    }
</style>
<h3 class='title'>我就是一个三级标题<a href='#' class='more'>更多</a></h3>

如果你能脑补出下图的效果,说明你已经懂了,如果还不能,请回到前文继续阅读。

重塑你的CSS世界观——浮动魔鬼float

让我们用上面用过的套路来分析这一现象,首先确认 .more 元素的包含块为 .title 元素的内容边界,然后由于内容过长,宽度不够,此时就变为了两行显示,而 .more 元素就在“第二行”,所以它的当前行就是“第二行”,添加浮动属性之后,在 .title 元素的内容边界范围内的“第二行”向右浮动,就形成了我们看到的样子。

如果希望无论标题内容无论多长, .more 元素都要固定浮动在右边,那么只需要将 .more 元素的代码放置带 .title 元素的最前方即可,也就是:

<h3 class='title'><a href='#' class='more'>更多</a>我就是一个三级标题</h3>

此时的效果就是我们想要的了

重塑你的CSS世界观——浮动魔鬼float

重叠区不允许渲染“内容”,行框盒子“卡位”,实现文字环绕

这一部分其实是浮动的本质,却经常被我们所遗忘,主要是因为这个文字环绕的效果主要影响的是浮动元素的跟随元素。

由于这一部分规范中的定义比较晦涩,所以以下内容是我在阅读《CSS世界》结合自身的理解来阐述的,如果你希望查看规范的原文, 可以访问这个链接

首先由于浮动,浮动元素和跟随元素出现了一定的重叠区,这一点大家都知道了,而要想内容环绕,则重叠区不能有内容,注意是内容,文本、svg、图片等替换元素都被视为“内容”,基本都是inline或line-block的元素。

而无论是inline还是inline-block元素,都会产生“行框盒子”,在重叠区中不允许出现“内容”,也就可以转化成重叠区的“行框盒子”需要“贴在”浮动元素浮动方向的反方向的侧边即可,就是说如果一个元素向左浮动,则跟随元素在重叠区的“行框盒子”需要“贴在”浮动元素的右侧边,反之亦然。

让我们看一段代码:

<style>
    .container{
        width:250px;
        border:1px solid black;
    }
    
    .float {
        float:left;
        width: 100px;
        height:100px;
        border:2px solid red;
    }
    .paragraph {
        background-color:green;
        color:white;
    }
    
    .paragraph:first-line {
        background-color:blue;
    }
    
</style>
<div class='container'>
    <div class='float'></div>
    <div class='paragraph'>
        我是匿名内联盒子的文本
        <span>,而我是内联盒子的文本,如果文本很长,那就会出现balabalabala的效果。
        </span>
        充点字数哈兄弟。
    </div>
</div>

重塑你的CSS世界观——浮动魔鬼float

我们用蓝色背景高亮文本的第一行,就可以看得出来“行框盒子”卡浮动元素侧边的效果了,然后注意,重叠区是不能渲染“内容”,背景颜色不属于“内容”,所以我们可以看到绿色背景色连重叠区也铺满。

这时候可能有大兄弟要问,那重叠区到底是浮动元素在上还是跟随元素在上啊?答案就是:浮动元素在上。

让我们加一段代码验证以下:

.float {
    background-color:yellow;
}

效果如下:

重塑你的CSS世界观——浮动魔鬼float

总结

当一个元素要进行浮动时,需要以下步骤:

    1. 脱离文档流;
    1. 在“包含块”范围内及“当前行”上根据浮动方向进行浮动;
    1. 受浮动影响的元素在与浮动元素的重叠区中不允许渲染“内容”,受浮动影响的元素内的行框盒子进行“卡位”,实现文字环绕;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java 8函数式编程

Java 8函数式编程

[英] Richard Warburton / 王群锋 / 人民邮电出版社 / 2015-3 / 39.00元

通过每一章的练习快速掌握Java 8中的Lambda表达式 分析流、高级集合和其他Java 8类库的改进 利用多核CPU提高数据并发的性能 将现有代码库和库代码Lambda化 学习Lambda表达式单元测试和调试的实践解决方案 用Lambda表达式实现面向对象编程的SOLID原则 编写能有效执行消息传送和非阻塞I/O的并发应用一起来看看 《Java 8函数式编程》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试