内容布局(一):position布局

最近出于某些原因,我又开始翻看 CSS 相关书籍了。可能有将近两年没再阅读过相关书籍了,工作中一般就是依靠 vuetify 之类的 UI 框架做做内容布局,久而久之很多知识也就生疏了。想再开个专题,写几篇 CSS 布局相关的文章,回顾一下老得没人再愿意啃的知识。

概述

定位(position)是 CSS2 中一个重要的属性,用于规定元素的定位类型,是页面内容布局中很常用的手段。所谓定位,其主要意义就在于将使用了 position 的元素(absolute 或 fixed)从常规页面流里脱离出来,也就是说它能让元素超脱垂直布局(block)或是水平布局(inline-block)。

position 共有五种可能值:

static 默认值,没有定位。
inherit 继承父元素 position 属性(注:默认是 static,不是 inherit)
relative 相对于正常的页面位置定位,偏移后不影响周围元素的位置,即原始空间不会被挤占
absolute 相对于最近的非 static 祖先的定位;绝对定位会脱离页面流,也就是说原始空间会被挤占
fixed 相对于浏览器窗口定位,也是脱离页面流的存在

虽说定位有五种属性,但是现实开发中,用到的基本只有 absolute,所以后文就只介绍绝对定位。

  • inherit 是语义级别的存在
  • static 主要用于覆盖其他 CSS 的 position 属性
  • fixed 常用于弹框、横幅等一些比价猥琐的广告位
  • relative 在十几二十年前有人用于布局,但它移动后原始位置会被挖空,显得很奇怪,所以现在基本不用了。现在主要就是为 absolute 创建定位上下文——absolute 需要非 static 祖先定位

绝对定位

绝对定位非常适合用于提示、对话框、下拉菜单这类覆盖于其他内容之上的组件。上面提到过:absolute 一般是依靠最近的 relative 祖先定位的,relative 祖先会对 absolute 后代提供一个叫定位上下文的页面空间;absolute 组件便可以通过 top、right、bottom 和 left 四个属性,依靠在定位上下文的四个边框内。这里的 top 值——其他三个同理——指的是 absolute 相对于 relative 上边界的距离,正值为上边界下方,负值为上边界上方。

我们用一个例子来说明一下 absolute 的使用方法,下图将一句 comment——Soga!——浮动于段落的左侧:

Comment in aside

这个 comment 事实上是一个aside标签,位于 html 正文第二段。

<main>
  <p>Domain-driven design (DDD) is an approach...</p>
  <aside class="comment">Soga!</aside>
  <p>Domain-driven design is predicated on...</p>
</main>

理论上它是这样的:

Comment Original

为了将 comment 脱离至上而下的页面流,我们给 comment 加上绝对定位:

.comment {
  position: absolute;
}

后续的段落随之挤了上来,占据了 comment 的页面空间;而 comment 则悬浮于原始页面流的正上方。

Comment Absolute

之后的操作就很简单了,让 comment 向左上方移动即可。传统的做法是:先将父组件置为relative,再利用topleft位移到特定位置:

main {
  position: relative;
}

.comment {
  position: absolute;
  top: 2em;
  left: 3em;
}

这个方法本身没有太大问题,只是topleft偏移属性是相对于祖先元素定位的,编码的过程需要调整祖先元素的样式表。

top & left

我们这里偷个懒,使用一个更简单的方式——负外边距,我们就不用理会main的样式了:

.comment {
  position: absolute;
  margin-top: -2em;
  margin-left: -6em;
}
negative margin

在 CSS 中,负 margin 是有效的:左边或上边的负 margin 会将浮动元素向左或向上拉,盖住其旁边的元素。这个效果和相对定位(position: relative)的元素位移很相似,只是 relative 元素还会占据原始页面流的空间。

创建三角形

这里顺便再引申一个知识点。大家有没有发现,comment 右上方有一个小三角,这个是怎么做到的呢?

Triangle

我的实现用到了伪元素定位。我们先把 after 伪元素也设置为 absolute,这样它便根据父元素 comment 定位了。(再回忆一下,absolute 相对于最近的非 static 祖先定位)

