之前写前端总会发现自己css定位代码得不到预期效果,有时候甚至要费上好几个小时来解决一个小问题,但最后还是不清楚原理。于是决定要系统地学下定位。翻了四五天w3c的规范,发现定位规则实际上是非常复杂的,有些效果要触发所需的充分条件都有十几条,涉及n个概念,w3school教程里面的定位规则并不完全准确(那个网站部分内容让我觉得写的人并不精通定位规则)。同时也发现之前以为等价的几个概念实际上是独立的。以上两点便是之前一直觉得定位规则诡异的原因。
但是,全面地把这些规则学通很难,也完全没有必要。师兄们是靠经验写前端,久而久之自己会形成一套固定的定位写法,简单又可靠。因此,我只想尝试整理出一套系统而又精的定位知识,并且把自己做定位的套路记录下来。
元素生成框
首先应该明确几个概念和它们的关系。这些概念包括:元素element(inline 或者 block),框 box(inline 或者 block),框模型box model,块级框block-level box,内联级框inline-level box, container box,包含块containing block , 块级格式化上下文 block formatting contexts,内联格式化上下文 inline formatting contexts 。里面有很多概念长得很像,但是有些是完全不同的!而且它们各自有独立的作用。我之前觉得定位规则诡异的主要原因就是简单把父级box和containing box简单看成是同一个概念。其实还有不少看似很像但是不一样的概念,像block box ,inline box ,这里略。(注意核对英文单词来确认是否为同一个概念)
下面来简单理一下这些概念之间的关系。在这之前,应当先理解element在文档树中的父子关系和框模型box model,这里略。
首先,可以粗略地认为,html文档中的 每一个元素element 会对应生成 一个在浏览器中真实可见的 框box,其模型为框模型box model。内联元素inline element 生成内联级框inline-level box,块级元素block element 生成 块级框block-level box。由于html文档中的元素间具有家族关系,因此元素生成的框也有家族关系。框之间的家族关系表现在父框包含子框。
w3c关于这点的原文为:Each block-level element generates a principal block-level box that contains descendant boxes and generated content and is also the box involved in any positioning scheme.Inline-level elements generate inline-level boxes, which are boxes that participate in an inline formatting context.
(图片出自http://www.ddcat.net/blog/?p=216)
格式化上下文决定定位规则
那这些一个包着一个的框具体应该怎么定位呢?考虑定位不能离开三个概念:包含块containing block , 块级格式化上下文 block formatting contexts,内联格式化上下文 inline formatting contexts 。
Formatting contexts 是个不同于框的概念,准确来说是框产生formatting contexts。什么条件下框才会生成formatting context ,会生成哪种 formatting context,等下有讲。
考虑一个框会怎么定位,就要先考虑它在哪个formatting context中 还有 它和哪些框存在于同一个formatting context中。对于block formatting context(BFC).假设A框是B,D框的父框,B框是C框的父框,A促发条件生成了一个BFC。那么,B,C,D都同在A产生的BFC里面。但若B又促发条件,产生了新的BFC,那么 B,D在A产生的BFC里面,而C只在B产生的BFC中。(这个问题最切身的影响是外边距折叠和浮动,两个框产生外边距折叠有一个必要条件是,两个框处于同一个BFC中。)而对于inline formatting context(IFC),一个IFC里面所有内联级元素都处于该IFC中(不考虑嵌套)。而产生IFC的块级框会存在于一个BFC中。块级框只存在于BFC中,内联级框只存在于IFC中。
以下是生成formatting context的促发条件:
Block formatting contexts:浮动框,绝对定位框,inline-block 的内联级框 ,overflow属性不为visible的框都会生成一个新的block formatting context。
W3C原文:Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
Inline formatting contexts:block-level box 和 inline-block 的inline-level box 也是 container box(还是要注意不要把这和其他相似的概念混淆),container box 若只含内联级框,就生成inline formatting context,否则,只要含一个块级框,就不会生成新的formatting context,并且自动生成无名块级框把所有内联元素包起来。
W3C原文:Except for table boxes, which are described in a later chapter, and replaced elements, a block-level box is also a block container box. A block container box either contains only block-level boxes or establishes an inline formatting context and thus contains only inline-level boxes. Not all block container boxes are block-level boxes: non-replaced inline blocks and non-replaced table cells are block containers but not block-level boxes. Block-level boxes that are also block containers are called block boxes.
稍微总结一下:文档树中的每个element对应生成一个框。某些框促发条件生成formatting context。最后每个框都会在某个formatting context中。而生成规则使得BFC中全为块级框,IFC中全为内联级框。
有了formatting context,才能谈定位规则,因为定位规则实际上是由formatting context决定的。BFC对BFC中的框有一套定位规则,IFC对IFC中的框有另一套定位规则。简单来说,BFC中的每一个框各占一行(也就是我以前理解的块级框要换行),IFC中会尝试用一个行框尽可能多地把多个框包起来,放在一行里面显示(也就是我以前理解的内联级框会在同一行上显示)。详细:http://www.w3help.org/zh-cn/kb/010/
以包含块为基准算出定位结果
好了,以上规则规定了哪些框会在同一个上下文(formatting context)中,这影响了元素定位效果。但是要明确规定框究竟在哪个位置,靠上下文这个无形无具体位置的概念是不够的。
因此我们需要 包含块containing block 这个有固定区域有固定位置的概念来作为框定位的基准。首先应该明确,包含块不是框,更加不能简单理解为父级框(上个学期因为这个问题好多时间都哗哗流掉5555555555)。准确来说,包含块是网页上的区域。每个框都有它的 包含块containing block ,框的包含块的区域和位置总是比该框早确定下来,然后该框就可以 以包含块为基准,按照所在formatting context 对框规定的定位规则,明确得出框的位置了。
那框的包含块是怎么确定的,为什么框的包含块总是先与框确定下来?直接看下面两个图。(其实两张图讲的是一个内容)
图片来自http://www.w3help.org/zh-cn/kb/010/