当前内容所在位置(可进入专栏查看其他译好的章节内容)
- 第一章 层叠、优先级与继承(已完结)
- 第二章 相对单位(已完结)
- 第三章 文档流与盒模型(已完结)
- 第四章 Flexbox 布局(已完结)
- 第五章 网格布局(已完结)
- 【第六章 定位与堆叠上下文】 ✔️
- 6.1 固定定位 ✔️
- 6.1.1 创建一个固定定位的模态对话框 ✔️
- 6.1.2 在模态对话框打开时防止屏幕滚动 ✔️
- 6.1.3 控制定位元素的大小 ✔️
- 6.2 绝对定位(精译中 ⏳)
文章目录
《CSS in Depth》新版封面
译者按
无论是前面介绍的 Flexbox 布局还是 Grid 网格布局,页面元素的布局都是通过控制文档流实现的。本章将重点关注另一类可以脱离文档流的布局方案:定位(positioning),以及由此引出了另一个重要概念——堆叠上下文(stacking contexts)。由于新版中完全删除了浮动布局一整章(毕竟浮动原就不是用来做布局的),并且在第三章介绍常规文档流时引入了逻辑属性的重要概念,本章内容也相应作了大幅更新,增补了大量与逻辑属性相关的样式代码及最佳实践,以前冗余的样式声明现在只要一行简写就轻松搞定了。让我们从最简单的固定定位开始,一起先睹为快吧!
第六章 定位与堆叠上下文 Positioning and stacking contexts
本章概要
- 元素定位的类型:固定(fixed)定位、相对(relative)定位、绝对(absolute)定位、以及粘性(sticky)定位
- 构建模态对话框和下拉菜单
- 深入理解
z-index
和堆叠上下文- 用 CSS 绘制一个简单的三角形
我们现在已经学习了好几种页面布局方式,包括常规文档流布局、Flexbox 布局以及网格布局。本章将介绍另一项重要的技术:position
属性(property)。它可以用来构建下拉菜单、模态对话框以及现代 Web 应用的其他基本效果。
定位处理起来可能会很复杂,很多开发者对此也仅有粗浅的认知。如果没有彻底理解定位及其可能带来的后果,就很容易给自己挖坑。比如遇到某些元素被错误地放置在了其他元素的前面,而纠正这个问题却没有那么简单。
相信在考察完各式各样的定位类型后,您一定可以准确理解每一种定位的行为特征。此外,本章还将引入一个新名词,称为 堆叠上下文(stacking context),姑且算作定位问题潜在的副作用吧。深入理解堆叠上下文能帮您少走很多弯路,哪怕是不慎钻进某个页面布局的死胡同,对堆叠上下文的准确理解也可以助您快速突出重围。
position
属性的初始值为 static
。前面的章节中默认用的都是这个静态定位。如果改为其他值,我们就说该元素 已定位(positioned);而处于静态定位下的元素,我们就说它 未被定位(not positioned)。
前面章节介绍的布局方法是通过各种操作来控制文档流的行为。定位则不同:它将元素彻底从文档流中移走。这样,元素就能位于屏幕的任意位置,或者将元素放在另一个元素的前方或后方,彼此间相互重叠。
6.1 固定定位 Fixed positioning
固定定位随不如其他定位类型来得普遍,但它可能也是这当中最好理解的定位类型了,我们就从这里切入。给一个元素设置 position: fixed
,就能将其放到视口(viewport)的任意位置。这是通过搭配另外四个属性来实现的:top
、right
、bottom
以及 left
。这些属性的值决定了固定定位的元素与浏览器视口边缘的相对距离。比如,top: 3em
表示元素的上边缘距离视口顶部 3em
。
设置这四个值还间接定义了元素的宽高。例如,设置 left: 2em; right: 2em
表示元素左边缘距离视口左边 2em
,且其右边缘距离视口右边也为 2em
。因此元素的宽度等于视口总宽度减去 4em
。同理,属性 top
、bottom
与视口高度也满足这样的关系。
此外,还可以使用简写属性 inset
来设置元素位置。该属性可以非常方便地一次性设置元素四周的距离,例如 inset: 0
等价于 top: 0; right: 0; bottom: 0; left: 0
。能用 inset
属性进行简写的逻辑属性(译注:即 logical properties,详见第 3 章 3.1.2 小节内容)如下:
inset-block-start
;inset-block-end
;inset-block
—— 即inset-block-start
与inset-block-end
的简写形式;inset-inline-start
;inset-inline-end
;inset-inline
—— 即inset-inline-start
与inset-inline-end
的简写形式。
以上罗列的这些简写属性,在需要通过 top
、right
、bottom
和 left
属性提供冗长样式声明的场景下通常会很管用。inset
属性本身最多可以指定四个值,用于单独设置对应方向的距离大小。
6.1.1 创建一个固定定位的模态对话框 Creating a modal dialog with fixed positioning
本节将用这些属性来实现如图 6.1 所示的模态对话框。该对话框会在网页内容前方弹出并遮挡住网页内容,直到关闭该对话框。
定义
模态对话框(modal dialog box) 是一个出现在主页面前方的窗口。显示该窗口期间,其后面的用户界面将被全面禁用;用户必须通过某种方式与模态框进行交互方能返回主页面。
图 6.1 模态对话框示意图
通常情况下,模态对话框用于要求用户阅读一些内容,或者在进行下一步操作之前输入一些内容。如图 6.1 所示,该模态框展示了一个表单供用户订阅推送消息。实现该效果时,初始状态下可以先声明 display: none
来隐藏该弹窗;若要显示它,则可以通过 JavaScript 将 display
属性值改为 block
来实现。
下面创建一个新页面,将代码清单 7.1 中的内容加到页面的 <body>
元素中。该代码将所有内容放在两个容器元素中,并通过 <script>
标签引入一段 JavaScript 脚本,以此来实现一些基础功能。注意,只有等到正确的 CSS 样式加进来,模态框才会正常运转。
代码清单 6.1 创建模态对话框的 HTML 标记内容
<header class="top-banner">
<div class="top-banner-inner">
<p>Find out what's going on at Wombat Coffee each
month. Sign up for our newsletter:
<button id="open" type="button">Sign up</button><!-- 触发弹窗的按钮 -->
</p>
</div>
</header>
<div class="modal" id="modal" role="dialog" aria-modal="true"><!-- 模态框容器 -->
<div class="modal-backdrop"></div><!-- 模态框后面遮挡网页内容的“蒙层” -->
<div class="modal-body"><!-- 模态框内容 -->
<button class="modal-close" id="close" type="button">close</button>
<h2>Wombat Newsletter</h2>
<p>Sign up for our monthly newsletter. No spam.
We promise!</p>
<form>
<p>
<label for="email">Email address:</label>
<input type="text" name="email"/>
</p>
<p><button type="submit">Submit</button></p>
</form>
</div>
</div>
<main class="container">
<h1>Wombat Coffee Roasters</h1>
</main>
<script type="text/javascript">
const button = document.getElementById('open');
const close = document.getElementById('close');
const modal = document.getElementById('modal');
// 用户点击注册按钮时,打开模态对话框
button.addEventListener('click', function (event) {
modal.classList.add('is-open');
});
// 用户点击关闭按钮时,关闭模态对话框
close.addEventListener('click', function (event) {
modal.classList.remove('is-open');
});
</script>
代码中的第一个元素为页面顶部的横幅区,里面包含触发模态框的按钮。第二个元素则为模态框,里面包含了一个空的 modal-backdrop
元素,用于遮住页面剩余部分,让用户的注意力集中到对话框的内容上。弹框内容则放在 modal-body
元素内。
这里的 JavaScript 代码则用于在模态框中添加或移除一个 is-open
类,并通过这个样式类来切换模态框的可见性。
注意
目前 HTML 自带的
<dialog>
元素已经具备了大量模态对话框的相关功能。但为了更全面地说明固定定位的行为特征,本例并没有使用<dialog>
元素。想了解该元素的更多信息,可以参阅我的这篇文章:https://keithjgrant.com/posts/2018/01/meet-the-new-dialog-element/。
初始页面的样式设计如以下代码所示。开始定位前,还有几个地方要提前交代一下。页面中的 <body>
元素,高度设置为 200vh
,旨在令页面强制滚动;这样设计也有助于观察本章演示的定位元素与页面滚动间的交互情况。此外,还可以在 <main>
元素中多加几段内容,直到页面出现滚动条。
根据代表清单 6.2 更新样式表。该样式包括了顶部横幅及按钮的基本样式:
代码清单 6.2 页面初始样式
body {
font-family: Helvetica, Arial, sans-serif;
min-height: 200vh; /* 设置固定高度,强行让页面出现滚动条(仅用于演示) */
margin: 0;
}
button,
input {
font: inherit; /* 覆盖用户代理默认的表单字体样式 */
}
button {
padding: 0.5em 0.7em;
border: 1px solid #8d8d8d;
background-color: #eee;
border-radius: 5px;
cursor: pointer;
}
.top-banner {
padding: 0.5em;
background-color: #ffd698;
box-shadow: 0 1px 5px rgb(0 0 0 / 0.1);
}
.top-banner-inner {
width: 80%;
max-inline-size: 1000px;
margin-inline: auto;
}
.container {
width: 80%;
max-inline-size: 1000px;
margin: 1em auto;
}
.modal {
display: none; /* 默认隐藏模态对话框 */
}
.modal.is-open {
display: block; /* 当存在 is-open 类时显示模态框 */
}
上述样式还对按钮和文本输入框设置了 font: inherit
。该声明用于覆盖表单元素中一些默认的用户代理样式。这也是页面样式设计一个值得推荐的常见做法。
最后,模态框会在 display: none
的作用下初始隐藏;等到样式类 is-open
添加完毕,模态框又会在 display: block
的作用下重新显示到页面。
对于模态框本身的样式设置,需要用到固定定位两次:第一次是针对 modal-backdrop
元素,其 inset
属性值为 0
。这样,蒙层就会填满整个视口。蒙层的背景色为 rgba(0 0 0 / 0.5)
,其中红、绿、蓝三原色的值均为 0
,算出来是黑色;第四个值即“alpha”通道,代表透明度:为 0
时表示完全透明;为 1
时则表示完全不透明。这里的 0.5
表示半透明。这样一来该元素下面所有的网页内容都会变暗。
而第二次固定定位则用于样式类 modal-body
所在的元素。它的四条边都在视口内:上下两边分别距离对应的视口边缘 3em
,左右两边则与视口左右边缘保持 20%
的距离。由于背景色为白色,所以模态框呈现为一个在屏幕居中的白色盒子。此时虽然可以随意滚动页面,但背景蒙层与模态对话框主体均不会移动。
根据代码清单 6.3 更新本地样式表。
代码清单 6.3 添加模态框样式
.modal-backdrop {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-body {
position: fixed;
inset-block: 3em;
inset-inline: 20%;
padding: 2em 3em;
background-color: white;
overflow: auto;
}
打开页面后,会看到屏幕顶部有一个带按钮的淡黄色横幅。单击按钮就会弹出设置好定位的模态对话框。由于是固定定位,此时即便滚动页面,模态框仍旧保持在原位不动。
要关闭模态对话框,点击它顶部的关闭按钮(Close)即可。此时按钮的位置还有点问题,稍后再作调整。
6.1.2 在模态对话框打开时防止屏幕滚动 Preventing the screen from scrolling while the modal dialog is open
在模态对话框打开时滚动页面,虽然可以进一步观察固定定位的工作原理,但此时的用户体验并不十分理想。当模态框位于页面内容前方时,通常用户只希望与模态对话框本身进行交互,其他内容则与之无关。
要修复这问题,可以在模态对话框打开时,在 <body>
元素上设置 overscroll: hidden
即可,例如:
body.no-scroll {
overflow: hidden;
}
接着更新上述样式,并根据代码清单 6.4 所示的 JavaScript 脚本修改示例页面。该代码通过在 body
元素上添加或移除 no-scroll
样式类来切换上面提到的样式。
代码清单 6.4 禁用页面滚动的新版 JavaScript 代码
<script type="text/javascript">
const button = document.getElementById("open");
const close = document.getElementById("close");
const modal = document.getElementById("modal");
button.addEventListener("click", function (event) {
modal.classList.add("is-open");
document.body.classList.add("no-scroll");
});
close.addEventListener("click", function (event) {
modal.classList.remove("is-open");
document.body.classList.remove("no-scroll");
});
</script>
这样一来,打开模态对话框时,页面后方内容将不再触发滚动;模态框关闭后,页面滚动才重新得以恢复。
对于存在页面交互的元素,像这样通过添加或移除样式类来实现交互的做法非常强大。它既能保证 JavaScript 代码相对简单,同时又能将关键的视觉处理逻辑留在样式表中。
提示
在某些浏览器中,也可以不使用 JavaScript,直接通过伪类
:has()
实现同样的功能。这样,本例使用的选择器body.no-scroll
就要改为:has(.modal.is-open)
;后者表示仅在模态对话框存在is-open
类时才会对body
元素设置页面禁止滚动。由于 Firefox 直到 2023 年底才添加了对伪类:has()
的支持,选用这种写法可能还不太通用。有关伪类:has()
的更多信息,请参阅 附录 A(待后续翻译)。
6.1.3 控制定位元素的大小 Controlling the size of positioned elements
在定位一个元素时,不用每次都必须写全四个方向的值,也可以只设置需要的方向值,然后用 width
和/或 height
属性来决定其大小。有时还可以通过元素本身来决定大小,例如以下声明:
position: fixed;
top: 1em;
right: 1em;
width: 20%;
这段代码会将元素放置在距离视口顶部及右边 1em
的位置,宽度则为视口宽度的 20%
。它省略了 bottom
和 height
属性,元素高度则由自身内容决定。作为示例,该写法可用于将一个导航菜单固定到屏幕上。即使用户向下滚动页面,导航菜单的位置也不会改变。
因为固定定位元素从文档流中移除了,因此它将不再影响页面其他元素的位置。剩下的这些元素将按照常规文档流进行排布,就好像固定元素不存在了一样。也就是说,它们通常会流动到固定元素的下面不被看到。这对于模态框来说问题不大,毕竟我们也希望模态框在用户关闭之前始终处在最前方的中间位置。
而对于那些一直都在的元素,例如侧边导航栏,就需要注意不要让其他内容出现在它下面。要解决这个问题,给其他内容添加一个外边距的做法往往是最简单的。比如,将所有内容放入一个右外边距为 20%
的容器中,该外边距就会流动到设置了固定定位的元素下方,从而不让内容与导航栏重叠。
关于《CSS in Depth》(中译本书名《深入解析 CSS》)
第 1 版 | 第 2 版 | |
---|---|---|
读者评分 | 原版:4.7(亚马逊);中文版:9.3(豆瓣) | 原版:5.0(亚马逊);中文版:暂无,待出版 |
出版时间 | 原版:2018 年 3 月;中文版:2020 年 4 月 | 原版:2024 年 7 月;中文版:暂无,待出版 |
原价 | 原版:$44.99;中文版:¥139.00 | 原版:$59.99;中文版:暂无,待出版 |
现价 | 原版:$36.49;中文版:¥52.54 起步 | 原版:$52.09;中文版:暂无,待出版 |
原版国内预订 | 起步价 ¥461.00 | 起步价 ¥750.00 |
本专栏为该书第 2 版高分译文专栏,全网首发,精译精校,持续更新,计划今年内完成全书翻译,敬请期待!!!
标签:模态,定位,对话框,精译,元素,Depth,modal,页面 From: https://blog.csdn.net/frgod/article/details/142443139