CSS进阶——绝对定位元素的宽高是如何定义的

先抛两个小问题:

  • 绝对定位相对于谁来定位?
    大多数人都知道是相对于最近的position设置为relative/absolute/fixed的父元素来定位。那如果所有父元素的position都没有设置上面三个值,那又是相对谁来定位呢?

  • 包含块是什么?初始包含块又是什么?
    元素A包含元素B,A设置position:relative,B设置position:absolute;left:0;top:0,这个left:0;top:0是相对于A元素的content-box、padding-box还是margin-box的左上角?

如果你还不是很有把握说出来答案,可以先思考一下或者实践一下,然后再阅读后面的内容。

绝对定位元素的特性

  • 绝对定位元素完全脱离文档流,不会对后面兄弟元素的布局产生任何影响

  • 其位置(或者说大小)是由top right bottom left四个属性决定的

  • 绝对定位元素的margin不会和其他元素的margin折叠

上面说到绝对定位元素的大小是由top right bottom left四个属性决定的,这四个属性是相对于绝对定位元素的包含块来定位的

包含块

  • 绝对定位元素的包含块是由其最近的position属性设置为relativeabsolutefixed的祖先元素,按照以下方式生成的:

    • 如果这个祖先元素是行内元素...此种情况请参考后续文章

    • 否则,包含块是由祖先元素的padding edge组成(即相对于祖先元素padding-box进行定位)

  • 如果绝对定位元素的所有祖先元素的position属性都没有设置relativeabsolutefixed,则其包含块为初始包含块

初始包含块

根元素(在HTML文档中即<html>元素)所在的包含块即是初始包含块,对于浏览器来说:初始包含块的大小即是视口的大小,但是是以画布原点为锚点的。
浏览器的视口是固定在那不变的,但是一个文档可能会很长,可以上下滚动,视口中的内容会不断变化。初始包含块可以简单理解为第一个视口区域(这句话是我自己造的),上图:
clipboard.png

页面滚动之后:
clipboard.png

<!-- demo1 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>初始包含块</title>
  <style>
    html {
      margin: 10px;
      padding: 10px;
      border: 1px solid red;
    }
    body {
      padding: 10px;
      border: 1px solid blue;
    }
    .abs {
      position: absolute;
      /*left: 0;*/
      /*bottom: 0;*/
      padding: 5px;
      background-color: #9089e4;
      color: #fff;
    }
  </style>
</head>
<body>
  <h3>绝对定位之初始包含块</h3>
  <div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>初始包含块不是html,也不是body,而是指视口。</div>
    <div>滚到底啦,没有更多内容啦~~~</div>
    <div class="abs">我是绝对定位元素。</div>
  </div>
</body>
</html>

clipboard.png

红色边框代表html元素,蓝色边框代表body元素,由gif图可以进一步加深对初始包含块的理解:初始包含块并不是大多数人认为的html或body元素,这是个误区,要纠正!

绝对定位元素的大小

静态位置(static position)

一个元素的静态位置可以简单理解为这个元素在普通文档流中的位置,就是这个元素的positionstaticfloatnone时,元素在文档中所处的位置。

  • 静态位置的left值:包含块的左边界到当前定位元素的左外边距(Left Margin)边界的距离

  • 静态位置的right值:包含块的右边界到当前定位元素的右外边距(Right Margin)边界的距离

<!-- demo2 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>初始包含块</title>
  <style>
    body {
      position: relative;
      padding: 10px;
      border: 1px solid blue;
    }
    .abs {
      position: absolute;
      margin-left: 5px;
      padding: 5px;
      background-color: #9089e4;
      border: 1px solid orange;
      color: #fff;
    }
    .abs2 {
      position: absolute;
      top: 50px;
      left: 10px;
      margin-left: 5px;
      padding: 5px;
      background-color: #9089e4;
      color: #fff;
      border: 1px solid orange;
    }
  </style>
</head>
<body>
  <div class="abs">绝对定位元素1</div>
  <div class="abs2">绝对定位元素2</div>
  <div>body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素body的其他子元素</div>
</body>
</html>

从上面代码可以看出,.abs.abs2两个元素的静态位置的left值都为10px(即body的padding-left值),我们不设置.abs的left值,即默认auto,而.abs2的left设置为10px,会看到两个元素距离包含块左边的距离是一样的。
clipboard.png

绝对定位元素的布局

计算公式(width表示内容宽度,即标准盒子模型):

'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block
left、width、right默认值是auto
margin-left、margin-right默认值是0

