因为从工做到如今,个人主要工做都是写JavaScript,几乎没怎么碰CSS,一般都是别人写好界面,而后我来开发JavaScript逻辑代码,这致使了严重的偏科,CSS弱得很,因此我决定要从新学习CSS,从打好CSS2的基础开始。最近我开始在看一本关于CSS的书,这本书叫《CSS世界》,是张鑫旭这个CSS大神写的,质量十分有保障。css
这本书几乎彻底颠覆了我对于CSS的认知,几乎等于重塑个人CSS世界观。html
子曰:学而不思则罔,思而不学则殆。
因此我决定把从这本书学习到的知识结合我看到的一些文档来总结一下,以便之后复习使用,因此文章中的看法也有多是错的,不追求彻底正确,而是但愿可以做为方法论来指导以后的CSS开发。若有错误,请诸君指正。前端
浮动的本质就是为了实现文字环绕效果 ——《CSS世界》第六章 流的破坏和保护
熟悉张鑫旭老师的人,应该在他的博客和一些视频教程中都有听到上面那句话,可能和我同样,咋一看以为懂,而后就不觉得然就过去了,而后使用在float
的时候仍是错误百出。浏览器
撇开咱们是前端开发者这个身份,让咱们做为一个普通用户的角度来思考有哪些场景咱们会用到文字环绕效果。相信不少人都是使用过Word
这个软件,在咱们编辑图文信息的时候,但愿文字能够围绕图片来排列,也就是文字环绕,也就是从图1变为图2:svg
<center>图1</center>布局
<center>图2</center>学习
仍是同样,让咱们做为一个普通用户来思考如何实现这一效果。为了方便,我在这里定义一些词汇,将文本、段落称之为跟随内容
,将图片等须要被环绕的称之为目标元素
。
首先而后咱们将这二者简化一下,分红两个区块,如图:spa
而所谓的环绕,那就是把两个区块重叠在一块儿,目标元素
悬浮于重叠区上面,并且重叠区不能有跟随内容
的内容,如图:翻译
因此要使得跟随内容
可以环绕目标元素
咱们须要作到如下步骤:设计
跟随内容
的顶部可以上升到与目标元素
顶部同一水平线上;跟随内容
与目标元素
的重叠区域不能有文字,不重叠区域按照原来的排版方式。浮动最初是为了实现文字环绕效果,通过上面的讨论咱们意识到了须要让浮动元素和跟随元素的顶部上升到同一水平线上。而在文档流中,若是浮动元素和跟随元素都是Div元素,它们两在默认状况下都将占据一行。因此咱们要让浮动元素脱离文档流,这个时候跟随元素才能根据文档流的要求进行上移,从而达到视觉上的重叠效果。
而因为浮动元素脱离了文档流,若是父元素没有指定高度或者其余元素撑起,也就出现了所谓的浮动元素的父元素高度塌陷。
在进行原理分析前,让咱们先来看一段代码,并想象它的渲染结果
<style> .box { border:1px solid red; width: 200px; margin-left:50px; } .text { background-color:gray; color:white; } .float { float:left; color:blue; } </style> <div class='box'> 我就是 <span class='text'>一段文本<em class='float'>浮动</em></span> </div>
此时,你脑海里想象的结果可能以下图:
但实际运行效果倒是这样的:
为何会出现这种状况呢?让咱们从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)
关于如何肯定一个元素的包含块,标准也给出了定义,因为英文的较长,阅读起来比较吃力,在此我放一段中文的定义:
- 根元素所在的包含块是一个被称为初始包含块的矩形。对于连续媒体,尺寸取自视口的尺寸,而且被固定在画布开始的位置;对于分页媒体就是页区(page area)。初始包含块的'direction'属性与根元素的相同
- 对于其它元素,若是该元素的position是'relative'或者'static',包含块由其最近的块容器祖先盒的内容边界(
the content edge
)造成
- 若是元素具备'position: fixed',包含块由连续媒体的视口或者分页媒体的页区创建
- 若是元素具备'position: absolute',包含块由最近的'position'为'absolute','relative'或者'fixed'的祖先创建,按照以下方式:
- 若是该祖先是一个行内元素,包含块就是环绕着为该元素生成的第一个和最后一个行内盒的内边距框的边界框(bounding box)。在CSS 2.1中,若是该行内元素被跨行分割了,那么包含块是未定义的
- 不然,包含块由该祖先的内边距边界造成
若是没有这样的祖先,包含块就是初始包含块
根据上面关于包含块的定义,咱们能够发现.float
元素符合第2条,那么它的包容块就是.box
这个div
的内容边界
,注意是内容边界(the content edge
),而不是整个盒子,因此不包含padding、border、margin。
让咱们先来看一张关于盒模型图:
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
元素盒子之间的间距,效果以下:
OK,那如何经过只调整CSS的方式就达到咱们一开始脑海里想象的渲染效果呢?答案就是让.text
span元素成为.float
元素的包含块
就好了,让inline level element
变成block level element
,只需添加一句dispaly:inline-block
或dispaly:block
便可,这里咱们还想保持.text
元素的内联性,因此咱们添加:
.text { display:inline-block; }
此时的效果就是咱们一开始想要的了
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)
就决定了在浮动范围的哪一个垂直位置进行浮动。
一样,咱们经过一个小场景来了解这个当前行
的重要性。
想象一下,你接到了一个任务,要求实现一个三列布局,不管左右两列的宽度怎么变化,中间列的宽度都要自适应,效果大概以下图(为了突出效果,给三列都添加了背景颜色):
聪明的你可能已经想到了办法,让左右两列浮动,中间列的左右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>
然而你运行代码,你看到的效果倒是这样的:
而后你的心情奔溃了:
遇事不慌,首先让咱们分析一下,浮动元素.right
的包含块是什么?没错,就是.container
的内容边界(content edge)
,因此虽然有点差错,但好歹还在里面浮动着。
那接下来就是肯定当前行(current line)
了。
让咱们还原到最开始的时候的布局,去除.left
和.right
的浮动属性,此时会有"三行"
首先咱们让.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>
而后就是运行以后就是咱们一开始想要的效果了:
看到这里相信你已经理解了许多,让咱们经过相关下面这段代码的运行效果,来检验你是否真的理解了吧,提示:标题中的文字已经超出了.title
的宽度了
<style> .title { width:160px; } .more { float:right } </style> <h3 class='title'>我就是一个三级标题<a href='#' class='more'>更多</a></h3>
若是你能脑补出下图的效果,说明你已经懂了,若是还不能,请回到前文继续阅读。
让咱们用上面用过的套路来分析这一现象,首先确认.more
元素的包含块为.title
元素的内容边界,而后因为内容过长,宽度不够,此时就变为了两行显示,而.more
元素就在“第二行”,因此它的当前行就是“第二行”,添加浮动属性以后,在.title
元素的内容边界范围内的“第二行”向右浮动,就造成了咱们看到的样子。
若是但愿不管标题内容不管多长,.more
元素都要固定浮动在右边,那么只须要将.more
元素的代码放置带.title
元素的最前方便可,也就是:
<h3 class='title'><a href='#' class='more'>更多</a>我就是一个三级标题</h3>
此时的效果就是咱们想要的了
这一部分实际上是浮动的本质,却常常被咱们所遗忘,主要是由于这个文字环绕的效果主要影响的是浮动元素的跟随元素。
因为这一部分规范中的定义比较晦涩,因此如下内容是我在阅读《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>
咱们用蓝色背景高亮文本的第一行,就能够看得出来“行框盒子”卡浮动元素侧边的效果了,而后注意,重叠区是不能渲染“内容”,背景颜色不属于“内容”,因此咱们能够看到绿色背景色连重叠区也铺满。
这时候可能有大兄弟要问,那重叠区究竟是浮动元素在上仍是跟随元素在上啊?答案就是:浮动元素在上。
让咱们加一段代码验证如下:
.float { background-color:yellow; }
效果以下:
当一个元素要进行浮动时,须要如下步骤: