CSS绝对定位absolute详解

之前介绍过CSS浮动float详解,本篇介绍的绝对定位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详解会发现效果是一样的。但其背后的原理其实是有区别的,并不完全相同。加点文字就看出来了:

<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分钟看到这里的已经非常不容易了。(我假设你没拖动滚动条快进_

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,737评论 1 92
  • 各种纯css图标 CSS3可以实现很多漂亮的图形,我收集了32种图形,在下面列出。直接用CSS3画出这些图形,要比...
    剑残阅读 9,508评论 0 8
  • 当在这一个页面上实现布局和定位有几种不同的技术。使用哪种技术,很大程序上取决于内容和目标页面,因为有很多技术比别人...
    lulu_c阅读 1,048评论 0 5
  • 今天跟予眠说,我这两天很平静,似乎接受了自己的平凡。我的懒惰、我的虚荣、我的自私、我的伪善。而且,我好像暂时没那么...
    陈叁愿阅读 285评论 0 0
  • 密文破解网站:www.cmd5.com // // ViewController.m //加密 // // Cre...
    nebsKXZ阅读 3,433评论 1 0