.comment::after {
  position: absolute;
  right: -1em;
  content: '';
  width: 0;
  height: 0;
  border: .5em solid red;
}

接着,我们再定制一下这个伪元素的大小:把 content 置为空字符,高度和宽度设为 0,这样整个伪元素就成了 0 像素大小;同时把 border 做成.5em,这样该伪元素就成了一个边长为1em的实心正方形。然后,将偏移量 right 置为-1em,整个伪元素就被移到了 comment 的右侧。

pseudo after

这时候再看一下该红色正方形的真实组成,如下:当 content 为 0 像素,并且 border 不为 0 时,元素的四边会被挤压成四个等腰三角形。

border

只要把正方形的右下部分,也就是 border-right 和 border-bottom 做成透明,整个三角就完工了。

.comment::after {
  position: absolute;
  right: -1em;
  content: '';
  width: 0;
  height: 0;
  border: .5em solid red;
  border-bottom-color: transparent;
  border-right-color: transparent;
}
Red Triangle

最后一步就是把 border 颜色改成和 comment 底色一样。Bingo!

Final Triangle

定位与 z-index

用好定位,还需要掌握 z-index 技术,也就是堆叠元素的次序。这里再补充一个知识点:static 定位以外的元素会根据他们在代码树中的深度依次叠放。类似于打扑克,后发的牌会压在先发的牌面上,但是他们次序是可以更改的,可以通过设置 z-index 大小调整。z-index 可正可负,值越大,显示越靠上方。

Debug 的时候,我们经常会通过给 z-index 添加一个很大的值来将被覆盖的元素显示出来;不过,我个人惨痛的经验是:绝大多数情况都是失败的。主要原因是除了 z-index,还有其他影响元素堆叠次序的因素。这里再给一个概念,叫堆叠上下文:就像一盒扑克牌,每盒牌本身就是一个上下文,而 z-index 只是作用于自己所在的牌盒里。但是堆叠上下文的产生有点复杂,很难一一道来,我这里说几个比较常见的:

  • 设定了position: absolute 及 z-index 不为auto的元素,会创建一个专属于自己后代元素的堆叠上下文
  • 透明度(opacity)小于 1 的元素需要独立渲染,它会自动触发新的堆叠上下文
  • transform 和 filter 属性会创建新的堆叠上下文

何时创建堆叠上下文,各个浏览器厂家甚至各个版本的浏览器在实现上都有较大差别,很难给出明确的标准;我自己开发的时候经常碰到这类坑,从中曲折一言难尽。当然,堆叠上下文这个知识还是能帮到我们一点:给目标元素增加 z-index 无效时,我们可以尝试给它的每一层祖先元素都增大z-index值;方法虽然有点笨,但一般都能解决问题。

小结

这次我们复习一个很原始,但经常碰到的知识点——定位布局,介绍了它的基本使用方式和经常碰到的陷阱。定位布局是所有 CSS 开发人员的入门课;后面,我还会继续介绍更复杂的水平布局、flux 布局,以及 grid 布局。敬请关注。

相关

文章同步发布于an-Onion的Github。码字不易,欢迎点赞。

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

推荐阅读更多精彩内容

  • CSS 定位 CSS有三种基本的定位机制:普通流,浮动,绝对定位(absolute, fixed):普通流是默认定...
    _空空阅读 5,642评论 0 15
  • 浮动定位 BFC 边距合并 浮动元素 div的顺序是HTML代码中div的顺序决定的。 浮动可以理解为让某个div...
    nianxiaoge阅读 686评论 0 0
  • 一:在制作一个Web应用或Web站点的过程中,你是如何考虑他的UI、安全性、高性能、SEO、可维护性以及技术因素的...
    Arno_z阅读 1,087评论 0 1
  • 大家好!我是张大妈,你们可听说过湖南有道菜名叫做永州血鸭,东安鸡?没错我就是来自这两道名菜的家乡湖南永州东安。我是...
    张梓萌_a0df阅读 1,209评论 0 0
  • 我的一战成名,全来自于我屁股底下这个伟大的板凳,他一度和月牙刀相媲美。我走在走廊里,会听到有人小声说,这就是扔板凳...
    小钢管儿阅读 317评论 0 0