以水平方向(从左至右:ltr)为例说明一下绝对定位元素的位置(或大小)是如何定义的。起决定因素的有left right width,每个属性都可以设置或者不设置值,默认为auto,设置了值的在表格中用1表示,总共有2 * 2 * 2 = 8 种情况:

Left Width Right 布局
auto auto auto 把margin-left和margin-right为auto的设置为0;把left设置为静态位置的left值;宽度自适应:margin-right边缘最远到right为0的位置(如果margin-right为0,则取border-right边缘,如果border-right-width也为0,则取padding-right边缘,下同)
1 auto auto 把margin-left和margin-right为auto的设置为0;宽度自适应:margin-right边缘最远到right为0的位置
auto auto 1 把margin-left和margin-right为auto的设置为0;宽度自适应:margin-left边缘最远到left为0的位置
auto 1 auto 把margin-left和margin-right为auto的设置为0;把left设置为静态位置的left值
1 1 auto 把margin-left和margin-right为auto的设置为0;从左至右按照各属性值布局
auto 1 1 把margin-left和margin-right为auto的设置为0;从右到左按照个属性值布局
1 auto 1 把margin-left和margin-right为auto的设置为0;宽度自动拉伸
1 1 1 ① 如果margin-left和margin-right都为auto,此时二者相等,则按照上述方程计算出对应的margin值;如果此时计算出来的margin值为负值,则设置margin-left为0,然后根据方程再计算出margin-right的值。
② 如果margin-left、margin-right中有一个为auto,则按照方程计算出这个值;
③ 如果margin-left、margin-right都设置了值,且导致方程左右不相等,则忽略right值。

上面是以水平方向布局讲述了绝对定位元素的宽度是如何定义的,高度是类似的,就不再详细阐述了,想进一步了解细节的同学请参考 https://www.w3.org/TR/CSS22/visudet.html#abs-non-replaced-height

本文主要参考:
https://www.w3.org/TR/CSS22/visudet.html


篇幅已经很长了,还有一部分知识点没讲到:

包含块部分:

  • 绝对定位元素的包含块是由其最近的position属性设置为relativeabsolutefixed的祖先元素,按照以下方式生成的:

    • 如果这个祖先元素是行内元素...(包含块是如何定义的?)

布局部分:
上述所讲的绝对定位元素的布局是针对非可替换元素,如果是可替换元素,布局又是怎样的?这些内容将在后续文章中做进一步阐述。


5.7k 声望
323 粉丝
0 条评论
推荐阅读
跨域资源共享——CORS
跨域资源共享(Cross-Origin Resource Sharing)是一种机制,它使用额外的 HTTP 头部告诉浏览器可以让一个web应用进行跨域资源请求。

无名小贝勒5阅读 5k评论 1

还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan35阅读 2.6k评论 2

封面图
CSS transition 小技巧!如何保留 hover 的状态?
欢迎关注我的公众号:前端侦探通常情况下,hover 是无法保存状态的。鼠标移入触发额外样式,一旦移出就还原了 {代码...} 这就意味着,如果需要保留hover的状态,可能就不得不借助JS了,比如下面是某某书院的首页...

XboxYan30阅读 3.8k评论 2

封面图
CSS 如何设置自动滚动定位的“安全”间距?
欢迎关注我的公众号:前端侦探介绍两个和滚动定位相关的 CSS 属性:scroll-padding和 scroll-margin在平时开发中,经常会碰到需要快速定位的问题,比如常见的锚点定位 {代码...} 这样,在点击a标签时会自动定位到...

XboxYan31阅读 2.5k评论 2

封面图
【已结束】SegmentFault 思否写作挑战赛!
SegmentFault 思否写作挑战赛 是思否社区新上线的系列社区活动在 2 月 8 日 正式面向社区所有用户开启;挑战赛中包含多个可供作者选择的热门技术方向,根据挑战难度分为多个等级,快来参与挑战,向更好的自己前进!

SegmentFault思否20阅读 5k评论 10

封面图
由小见大!不规则造型按钮解决方案
今天,有个群友在群里提问,使用 CSS 能否实现下述这个图形:emmm,中间这个酷似三次贝塞尔曲线的造型,使用 CSS 不太好实现。我的建议是切图实现,然而群友要求一定要用 CSS 实现。虽然麻烦,但是这个图形勉强也...

chokcoco17阅读 1.2k

封面图
CSS 如何根据背景色自动切换黑白文字?
在项目中,经常会碰到背景色不确定的场景,为了让内容文字足够清晰可见,文字和背景之间需要有足够的对比度。换句话说,当背景是深色时,文字为白色,当背景是浅色时,文字为黑色,就像这样:

XboxYan19阅读 1.7k

封面图
5.7k 声望
323 粉丝
宣传栏