深入了解定位
定位机制
在CSS2 中定义了,一个盒子可以有三种定位方案进行布局:
常规流: 包括块级、行内级和相对定位的元素
浮动: 浮动模型
绝对定位: 绝对定位的盒子会被完全移除常规流,并相对于其包含块进行定位
属性值: static(默认值)、relative、absolute、fixed、sticky(CSS3新增)
偏移属性: top、bottom、left、right。如果值为百分数,top、bottom的值相对于包含块的高度,left、right的值相对于包含块的宽度。
包含块
元素的位置和大小有时是相对于某个称为元素的包含块的矩形计算的。
包含块在布局中是一个很重要的概念,在CSS2中,这么定义包含块:
- “根元素”所在的包含块被称为初始包含块,大多数浏览器的初始包含块为视口大小的矩形框,初始包含块的 direction 属性和根元素的一致;
- 对于非根元素,如果元素的 position 值为 relative 或 static,则包含块由最近的块级框、行内块祖先框的内容边界组成;
- 如果元素的 position 值为 fixed,则由视口建立包含块;
- 如果元素的 position 值为 absolute,包含块则由最近的 以 position 值为 absolute、fixed或relative的祖先盒子建立:
- 如果这个祖先元素是行内元素,包含块则为该元素生成的第一个和最后一个行内框组成的包围框(在从左向右读的语言中,包含块的上边界和左边界是该祖先元素的中第一个行内框的上边界和左边界,包含块的下边界和右边界是该祖先元素中最后一个行内框的下边界和右边界);
- 如果这个祖先元素是块级元素,包含块则为该元素的内容边界;
- 如果没有这样的祖先元素,包含块则会初始包含块。
static
盒子表现为常规流。块级元素生成一个块框,行内元素生成一个或多个行框。偏移属性不起作用。
relative
相对定位:一个常规流或浮动的盒子相对于自己的位置进行移动。
相对定位被划分为了常规流布局,元素保持其未定位前的形状和大小,初始位置的空间保留。
如果由相对定位的原因导致出现了 overflow: auto 或者 overflow: scroll 内容溢出,为了能够正常的访问溢出的内容,则需要创建滚动条,这可能会影响到布局。
在CSS2.1规范中指出了在遇到过度受限的相对定位时,一个值会被重置为另一个值的相反数(即right = -left)。
对于水平方向上,如果left 和 right 的值都为 auto (初始值),其值为 0,即保持在原位置不动;如果其中一个为 auto 则使用另一个值作为计算值;如果这两个值都不为 auto ,则位置被过度受限, 根据 direction 的属性值进行计算(即根据文本的排版方向决定),如果 direction: ltr 时,则以 left 值为准,right 变为 -left,如果 direction: rtl 时,则以 right 值为准,left 变为 -right,垂直方向同理。
absolute
在绝对定位模型中,绝对定位的盒子会被从常规流中完全移除,并相对于其包含块进行定位,包含块可能是 position 值为 relative、absolute、fixed 的祖先元素也可能是初始包含块。
元素绝对定位后如果没有设置偏移属性,则会定位在常规流中原始的位置。
元素绝对定位后会生成一个块级框,而不论之前在常规流中是何种类型的框。
定位元素不会流入其他元素的内容,定位元素可能覆盖其他元素或者被其他元素覆盖,这取决于其重叠框的堆栈级别(即 z-index 的值,该值设置为 负数 时则置于包含块底层)。
同样由绝对定位的原因导致出现了 overflow: auto 或者 overflow: scroll 内容溢出,为了能够正常的访问溢出的内容,则需要创建滚动条,这可能会影响到布局。
在包含块的文本溢出中,绝对定位元素的初始位置基于包含块的框进行定位,包含块的内容滚动时,绝对定位的元素也会跟着一起滚动。
fixed
固定定位是绝对定位的一个子类,不同点在于固定定位元素的包含块是初始包含块,即基于视口进行定位的,不会因为文档的滚动而位置改变。
元素固定定位后如果没有设置偏移属性,则会在定位在常规流中原始的位置。
sticky
该属性的支持程度很不错的,除了IE,其他主流的17年之后发版的浏览器都支持,唯一需要注意的是 Safari 需要增加前缀 -webkit-sticky。
这个属性值是CSS3新增的“粘性”定位(不过还没有在w3c上找到正式文档,只找到了工作草案文档和MDN上的文档)。
元素的初始位置根据常规流进行布局,然后根据 top、bottom、left、right 相对于其滚动的祖先元素(包含块)进行偏移,并且必须设置偏移属性,偏移不会影响其他元素的位置。
应用
冻结的表格单元 冻结第一行和第一列
sticky 遇上 hidden 的一个bug https://github.com/w3c/csswg-drafts/issues/865
