之前介绍过 CSS 浮动 float 详解 https://www.jianshu.com/p/07eb19957991 , 本篇介绍的绝对定位 absolute 和浮动 float 有部分相似性. 如果能理解浮动 float, 对理解绝对定位 absolute 会大有帮助.
先说 absolute 和 float 的相似处: 包裹性 和 高度欺骗
包裹性
所谓一图胜千言 (唯一的区别是: 下图的 div 增加了 absolute)
- <div style="border:4px solid blue;">
- <img src="img/25/1.jpg" />
- </div>
- <div style="border:4px solid red; position: absolute;">
- <img src="img/25/2.jpg" />
- </div>
一旦给元素加上 absolute 或 float 就相当于给元素加上了 display:block;. 什么意思呢? 比如内联元素 span 默认宽度是自适应的, 你给其加上 width 是不起作用的. 要想 width 定宽, 你需要将 span 设成 display:block. 但如果你给 span 加上 absolute 或 float, 那 span 的 display 属性自动就变成 block, 就可以指定 width 了. 因此如果看到 CSS 里 absolute/float 和 display:block 同时出现, 那 display:block 就是多余的 CSS 代码.
高度欺骗
上例中给图片外层的 div 加上 absolute, 因此高度欺骗未能很好的体现出来, 将 absolute 移到内部图片上, 效果就出来了:
- <div style="border:4px solid blue;">
- <img src="img/25/1.jpg" />
- </div>
- <div style="border:4px solid red;">
- <img style="position: absolute;" src="img/25/2.jpg" />
- </div>
如果你看过 CSS 浮动 float 详解 https://www.jianshu.com/p/07eb19957991 会发现效果是一样的. 但其背后的原理其实是有区别的, 并不完全相同. 加点文字就看出来了:
- <div style="border:4px solid blue;">
- <img src="img/25/1.jpg" />
- </div>
- <div style="border:4px solid red;">
- <img style="position: absolute;" src="img/25/2.jpg" />
我是一个绝对定位的 absolute 元素
</div>
从图中明显看出文字被图片遮盖了, 这点和 float 不同. float 是欺骗父元素, 让其父元素误以为其高度塌陷了, 但 float 元素本身仍处于文档流中, 文字会环绕着 float 元素, 不会被遮蔽.
但 absolute 其实已经不能算是欺骗父元素了, 而是出现了层级关系. 如果处于正常的文档流中的父元素算是凡人的话, 那 absolute 已经得道成仙, 用现在的话说已经不在一个次元上. 从父元素的视点看, 设成 absolute 的图片已经完全消失不见了, 因此从最左边开始显示文字. 而 absolute 的层级高, 所以图片遮盖了文字.
记得我刚开始接触 CSS 尚处于战斗力为 5 的渣渣时, 知道了 absolute 可以出现层级的概念, 就误以为已经彻底懂了, 现在想想真是图样图森破 (当然这不是件坏事, 每当你觉得以前的自己渣像块豆腐渣时, 就代表你进步了. 反过来总说想当年自己如何如何, 那说明你还在吃老本).
有了上面的基础后, 你还需要了解 absolute 以下特性
如何确定定位点
和 relative 相爱相杀
和 z-index 的关系
减少重绘和回流的开销
如何确定定位点
一旦 absolute 分层后, 第一个出现的问题就是让浏览器在何处显示该元素. 普通文档流里的元素, 浏览器可以根据其父子兄弟元素的大小和位置, 计算出该元素的位置. 但分层后怎么办? 基本思路如下:
第一种情况: 用户只给元素指定了 absolute, 未指定 left/top/right/bottom. 此时 absolute 元素的左上角定位点位置就是该元素正常文档流里的位置. 如上面图例中, 图片熊猫是父元素的第一个孩子, 因此左上角定位点就是父元素的 content 的左上角.
如果将图片熊猫和下面的文字顺序改一下, 让其成为父元素的第二个孩子, 一图胜千言:
<div style="border:4px solid red;">
我是一个绝对定位的 absolute 元素
- <img style="position: absolute;" src="img/25/2.jpg" />
- </div>
结论重复一遍: 未指定 left/top/right/bottom 的 absolute 元素, 其在所处层级中的定位点就是正常文档流中该元素的定位点.
第二种情况: 用户给 absolute 元素指定了 left/right,top/bottom
先简单点, 让 absolute 元素没有 position:static 以外的父元素. 此时 absolute 所处的层是铺满全屏的, 即铺满 body. 会根据用户指定位置的在 body 上进行定位.
只指定 left 时, 元素的左上角定位点的 left 值会变成用户指定值. 但 top 值仍旧是该元素在正常文档流中的 top 值:
<div style="border:4px solid red;">
我是一个绝对定位的 absolute 元素
- <img style="position: absolute;left:50px;" src="img/25/2.jpg" />
- </div>
只指定 right 时, 元素的右上角定位点的 right 值会变成用户指定值. 但 top 值仍旧是该元素在正常文档流中的 top 值:
<div style="border:4px solid red;">
我是一个绝对定位的 absolute 元素
- <img style="position: absolute;right:50px;" src="img/25/2.jpg" />
- </div>
只指定 top 时, 元素的左上角定位点的 top 值会变成用户指定值. 但 left 值仍旧是该元素在正常文档流中的 left 值:
<div style="border:4px solid red;">
我是一个绝对定位的 absolute 元素
- <img style="position: absolute;top:50px;" src="img/25/2.jpg" />
- </div>
只指定 bottom 时, 元素的左下角定位点的 bottom 值会变成用户指定值. 但 left 值仍旧是该元素在正常文档流中的 left 值:
<div style="border:4px solid red;">
我是一个绝对定位的 absolute 元素
- <img style="position: absolute;bottom:50px;" src="img/25/2.jpg" />
- </div>
通过对 left/top/right/bottom 的组合设置, 由于没有 position:static 以外的父元素, 此时 absolute 元素可以去任意它想去的地方, 天空才是它的极限.
和 relative 相爱相杀
通常我们对 relative 的认识是 (好吧, 我承认, 这是我战 5 渣渣时的认识. 如果你是弗利萨, 可以鄙视这句话):relative 主要用于限制 absolute
上面已经说了, 如果 absolute 元素没有 position:static 以外的父元素, 那将相对 body 定位, 天空才是它的极限. 而一旦父元素被设为 relative, 那 absolute 子元素将相对于其父元素定位, 就好像一只脚上被绑了绳子的鸟.
比如你要实现下图 iOS 里 App 右上角的红色圈圈
通常的做法是将 App 图片所处的 div 设成 relative, 然后红色圈圈设成 absolute, 再设 top/right 即可. 这样无论用户怎么改变 App 图片的位置, 红色圈圈永远固定在右上角, 例如:
- .tipIcon {
- background-color: #f00;
- color: #fff;
- border-radius:50%;
- text-align: center;
- position: absolute;
- width: 20px;
- height: 20px;
- right:-10px; // 负值为自身体积的一半
- top:-10px;
- }
- <div style="display: inline-block;position:relative;">
- <img src="img/25/2.jpg" />
- <span class="tipIcon">6</span>
- </div>
这样做效果是 OK 的, 兼容性也 OK. 但 CSS 的世界里要实现一个效果可以有很多种方式, 具体选用哪个方案是见仁见智的. 我比较看重的标准: 一个是简洁, 另一个是尽量让每个属性干其本职工作.
用这两个标准看待上述实现方法, 应该是有改进的空间的. 首先外层 div 多了 relative 未能简洁到极致. 其次 relative 的本职工作是让元素在相对其正常文档流位置进行偏移, 但父层 div 并不需要任何位置偏移, 之所以设成 relative 唯一的目的是限制 absolute 子元素的定位点. 因此在我看来这并没有让 relative 干其本职工作. 好比小姐的本职工作是服务业, 顺便陪客户聊聊天, 但纯聊天聊完一个钟, 恐怕会被投诉.
那怎么改进呢? 答案在上面探讨 absolute 定位点时已经说了: 未指定 left/top/right/bottom 的 absolute 元素, 其在所处层级中的定位点就是正常文档流中该元素的定位点. 因此改进如下:
- .tipIcon2 {
- background-color: #f00;
- color: #fff;
- border-radius:50%;
- text-align: center;
- position: absolute;
- width: 20px;
- height: 20px;
- margin:-10px 0 0 -10px; // 不需要 top 和 right 了, 改用 margin 来进行偏移
- }
- <div style="display: inline-block;"> // 父元素不需要 relative 了
- <img src="img/25/2.jpg" /><!--
- --><span class="tipIcon2">6</span>
- </div>
- //img 和 soan 间的 html 注释的目的是消除换行符, 你也可以将 span 紧跟在 img 后面写到一行里
更深入一点看, 多一个属性意味着多一层维护. 如果用父 relative + 子 absolute 来实现定位, 万一将来页面布局要调整, 父元素的尺寸需要变换呢?
- <div style="display: inline-block; position:relative;width: 200px;">
- <img src="img/25/2.jpg" />
- <span class="tipIcon">6</span>
- </div>
上面仅仅由于父元素的 width 做了些改变, 导致右上角 absolute 图标错位了. 由于 absolute 和 relative 耦合在了一起, 父元素有点风吹草动 (如尺寸变化, 或干脆需要去掉 relative), 子元素需要重新寻找定位点. 苦逼的前端仔拿着微薄的工资在那里加班加点, 那是大大地不划算. 但如果用上例中 absolute 自身的定位特性, 无论父元素怎么折腾, 红色的圈圈都牢牢黏在图片的右上角.
这么说来 relative 和 absolute 是否应该彻底断绝关系呢? 不是这样的, 这段的标题是 "和 relative 相爱相杀", 刚才说的想杀部分, 现在说下什么相爱部分.
用 absolute 常见的一个案例是透明层覆盖元素. 要实现对全屏加一层滤镜怎么办? 很容易:
- .cover {
- position: absolute;
- left: 0;right: 0;top: 0;bottom: 0;
- background-color: #fff;
- opacity: .5;filter: alpha(opacity=50);
- }
- <div style="display: inline-block;">
- <img src="img/25/2.jpg" /><!--
- --><span class="tipIcon2">6</span>
- </div>
现在是全屏滤镜时间
<span class="cover"></span>
CSS 里有个细节值得关注: 用 absolute 的 left: 0;right: 0;top: 0;bottom: 0; 来实现全屏拉伸, 对于 absolute 元素来说, 如果同时设置 left 和 right 会水平拉伸, 同时设置 top 和 bottom 会垂直拉伸. 那为何不设 width/height 为 100% 呢? 代码都贴给你了, 可以自己试试. 算了告诉你答案吧, 前面说了, 不设 top/right/top/bottom 的话 absolute 会从正常文档流应处的位置开始定位, 因此做不到全屏. 除非你设置 width/height 为 100% 后, 同时再设 left: 0; top: 0;. 这样就显得很啰嗦.
那我不想全屏滤镜, 只希望给图片部分设置滤镜呢? 用 JS 计算图片的大小尺寸和定位点后, 设置 absolute 滤镜的尺寸和定位点? 太麻烦了. 用 relative 吧
- //CSS 部分不变
- <div style="display: inline-block;position: relative;">
- <span class="cover"></span>
- <img src="img/25/2.jpg" /><!--
- --><span class="tipIcon2">6</span>
- </div>
现在是图片滤镜时间
结论:
1. 相对定位时, 不必拘泥于 relative+absolute, 试试去掉 relative, 充分利用 absolute 自身定位的特性, 将 relative 和 absolute 解耦. 耦合度越低维护起来越容易, 前端仔腾出时间陪女朋友吃饭才是正道.
2. 拉伸平铺时, 用 relative 可以有效限制子 absolute 元素的拉伸平铺范围 (注意是拉伸, 不是缩小. 要缩小请再加上 width/height:100%;)
和 z-index 的关系
z-index 被太多的滥用了. 几乎成了个定势思维: 只要设了 absolute 就需要同步设置 z-index. 其实不是这样的. 上面所有例子都没有用到 z-index, 同样正常分层正常覆盖.
以下情况根本不需要设 z-index:
让 absolute 元素覆盖正常文档流内元素 (不用设 z-index, 自然覆盖)
让后一个 absolute 元素覆盖前一个 absolute 元素 (不用设 z-index, 只要在 HTML 端正确设置元素顺序即可)
那什么时候需要设置 z-index 呢? 当 absolute 元素覆盖另一个 absolute 元素, 且 HTML 端不方便调整 DOM 的先后顺序时, 需要设置 z-index: 1. 非常少见的情况下多个 absolute 交错覆盖, 或者需要显示最高层次的模态对话框时, 可以设置 z-index> 1.
比较好的是京东, 查看首页源码, 设置 z-index 的地方也只设了 1,2,3. 少数地方设了 11,12,13. 我更倾向于理解为是一种内部潜规则, 表明更高层级的一种需要.
要举反例可以看渣浪:
查看源码, 果然发现了 z-index:99, 当你设成 99 后, 现在我需要一个更高的层级怎么办? 重构页面代码, 将 99 等重构成 1,2,3? 可以想见, 前端仔瞄了一眼工资单后, 放弃重构, 果断将该元素的 z-index 设成 999. 恩, 没什么不好的, 淡定~
如果你的页面不比京东更复杂, 那 z-index 通常设成 1,2,3 足够了.
减少重绘和回流的开销
例如将元素隐藏, 你或许会用 display:none.
(这里插一句题外话, 用 display:none 隐藏容易显示难, 如果你用的是 jQuery 等插件, 你或许会疑惑, 直接用 show/hide API 不就行了, 难在哪里? 其中一个难点就是保存隐藏前元素的 display 属性值. 例如 A 隐藏前 display:block,B 隐藏前 display:inline,A 和 B 都改成 none 隐藏后, 要显示出来时, 你必须事先保存元素的 display 属性值, 否则做不到显示后 display 仍旧是原先的值. 而这些工作 jQuery 插件都替你做好了, 才让你产生了隐藏显示很容易的错觉.)
其实我更推荐的是 absolute 控制隐藏和显示. 方法当然相当简单, 如 absolute+ top:-9999em, 或 absolute + visibility:hidden.
优点是 absolute 由于层级的关系, 隐藏和显示只会重绘, 但不会回流 (其实我对 absolute 不会导致回流这一观点是持怀疑态度的, 说不会回流显得过于武断, 应该是 absolute 的回流开销小于正常 DOM 流中回流的开销. 但我战斗力不够, 尚未找到靠谱合理的检测回流的方法). 而用 display:none 会导致 render tree 重绘和回流.
另外, 考虑到重绘和回流的开销, 可以将动画效果放到 absolute 元素中, 避免浏览器将 render tree 回流.
总结
在这个快餐年代, 能坚持 10 分钟看到这里的已经非常不容易了.(我假设你没拖动滚动条快进_)
来源: http://www.bubuko.com/infodetail-2966024.html