美文网首页前端CSS积累前端
CSS绝对定位absolute详解

CSS绝对定位absolute详解

作者: 张歆琳 | 来源:发表于2016-05-04 11:12 被阅读36482次

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

    相关文章

      网友评论

      • 杨格格杨:很棒,但是这边我有一个想法,在APP的右上角的小图标中,可以将父级元素设置为position:absolute,将图片元素设置为position:relative,这样的话,就可以随着图片改变而改变,不会有分离的可能。不知楼主是没有想到还是这种方法有自身的缺陷?
      • e352cc291345:大神啊,没想到一个小属性这么多玄机
      • b5abd83fc996:我没快进看到最后了,但是没太看明白,是不是很尴尬
      • e8841a02a554:简书右下角这导航坐标z-index是1040
      • 刺猬儿:很棒
      • 10bef165cc90:对于Relative和Absolute之间的关系,是否有尺寸相关性?
      • HelloNOW:谢谢.很详细.只看了一半.收藏了先.
      • ea9a52d25831:感谢你
      • 龙波_63f7:没看懂
      • 浅陋迷蒙:收下我的膝盖
      • blueacker:赞这句:“每当你觉得以前的自己渣像块豆腐渣时,就代表你进步了。反过来总说想当年自己如何如何,那说明你还在吃老本”
      • 小小游轮:小小游轮: 不设置 left/top/right/bottom的absolute元素 在不同浏览器下 效果也是不一样的
        并不能通过margin负值来定位
        谷歌下并不能按照你的意思去显示 safari却可以按照你的意思‘正常’显示 希望作者进一步研究
      • 臭臭哥:优秀
      • 314d2953c2a9:学到很多,谢谢
      • 5ceffd6f0b3d:"未指定left/top/right/bottom的absolute元素,其在所处层级中的定位点就是正常文档流中该元素的定位点。"这句话绝大部分博文都未曾给出类似的,w3c是这样说的,“生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位。元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定。”,结合您说的就完全理解了
        小小游轮:不设置 left/top/right/bottom的absolute元素 在不同浏览器下 效果也是不一样的
        并不能通过margin复制来定位
      • 3db8f5afa559:收益颇多!!不过补充一小点:其实z-index大小并不能完全决定两个元素的层叠关系,是要看他们是否在同一个层叠上下文(stacking context)
      • 0701a3d8af90:“可以想见,前端仔瞄了一眼工资单后,放弃重构,果断将该元素的z-index设成999。恩,没什么不好的,淡定~”,我竟然觉得你说的很对,这是不是不太好:smile: :smile: :smile:
      • 挨踢小熊猫:如果祖先级都没有设置position为非static的话,不是找到body或者html进行定位,而是找到初始包含块,W3C原文如下
        If there is no such ancestor, the containing block is the initial containing block.
        可以试试把html body都加个border 然后div absolute之后,设置bottom为0 试试看
      • 6543855a70da:学习了,重绘和回流要去科普一下~
      • 930245bc541a:学习了,非常感谢!
      • 不大好都不带:棒棒哒,学到了
      • 1e361c10f838:幽默,竟然看完了,明天去操作试试,如何在不给熊猫设置relative的时候,让红❌始终粘在右上角
        228ebf2e659c:absolute在没有加top,left,right,bottom的情况下,他的作用等价于relative;
        -----------------------------------------------------------------------------------------------------
        <div style="display: inline-block;position:absolute;">
        <img src="http://img1.imgtn.bdimg.com/it/u=594559231,2167829292&fm=27&gp=0.jpg" />
        <span class="tipIcon">6</span>
        </div>
      • 浙里剡溪:看完受益匪浅,不过有个地方有点怀疑:“一旦给元素加上absolute或float就相当于给元素加上了display:block;” 应该是display: inline-block吧
        前端大飞:@RichardBillion 是 display:block没错
        a272b4345e65:应该是display: inline-block, 宽度不是占整行
        RichardBillion:@浙里剡溪 我觉得也是
      • f629c5216ad9:谢谢 你
      • c1c4726cc9fc:不错,图文并茂
      • a2f1081c9701:我已经很认真在看了,对于战斗力为6的我来说太难了!
      • 一俢:收藏
      • hcxowe:666

      本文标题:CSS绝对定位absolute详解

      本文链接:https://www.haomeiwen.com/subject/ssurrttx.html