diff --git a/.vscode/settings.json b/.vscode/settings.json index 8d6ca82..6f8a0fb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,6 +29,7 @@ "nowrap", "Osmani", "pagefind", + "Poupard", "Poupard’s", "rgba", "Screenshotting", @@ -42,6 +43,7 @@ "trackpad", "turbopack", "Unclickable", + "Verou", "Verou’s", "viewports", "WCAG", diff --git a/content/en/tips_tricks.mdx b/content/en/tips_tricks.mdx index 9c95f03..abc3cc6 100644 --- a/content/en/tips_tricks.mdx +++ b/content/en/tips_tricks.mdx @@ -241,12 +241,10 @@ html { And to polish it, we can revert the elements that shouldn’t have been inverted (such as images and videos): ```css -html { - img, - video, - iframe { - filter: invert(100%) hue-rotate(-25deg); - } +html img, +html video, +html iframe { + filter: invert(100%) hue-rotate(-25deg); } ``` diff --git a/content/zh/acknowledgements.mdx b/content/zh/acknowledgements.mdx index 3748244..cdcac62 100644 --- a/content/zh/acknowledgements.mdx +++ b/content/zh/acknowledgements.mdx @@ -1,5 +1,9 @@ # 致谢 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +这本书的想法始于 2020 年 4 月的一个笔记。我问我的妻子 Kholoud,你觉得写一本关于调试 CSS 的书怎么样?我告诉她这将是一本非常短的书(最多 60 页)。七个月后,这本书有了 300 页。Kholoud 是第一个支持这本书想法的人,她坚持认为我应该继续下去,现在我们就在这里。谢谢你,我最亲爱的人! - +第一个从社区鼓励我的人是 John Allsopp 先生。他邀请我在 Web Directions 会议上谈论这本书的主题,并且是最早的支持者之一。非常感谢! + +我想要感谢的是 Geoffrey Crofte。他很有帮助,很友善地校对了整本书,并指出了很多需要修复的地方。非常感谢! + +最后,我想感谢 Bram Van Damme,他审阅了这本书的第一稿。他指出了一些我应该改进的重要事项。谢谢你,Bram! diff --git a/content/zh/breaking_layout.mdx b/content/zh/breaking_layout.mdx index 26670e2..ec3f0a2 100644 --- a/content/zh/breaking_layout.mdx +++ b/content/zh/breaking_layout.mdx @@ -1,5 +1,155 @@ # 不同方式破坏布局 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +这整本书都是关于发现 CSS 问题并解决它们。我们能做相反的事情吗?在本节中,我们将探索破坏 CSS 布局并使其失败的不同方法。是的,你没看错。 - +## 添加长文本内容 + +正如我们所见,布局错误的一个常见原因是文本比预期的长。随机添加长文本可以揭示你没有想到的 CSS 问题。 + +![image-20250325213047635](/img/image-20250325213047635.png) + +第一个框中的文章标题很短;设计师设计时是为了让它适合。开发者复制了文本并基于此实现了组件。当我尝试添加长文本时,发生了有趣的事情!链接图标被推到了新行。 + +我们不能假设这是一个错误,但我们可以问自己几个问题: + +- 这种行为是故意的吗?也就是说,当文本变长时,它应该将其他元素推到新行吗? +- 或者这是意外的,根本不应该发生? + +一些 CSS 问题是由于设计师和开发者之间的误解而发生的。设计师没有制定所有可能的场景,开发者没有想到询问有关组件的问题。双方都有错。 + +### forceFeed.js + +幸运的是,存在帮助我们添加随机内容并测试问题的工具。[forceFeed.js](https://github.com/Heydon/forceFeed) 就是其中之一。让我们了解它是如何工作的。 + +#### 安装 + +首先,通过 `npm` 或 `bower` 安装它: + +```bash +npm install forcefeed +# or +bower install forcefeed +``` + +或者简单地从 GitHub 存储库下载 JavaScript 文件。 + +#### 包含脚本 + +在页面内容之后和 body 元素结束之前包含脚本。 + +```html + + +``` + +#### 向元素添加属性 + +向你想要添加随机内容的任何元素添加 `data-forcefeed` 属性。 + +```html +
+

+

This will be overridden

+
+``` + +第一个,`data-forcefeed="words|2"`,将根据定义的数组生成两个随机单词,`data-forcefeed="sentences|3|6"` 将生成三到六个之间的随机句子数。 + +#### 添加数组 + +```js +window.words = ['Design', 'Work', 'Awesome', 'Cool']; +window.sentences = [ + 'Can you break me?', + 'I love food and baking', + 'How are you today?', + 'When was the last time you saw mom?', +]; +``` + +#### 执行脚本 + +最后,我们需要让脚本在页面上工作。 + +```js +forceFeed({ words: window.words, sentences: window.sentences }); +``` + +这样,我们现在可以刷新页面(`Command` 或 `Control` + `R`)来查看内容更改。也许你会注意到一个破损的元素。 + +## 尝试不同语言的内容 + +如果你正在开发多语言网站,那么当一些设计组件有不同内容时,它们很可能会破坏。 + +![image-20250325213249095](/img/image-20250325213249095.png) + +我们可能有一个修改了字距(字符之间的间距)的英文标题。它可能工作得很好,但当翻译页面在从右到左(RTL)模式下有阿拉伯语内容时,文本会破坏。阿拉伯语没有字距这样的东西。 + +当我们假设按钮组件的特定最小大小时,可能会出现另一个错误。当内容被翻译成另一种语言时,它可能看起来不同。 + +![image-20250325213310756](/img/image-20250325213310756.png) + +在 Twitter 上,"Done" 按钮在英语中看起来很好。在阿拉伯语中,按钮看起来太小,不容易注意到。原因是按钮有 `min-width: 40px` 的规则。这可以通过增加按钮的最小宽度来修复。 + +这些与语言相关的错误很重要,不应该被忽略。如果你有兴趣了解更多关于这个问题的信息,我写了一个完整的指南,[RTL Styling 101](https://rtlstyling.com/)。 + +## 调整浏览器窗口大小 + +这是破坏布局并揭示其缺陷的最简单方法之一。当你调整浏览器窗口大小时,你会看到一些你通常不会注意到的问题。一个有趣的关注领域是我称之为"中间"设计案例。我在我的博客上写了一篇[详细文章](https://ishadeed.com/article/in-between/),我想在这里再次回顾这些概念。 + +> 在响应式网页设计中,通常在页面的不同变体上工作。典型的网页至少应该有两个变体,一个用于小屏幕尺寸(例如移动设备),另一个用于大屏幕(例如桌面)。很多时候,我们忘记了 `中间` 设计变体,我们最终得到一个组件或部分太宽或太窄。 + +换句话说,当你测试中间设计状态时,你会发现更多问题。相信我,你会发现一些你或团队没有考虑过的有趣问题。 + +![image-20250325213407658](/img/image-20250325213407658.png) + +这里我们有一些卡片,需要在移动设备上是一列,在平板电脑上是两列。中间状态使卡片看起来太宽,这可能影响可读性。虽然这可能看起来不像缺陷,但它是。 + +测试中间状态重要性的另一个明确例子是以下页脚设计,取自真实项目。 + +![image-20250325213427906](/img/image-20250325213427906.png) + +在中间视图中,Instagram 的社交媒体图标换行到新行。这种行为是不期望的,根本不应该发生。 + +这应该足以说明这样的问题不能被忽略。由于浏览器调整大小的简单技巧,我们可以相当容易地发现它们。 + +## 避免占位符图像 + +图像在使网页易于访问和阅读方面发挥重要作用。作为前端开发者,你的工作是为可以处理任何使用的图像的组件提供可靠的结构。例如,你可能正在开发一个具有完美封面图像和伴随文本的英雄部分。 + +![image-20250325213514475](/img/image-20250325213514475.png) + +破坏这样的组件很容易——只需更改图像。突然,你会看到文本很难阅读。我们忘记了放置一个半透明的黑色覆盖层,这会使文本易于阅读。不幸的是,一些设计师假设他们设计的实现将完全匹配他们的模型。情况并非如此。在开发过程中会发生很多变化。 + +通过更改图像并在它们上尝试不同的样式,我们可以发现隐藏的问题。 + +图像大小和尺寸是另一个值得关注的领域。作为前端开发者,你可能准备一个可用于食谱和文章等内容中图像的媒体组件。要提出的一个问题是:期望或推荐什么图像尺寸?内容管理者应该被限制为向内容管理系统(CMS)上传预定义大小的图像,还是应该自由上传他们喜欢的任何大小? + +![image-20250325213547667](/img/image-20250325213547667.png) + +我们这里有一个包含图像的媒体组件。第一个是设计师提供的默认图像,第二个是作者添加到 CMS 的。这种不一致性不好。设计师和开发者应该就图像大小达成一致,然后教作者遵循该标准。 + +如果你对图像大小没有太多控制,那么我建议使用 CSS `object-fit` 和图像的固定高度。这可以保持所有图像在相同高度内而不破坏它们。 + +```css +.media__thumb { + height: 220px; + object-fit: cover; +} +``` + +## 在 Internet Explorer 中打开 + +不,这不是玩笑。Internet Explorer 以破坏网站而闻名。如果你的网站需要在 Internet Explorer 中工作,那么你需要考虑你做出的每个 CSS 决定。例如,如果你使用 CSS grid 进行布局,那么建议使用 `flexbox` 或更旧的布局方法(`floats`、`inline-block` 等)添加后备。 + +## 在纵向和横向方向之间旋转 + +在更新我个人网站的移动菜单时,我发现了一个有趣的设计问题。像将设备从纵向旋转到横向这样简单的事情可以揭示意外问题,特别是如果一些元素是固定或绝对定位的。 + +![image-20250325213701039](/img/image-20250325213701039.png) + +"关闭"按钮是绝对定位并水平居中的。在横向方向上,按钮与导航重叠,这显然不是预期的。在两个方向上测试都很重要。 + +## 总结 + +在本章中,我们学习了如何故意破坏布局。接下来,我们将探索浏览器不一致性和实现错误。 diff --git a/content/zh/common_bugs.mdx b/content/zh/common_bugs.mdx index 736e5b6..3603bd2 100644 --- a/content/zh/common_bugs.mdx +++ b/content/zh/common_bugs.mdx @@ -1,5 +1,3408 @@ -# 通常会导致 bug 的 CSS 属性 +# 常见导致错误的 CSS 属性 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +网络浏览器在过去几年中发展了很多,这导致了更一致的网站。尽管如此,它们仍然不完美,一些问题可能会让开发者感到困惑。增加挑战的是不同的屏幕尺寸、多语言网站和人为错误。 - +当你实现设计时,你可能会因为各种原因遇到 CSS 错误,包括视觉和非视觉的,我在第 2 章中介绍过。其中一些很难追踪。 + +在本章中,我们将深入研究 CSS 属性及其问题。我们的目标是详细了解某些属性的常见错误,并学习如何正确解决它们。 + +## 盒子尺寸 + +`box-sizing` 属性控制如何计算元素的总宽度和高度。默认情况下,元素的宽度和高度仅分配给内容框,这意味着 `border` 和 `padding` 会添加到该宽度上。 + +如果你没有使用 CSS 重置文件,你可能会忘记重置 box-sizing 属性,这可能导致以下情况之一: + +- **水平滚动** + 块元素占用其父元素的全部宽度。如果它有边框或内边距,这将添加到其宽度上,这将导致水平滚动。 +- **过大的元素** + 元素的大小可能比你想要的要大。 + +为了避免这种情况,确保属性被正确重置: + +```css +html { + box-sizing: border-box; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} +``` + +有了这个,我们可以确保最重要的 CSS 属性按预期工作。值得一提的是,使 box-sizing [继承](https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/) 更好,因为它将使所有元素默认从 html 元素继承这个 box-sizing 属性。 + +## 显示类型 + +`display` CSS 属性控制元素是块元素还是内联元素。它还确定应用于其子项目的布局类型,例如 grid 或 flex。 + +当使用不当时,`display` 类型可能会让开发者感到困惑。在本节中,我们将介绍 `display` 属性可能出错的一些方式。 + +### 内联元素 + +诸如 `span` 和 `a` 之类的元素默认是内联的。假设我们想要为 `span` 添加垂直内边距: + +```css +span { + padding-top: 1rem; + padding-bottom: 1rem; +} +``` + +这不会起作用。垂直内边距对内联元素不起作用。你必须将元素的 `display` 属性更改为 `inline-block` 或 `block`。 + +`margin` 也是如此: + +```css +span { + margin-top: 1rem; + margin-bottom: 1rem; +} +``` + +这个边距不会有效果。你必须将 `display` 类型更改为 `inline-block` 或 `block`。 + +![image-20250325220454633](/img/image-20250325220454633.png) + +#### 间距和内联元素 + +每个内联元素都被视为一个单词。以下面为例: + +```html +Hello World +``` + +这将渲染 `Hello World`。注意两个单词之间的间距。这是从哪里来的?好吧,因为内联元素被视为一个单词,浏览器会自动在单词之间添加空格——就像你输入句子时每个单词之间有空格一样。 + +当我们有一组链接时,这变得更有趣: + +![image-20250325220524451](/img/image-20250325220524451.png) + +链接彼此相邻,它们之间有空格。当你处理 `inline` 或 `inline-block` 元素时,这些空格可能会造成混淆,因为它们不是来自 CSS——空格出现是因为链接是内联元素。 + +假设我们有一个内联的类别标签列表,我们希望它们之间有 8 像素的空间。 + +```html + +``` + +在 CSS 中,我们会这样添加间距: + +```css +.tag { + display: inline-block; + margin-right: 8px; +} +``` + +你会期望它们之间的空间等于 8 像素,对吧?事实并非如此。间距将是 8 像素**加上前面提到的字符间距的额外 1 像素**。以下是解决这个问题的方法: + +```css +ul { + display: flex; + flex-wrap: wrap; +} +``` + +通过向父元素添加 `display: flex`,额外的间距将消失。 + +### 块元素 + +`block` 显示类型是某些 HTML 元素的默认值,例如 `div`、`p`、`section` 和 `article`。在某些情况下,我们可能需要应用 `block` 显示类型,因为元素是内联的,例如: + +- 表单标签和输入, +- `span` 和 `a` 元素。 + +当 `display: block` 应用于 `span` 或 `a` 时,它会正常工作。但是,当它应用于输入时,它不会按预期影响元素。 + +```css +input[type='email'] { + display: block; /* The element does not take up the full width. */ +} +``` + +原因是表单元素是**替换元素**。什么是替换元素?它是一个 HTML 元素,其宽度和高度是预定义的,没有 CSS。 + +![image-20250325220636095](/img/image-20250325220636095.png) + +要覆盖该行为,我们需要在表单元素上强制全宽。 + +```css +input[type='email'] { + display: block; + width: 100%; +} +``` + +除了表单输入之外,还有其他替换元素,包括 `video`、`img`、`iframe`、`br` 和 `hr`。以下是关于替换元素的一些有趣事实: + +- 不可能对替换元素使用伪元素。例如,向输入添加 `:after` 伪元素是不可能的。 +- 替换元素的默认大小是 300 x 150 像素。如果你的页面有 `img` 或 `iframe` 并且由于某种原因没有加载,浏览器将给它这个默认大小。 + +考虑以下示例: + +```css +img { + display: block; +} +``` + +我们有一个带有 `display: block` 的图像。你期望它会占用其容器的全部宽度吗?它不会。你需要通过添加以下内容来强制这样做: + +```css +img { + display: block; + width: auto; + max-width: 100%; +} +``` + +值得[提及](https://bitsofco.de/styling-broken-images/)的是,当图像加载失败时,它不被视为替换元素。你实际上可以向它添加 `::before` 和 `::after` 伪元素: + +```css +img::after { + content: 'The image didn't load'; +} +``` + +#### 图像下方的间距 + +你是否注意到你添加的 `img` 下方有一点空间?你没有添加边距或任何东西。空间存在是因为 `img` 被视为内联元素,这类似于有一个字符在其下方有一些空间。 + +![image-20250325220721692](/img/image-20250325220721692.png) + +要修复这个问题,向图像添加 `display: block`。间距将被移除。 + +#### `legend` 元素 + +如果你使用 `fieldset` 来分组表单输入,添加一个 `legend` 元素。默认情况下,它不会占用其父元素的全部宽度,除非你强制它。 + +```html +
+ What's your favorite meal? + + + + +
+``` + +`legend` 元素是块级的,但其宽度将保持不变,因为它默认具有 `min-width: max-content`,这意味着它具有其文本内容的宽度。要使其全宽,请执行以下操作: + +```css +legend { + width: 100%; +} +``` + +### 使用 `display` 与定位元素 + +当元素被定位时(`position` 不是 `static`),`display` 属性的行为会有所不同。考虑以下示例: + +```css +.element { + position: absolute; + display: inline; +} +``` + +即使我们将 `display` 设置为 `inline`,元素也会表现得像块元素。这是因为定位元素会自动变成块级元素。 + +这种行为适用于以下定位值: +- `absolute` +- `fixed` +- `sticky`(在某些情况下) + +这意味着你可以为定位元素设置宽度和高度,即使它们的 `display` 值是 `inline`。 + +```css +.element { + position: absolute; + display: inline; + width: 200px; /* 这会起作用 */ + height: 100px; /* 这也会起作用 */ +} +``` + +### 内联元素的对齐 + +内联元素的垂直对齐可能会令人困惑。默认情况下,内联元素与其父元素的基线对齐。 + +```css +.icon { + vertical-align: middle; +} +``` + +`vertical-align` 属性只对内联或表格单元格元素有效。如果你想要垂直居中一个块元素,你需要使用其他方法,如 flexbox 或 grid。 + +### CSS 文件中的内联显示覆盖 + +有时,你可能会遇到这样的情况:你在 CSS 文件中设置了 `display: block`,但元素仍然表现为内联元素。这通常是因为有内联样式覆盖了你的 CSS: + +```html +
Content
+``` + +```css +.my-element { + display: block; /* 这会被内联样式覆盖 */ +} +``` + +内联样式具有更高的特异性,会覆盖 CSS 文件中的样式。 + +### 浮动和块显示 + +当元素被浮动时,它会自动变成块级元素,无论其原始 `display` 值如何: + +```css +.element { + float: left; + display: inline; /* 这会被忽略,元素会表现为块级 */ +} +``` + +这意味着浮动的内联元素可以设置宽度和高度。 + +### 浮动和 Flex 显示 + +如果一个元素同时设置了 `float` 和 `display: flex`,`float` 属性会被忽略: + +```css +.element { + display: flex; + float: left; /* 这会被忽略 */ +} +``` + +Flexbox 和 Grid 布局会使 `float` 属性失效。 + +### 显示和隐藏 `br` 元素 + +`br` 元素用于创建换行。有时,你可能想要在某些情况下隐藏它: + +```css +br { + display: none; /* 在桌面上隐藏换行 */ +} + +@media (max-width: 768px) { + br { + display: block; /* 在移动设备上显示换行 */ + } +} +``` + +这对于响应式设计很有用,你可能希望在不同屏幕尺寸上有不同的文本换行行为。 + +### 避免使用显示类型的情况 + +有一些情况下你应该避免更改 `display` 类型: + +#### 隐藏表单的输入标签 + +不要使用 `display: none` 来隐藏表单标签,因为这会影响可访问性: + +```css +/* 不好的做法 */ +label { + display: none; +} +``` + +#### 样式化复选框 + +不要尝试通过更改 `display` 类型来样式化复选框: + +```css +/* 不好的做法 */ +input[type="checkbox"] { + display: block; + width: 50px; + height: 50px; +} +``` + +相反,使用伪元素或自定义样式方法。 + +## 边距 + +如果页面上有两个或更多元素彼此靠近,用户会认为它们彼此相关。`margin` CSS 属性对于帮助我们使设计看起来更有序和一致很重要。 + +### 边距折叠 + +这是边距最常见的问题之一。假设你有两个元素,上面的元素有 `margin-bottom`,下面的元素有 `margin-top`。两个值中较大的那个将用作元素之间的边距,另一个将被浏览器忽略。 + +![image-20250325221203681](/img/image-20250325221203681.png) + +```html +
+
+``` + +```css +.item-1 { + margin-bottom: 50px; +} +.item-2 { + margin-top: 30px; +} +``` + +混合顶部和底部边距会导致问题。为了避免这种情况,使用单向边距——例如,为所有元素添加 `margin-bottom`。注意,如果元素是 flexbox 或 grid 容器的一部分,那么边距不会折叠。 + +### 边距和内联元素 + +如 `display` 部分所述,诸如 `span` 之类的内联元素不接受垂直边距,直到它们的显示类型被更改。确保垂直边距添加到正确类型的元素上。 + +### 预防性边距 + +我称之为"预防性"边距,因为它就是这样的。假设我们有两个元素: + +![image-20250325221240479](/img/image-20250325221240479.png) + +我们向其中一个元素的右侧或左侧添加边距(在这种情况下,向标题的右侧),以防其内容变得太长并使元素太靠近相邻的元素。如果标题太长,边距会防止它粘到图标上。 + +### 居中元素 + +因为 `margin: auto` 是居中元素的流行方法,重要的是要提到它只适用于块级元素。 + +```css +span { + width: 500px; + margin: 0 auto; +} +``` + +除非 `span` 更改为块级元素,否则这不会起作用。 + +### 自动边距和定位 + +当元素被绝对定位时,可以使用 `margin: auto` 来水平和垂直居中,而无需使用变换或其他 CSS 技术(如 flexbox)。 + +![image-20250325221310201](/img/image-20250325221310201.png) + +```css +.element { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + width: 120px; + height: 120px; + margin: auto; +} +``` + +在元素上设置 `width` 和 `height` 使得仅使用 `margin: auto` 就可以居中项目。 + +### 自动边距和 Flexbox + +使用 flexbox,我们可以使用 `auto` 边距将元素推到一个方向的最远处。 + +![image-20250325221342358](/img/image-20250325221342358.png) + +按钮有规则 `margin-left: auto`,这将其推到最右边。Flexbox 和自动边距在这种用途上配合得很好。我们可以使用这种技术来对齐元素而无需额外的标记。 + +## 内边距 + +`padding` 属性在元素**内部**添加空间。这就是它与 `margin` 的区别。关于内边距有一些误解。让我们探索它们。 + +### 使用内边距与高度 + +假设你有一个固定高度的元素,比如按钮。控制元素内的垂直间距可能会令人困惑,因为大的内边距值可能会将文本向下推并破坏按钮。 + +![image-20250325221415666](/img/image-20250325221415666.png) + +在左侧的按钮中,注意文本被推得太远了。原因是 CSS 中的这个: + +```css +.button { + height: 40px; + padding-bottom: 10px; +} +``` + +`button` 元素永远不应该给定固定高度。这会使事情变得复杂,控制按钮会更困难。相反,你应该使用垂直 `padding`。 + +当处理在其字符中有额外间距的 Web 字体时,你可能会遇到这种情况。在这种情况下,你可能需要调整顶部或底部内边距以垂直居中按钮的文本。 + +![image-20250325221443080](/img/image-20250325221443080.png) + +```css +.button { + padding: 3px 16px 8px 16px; +} +``` + +在这里,我们调整了内边距,以便文本可以在按钮中垂直居中。 + +### 内边距和内联元素 + +如 `display` 部分所述,除非元素的显示类型不是 `inline`,否则垂直内边距不会起作用。 + +### 内边距简写 + +内边距的简写按顺序为上、右、下、左。有时使用起来令人困惑,`margin` 也是如此。你可能最终会做以下事情: + +```css +.element { + padding-top: 20px 20px 0 20px; + /* … instead of: */ + padding: 20px 20px 0 20px; +} +``` + +这将是一个错误。`padding-top` 属性只接受一个值,所以写四个值会使规则无效。这可能是内边距不按你预期工作的原因。确保输入正确的属性名称。 + +记住 `padding` 和 `margin` 简写的正确顺序令人困惑,即使对于有经验的前端开发者也是如此。时钟是记住它的简单方法: + +![image-20250325221515807](/img/image-20250325221515807.png) + +记住从 `top` 开始,其余的会跟随。 + +### 基于百分比的内边距 + +使用百分比作为内边距是可以的,但要使其按你期望的方式工作,请记住它基于元素的宽度工作。 + +```css +.element { + width: 200px; + padding-top: 10%; +} +``` + +此元素的 `padding-top` 的计算值是 20px。当你调试具有基于百分比的内边距的元素时,记住它是如何计算的。 + +值得一提的是,在旧版本的 Firefox 中,flex 项目的顶部和底部基于百分比的内边距被不同地处理。Firefox 使用元素的高度而不是宽度来确定内边距的值。这个[问题在 Firefox 61 中得到修复](https://www.bram.us/2017/07/30/vertical-marginspaddings-and-flexbox-a-quirky-combination/)。 + +## 宽度属性 + +设置宽度是网页设计中最重要的事情之一。我们可以显式或隐式设置宽度。在本节中,我们将介绍 `width` 可能令人困惑的情况。 + +### 内联元素不接受宽度或高度 + +诸如 `span` 之类的内联元素不会接受 `width` 或 `height` 属性。这可能令人困惑。元素只有在其显示设置为 `inline` 以外的其他内容(如 `inline-block` 或 `block`)时才接受 `width` 和 `height`。 + +### 不推荐固定宽度 + +当在元素上使用固定宽度时,很可能会在移动设备上导致水平滚动。使用 `max-width` 更好,因为它会防止元素比视口更宽。 + +![image-20250325221623405](/img/image-20250325221623405.png) + +这里我们有一个标题列表,以及一个描述。描述需要有最大宽度以保持每行字符数易于阅读。如果你为文本使用固定宽度,你会注意到移动设备上的水平滚动。我花了五分钟想知道问题的原因,然后才确定固定宽度是罪魁祸首。 + +### 图像的全宽 + +默认情况下,HTML `img` 将根据其内容调整大小。为了防止图像比视口大,我们可以设置 `width` 属性。 + +```css +img { + width: 100%; +} +``` + +使用 `width: 100%`,`img` 的宽度将等于其父元素的宽度。但是,有时我们不想要那种行为。这里有一个更好的替代方案,即设置 `max-width`。 + +```css +img { + max-width: 100%; + height: auto; +} +``` + +上述方法确保以下内容: + +- 小图像(比如,650 x 250 像素)不会占用宽父元素(比如,1500 像素)的全宽。想象这样的图像占用那个容器!它会看起来像素化。 +- 另一方面,如果图像比视口宽,那么其宽度将等于其父元素的 100%。 + +### 使用 `100%` 与 `auto` 作为宽度 + +诸如 `div` 和 `p` 之类的块级元素的初始宽度是 `auto`,这让元素占用其父元素的全宽。在某些情况下,你可能需要 `div` 不占用全宽。 + +```css +div { + width: 50%; + margin: 20px; +} +@media (min-width: 800px) { + div { + width: 100%; + } +} +``` + +此元素的宽度是其父元素的 `50%`。当视口足够大时,我们希望它占用全宽。将宽度设置为 `100%` 会导致其内容占用其父元素的全宽而不计算 `margin`。 + +![image-20250325221726700](/img/image-20250325221726700.png) + +这是一个问题。为了解决它,我们应该使用 `auto` 而不是 `100%`。根据 CSS 规范: + +> 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block + +注意,当使用 `box-sizing: border-box` 时,`padding-left` 和 `padding-right` **不**包含在计算中。 + +将宽度设置为 `auto` 将导致内容框的宽度是内容本身减去边距、内边距和边框。 + +```css +@media (min-width: 800px) { + div { + width: auto; + } +} +``` + +我写了一篇关于 CSS 中 `auto` 的[详细文章](https://ishadeed.com/article/auto-css/),如果你想深入了解这个主题,值得查看。 + +### 带有 `position: absolute` 的图像不需要宽度或高度 + +你可能不会想到这一点,但知道这一点很有趣。考虑以下内容: + +```html +
+ +
+``` + +```css +.media { + position: relative; + width: 300px; + height: 200px; +} + +.media img { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; +} +``` + +你可能期望图像会占用其父元素的全宽,因为它相对于四个边绝对定位。好吧,那是错误的。如果图像足够大,它会突破其父元素。 + +为了防止这种情况发生,为图像设置宽度和高度。 + +```css +.media img { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; +} +``` + +## 高度属性 + +### 基于百分比的全高度 + +在 CSS 中设置基于百分比的高度起初可能看起来直观,但事实并非如此。除非其父元素的高度被**明确**定义,否则你无法为元素设置基于百分比的高度。 + +```css +.parent { + padding: 2rem; +} + +.child { + height: 100%; +} +``` + +子元素不会占用其父元素的 `100%`。以下是如何使其占用全高度: + +```css +.parent { + height: 200px; + padding: 2rem; +} +``` + +这样,子元素的基于百分比的高度值将基于某些东西,它将按预期工作,即使不推荐使用绝对 `height` 值。 + +### 填充可用剩余空间的高度 + +假设我们有一个卡片网格,我们使用 CSS grid 来布局它们。 + +```html +
+
+ +
+

+

+

+
+
+
+
+``` + +```css +.media-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(265px, 1fr)); + grid-gap: 1rem; +} +``` + +默认情况下,CSS grid 会使卡片的高度相等,这很有用。但有一个问题,当一张卡片的标题比另一张长时,`.card__content` 元素的高度会不同。 + +![image-20250325221856372](/img/image-20250325221856372.png) + +为了解决这个问题,我们需要使卡片成为 flex 容器,然后强制 `.card__content` 填充可用空间。 + +```css +.card { + display: flex; + flex-direction: column; +} +.card__content { + flex-grow: 1; +} +``` + +现在,我们想要使 `.card__content` 元素成为 flex 容器。最后,`.card__author` 元素将被给予 `margin-top: auto`,这样它就可以始终在卡片的基线上。 + +```css +.card__content { + flex-grow: 1; + display: flex; + flex-direction: column; +} +.card__author { + margin-top: auto; +} +``` + +![image-20250325221932305](/img/image-20250325221932305.png) + +### 基于百分比的宽度和无高度 + +有时,你需要一种方法来调整元素大小而不必同时更改宽度和高度。我喜欢在 Twitter 网站上找到的一个模式,它通过仅更改 `width` 属性来调整头像大小。 + +```html + +
+ +
+``` + +```css +.avatar { + position: relative; + width: 25%; + display: block; +} + +.avatar-aspect-ratio { + width: 100%; + padding-bottom: 100%; +} + +.avatar img { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; +} +``` + +通过添加一个元素(`.avatar-aspect-ratio`)并使用 `padding-bottom: 100%` 规则,这最终等于头像的 `width`,结果将是一个正方形。图像本身是绝对定位的。 + +![image-20250325222011812](/img/image-20250325222011812.png) + +注意只有宽度属性被调整大小;高度会跟随。有关该技术的更多详细信息,这里有一篇关于 CSS Tricks 的[很棒的文章](https://css-tricks.com/fluid-width-video/)。 + +### 高度和视口单位 + +我们可以使用视口单位与 `width` 或 `height` 来使元素占用视口的全宽或全高。我们将在这里处理视口高度单位。 + +```css +body { + height: 100vh; +} +``` + +这将使 `body` 元素占用视口的全高度。但是,移动设备上的 Safari 有一个问题,因为它在计算中不包括地址栏,这导致 `height` 的值更高。 + +一个解决方案是通过使用 `innerHeight` 方法[从 JavaScript 获得帮助](https://css-tricks.com/the-trick-to-viewport-units-on-mobile/)。 + +```javascript +// Get the viewport height and multiply it by 1% to get the vh value. +let vh = window.innerHeight * 0.01; +// Set the vh value in the CSS property +document.documentElement.style.setProperty('--vh', `${vh}px`); +``` + +然后,我们在 CSS 中使用它: + +```css +.my-element { + height: 100vh; /* Fallback for browsers that do not support custom properties */ + height: calc(var(--vh, 1vh) * 100); +} +``` + +通过获取浏览器的 `innerHeight`,我们可以在 `height` 属性中使用该值。 + +这里有一个不使用 JavaScript 的解决方案,我从 [@AllingsSmitty](https://twitter.com/AllThingsSmitty/status/1254151507412496384) 那里学到的。 + +```css +.my-element { + height: 100vh; + height: -webkit-fill-available; +} +``` + +通过为 `height` 使用内在值,浏览器将只填充可用的垂直空间。缺点是这在 Chrome 中会出现问题,因为该浏览器也理解 `-webkit-fill-available` 并且不会忽略它。我的建议是**不要使用**这个解决方案,直到其行为在浏览器中一致。 + +## 设置最小或最大宽度 + +在 CSS 中,我们有 `min-width` 和 `max-width` 属性。让我们探索它们发生的常见错误和混淆点。 + +### 最小宽度 + +#### 按钮的最小宽度 + +为按钮元素设置最小宽度时,请记住它应该在多语言布局中工作。 + +![image-20250325222124553](/img/image-20250325222124553.png) + +在这里,我们有一个带有 `min-width: 40px` 的按钮。它对英语布局完美工作。但是,当翻译成阿拉伯语时,由于其最小宽度,它变得非常小。这个例子来自 Twitter 的网站。这里的问题是按钮的宽度太短,这会使用户更难注意到它。 + +为了防止这样的问题,总是用多种内容进行测试。即使网站只针对一种语言,用不同内容进行测试也不会有害。 + +#### 最小宽度和内边距 + +另一个混淆点是当我们只依赖 `min-width` 时。例如,带有 `min-width` 的按钮可能看起来不错,因为内容适合大小。但是,当按钮的文本变得有点长时,文本会靠近边缘。 + +![image-20250325222151597](/img/image-20250325222151597.png) + +这样做的原因是作者盲目地依赖 `min-width` 并且没有考虑内容可能更长。 + +#### 哪个具有更高的优先级:`min-width` 还是 `max-width`? + +当为元素同时使用 `min-width` 和 `max-width` 时,知道其中哪一个是活动的可能会令人困惑。让我们澄清这一点。 + +如果 `min-width` 的值大于 `max-width` 的值,那么它将被作为宽度。 + +![image-20250325222226006](/img/image-20250325222226006.png) + +```css +.element { + width: 100px; + min-width: 50%; + max-width: 100%; +} +``` + +#### 重置 `min-width` + +让我们探索在 CSS 中重置 `min-width` 属性的方法。 + +##### 设置为 `0` + +`min-width` 的默认值是 `0`,但这对于 flex 子项目是不同的。 + +flex 子项目的 `min-width` 是 `auto`,如前所述。 + +##### 设置为 `initial` + +`initial` 值将重置为浏览器的初始值,这将是 `0` 或 `auto`,取决于项目是否是 flex 子项。 + +一般来说,我建议使用 `initial` 来重置。但是,根据用例,你可能需要为 flex 子项目使用 `min-width: 0`。 + +### 最大宽度 + +#### 页面包装器的最大宽度 + +`max-width` 属性的常见用例是将其作为元素的约束添加,例如页面包装器或容器。 + +```css +.wrapper { + max-width: 1200px; + margin: 0 auto; +} +``` + +![image-20250325222354252](/img/image-20250325222354252.png) + +这可能看起来没问题,直到你将屏幕调整为小于 1200 像素。然后你会注意到 `.wrapper` 的子元素粘在左右边缘,这不是我们想要的。确保为页面容器添加内边距,以便它在移动设备上有水平偏移。 + +```css +.wrapper { + max-width: 1200px; + margin: 0 auto; + padding-left: 16px; + padding-right: 16px; +} +``` + +![image-20250325222414906](/img/image-20250325222414906.png) + +#### 最大宽度的百分比 + +当为最大宽度使用百分比值时,通常会在移动设备上忘记它。 + +![image-20250325222442931](/img/image-20250325222442931.png) + +```css +.element { + max-width: 50%; +} +``` + +这在笔记本电脑或台式机上可能工作得很顺利。但是,在移动设备上,`50%` 可能是 150 或 200 像素,取决于视口的宽度。无论如何,计算的像素值都会很小,所以考虑移动尺寸很重要。 + +```css +@media (min-width: 800px) { + .element { + max-width: 50%; + } +} +``` + +好多了。一旦有足够的空间,媒体查询将激活 `50%` 宽度。 + +#### 基于内容设置最大宽度 + +这可以被视为常见错误或常见需求,所以我将尝试将它们都解决。有时,你需要根据你拥有的内容设置最大宽度。当内容变化时,这可能很棘手。错误是基于内容设置宽度。 + +![image-20250325222514820](/img/image-20250325222514820.png) + +我们有一个带有标题和描述的部分。我们希望包装器与内容一样宽,所以我们将尝试以像素为单位设置它。 + +```css +.wrapper { + max-width: 567px; +} +``` + +在 CSS 中使用像 `567px` 这样的硬编码值不是一个好的做法,因为当内容更改时这很容易失败。解决方案是使用内在的 CSS 值。 + +```css +.wrapper { + max-width: max-content; +} +``` + +这样,包装器的宽度将调整到内容,而无需我们硬编码值。 + +#### 在包装器中约束图像 + +`max-width` 的常见用例是约束 `img` 不要比其容器大。因为 `img` 元素是**替换元素**,其大小基于其内容。 + +有时,大图像可能会超出其容器。解决方案只是使用前面提到的技术: + +```css +img { + max-width: 100%; + height: auto; +} +``` + +#### 重置 `max-width` + +假设我们需要为特定视口大小或条件重置 CSS 属性。在 CSS 中有几种重置 `max-width` 的方法。 + +##### `none` 关键字值 + +`none` 值对大小不设限制,这正是重置属性的目标。 + +##### `initial` 关键字值 + +这将属性设置为其初始默认值,即 `none`。 + +##### `unset` 关键字值 + +如果属性从其父元素继承,`unset` 关键字将值重置为继承值。如果不是,值将是 `initial`。 + +我建议使用 `none` 关键字,因为它是最清楚的,你不必考虑后果。 + +### 最小高度 + +#### 为可变内容设置最小高度 + +CSS 中的一个常见挑战是为具有将更改或由用户输入的内容的部分设置固定高度。设置固定高度可能会在内容太长时破坏部分。 + +使用 `min-height` 可以解决这个问题。我们设置一个最小高度值,如果内容增长更长,部分的高度将扩展。 + +![image-20250325222610106](/img/image-20250325222610106.png) + +注意内容如何垂直溢出部分。这是因为它有固定高度。 + +```css +section { + min-height: 450px; + /* … instead of… */ + /* height: 450px; */ +} +``` + +这解决了问题。 + +#### 为定位元素设置最小高度 + +通常,模态组件包含诸如表单元素、文本、图像等内容。如果内容太短,模态高度会折叠,布局会看起来很糟糕。 + +![image-20250325222632142](/img/image-20250325222632142.png) + +设置 `min-height` 更好,这样模态就不能低于该值。因此,我们将防止任何不需要的行为。 + +```css +.modal-body { + min-height: 250px; /* 250px is just an example. Tweak according to +your project's needs. */ + padding: 16px; +} +``` + +### 最大高度 + +#### 为定位元素设置最大高度 + +让我们从这个问题开始,因为它与前面关于模态内容的问题相关。如果模态的内容太高怎么办?模态的高度将等于视口的高度,这不好。 + +![image-20250325222737756](/img/image-20250325222737756.png) + +所以,我们不仅应该使用 `min-height`,还应该使用 `max-height`,这样无论内容多高,它都不会超过我们设置的值。 + +```css +.modal-body { + min-height: 250px; + max-height: 600px; + overflow-y: auto; +} +``` + +不要忘记通过添加 `overflow-y: auto` 使模态可滚动。没有它,内容会超出其父元素。 + +#### 设置基于百分比的最大高度 + +这个也是相关的。我们以像素为单位设置最大高度,记得吗?这会起作用,但它有一个陷阱。如果视口的高度太短而 `max-height` 的值大于它怎么办? + +更好的解决方案是为 `max-height` 使用百分比。这样,无论内容的长度如何,模态的高度都不会超过该值。 + +```css +.modal-body { + min-height: 250px; + max-height: 90%; + overflow-y: auto; +} +``` + +#### 过渡元素的高度 + +我听到的一个常见问题是如何过渡元素的高度属性。不幸的是,该属性不可动画,因为通常我们想要将高度从 `0` 动画到 `auto`,而值 `auto` 对动画无效。可以使用 JavaScript,通过添加高度作为内联样式并递增它。 + +这里有一个 CSS 解决方案,有点像黑客,但它有效。通过使用 `max-height`,我们可以设置最大值,它会过渡。 + +```css +.element { + max-height: 0; + overflow: hidden; /* This prevents child elements from appearing +while the element's height is 0. */ + transition: max-height 0.25s ease-out; +} + +.element.is-active { + max-height: 200px; +} +``` + +#### 最大高度取决于元素的定义高度 + +如果元素有 `max-height: 90%`,那么它需要以下之一才能工作: + +- 具有明确定义 `height` 的父元素或包含块, +- 绝对定位的元素。 + +当你应用带有百分比值的 `max-height` 时,确保满足上述条件之一。否则,计算值将是 `none`。 + +## 简写与完整属性 + +如你所猜测的,简写是 CSS 属性的简短版本,完整属性是长版本。 + +```css +.element { + padding: 10px; +} +``` + +这是一个简写属性。这个 `padding` 有四个值,所有值都是 `10px`。我们可以这样写四个值: + +```css +.element { + padding: 10px 10px 10px 10px; +} +``` + +但因为它们都相等,所以没有必要写出来。完整版本看起来像这样: + +```css +.element { + padding-top: 10px; + padding-right: 10px; + padding-bottom: 10px; + padding-left: 10px; +} +``` + +当为元素设置背景时,它将是纯色或图像。我们必须注意如何编写它。假设我们写这个: + +```css +.element { + background: green; +} +``` + +我们会得到绿色,但实际上我们在做这个: + +```css +.btn { + background-image: initial; + background-position-x: initial; + background-position-y: initial; + background-size: initial; + background-repeat-x: initial; + background-repeat-y: initial; + ... and so on ... + background-color: green; +} +``` + +因为 `background` 是简写属性,当添加时它会将所有其他背景属性**重置**为其初始值。这会给你的布局引入一些令人困惑的错误。在这种情况下使用完整属性。 + +使用 `margin` 时我多次遇到类似情况。 + +```css +.wrapper { + margin: 0 auto; +} +``` + +如果我们之前为包装器定义了 `margin-top` 和 `margin-bottom` 怎么办?我们的新 CSS 声明会将它们重置为 `0`。相信我,当你工作了一整天并犯了这样的错误时,你可能会花一个小时才意识到为什么会发生这种情况。只有在需要时才使用简写属性,正如 [CSS Wizardy 建议的](https://csswizardry.com/2016/12/css-shorthand-syntax-considered-an-anti-pattern/)。这里是一个正确的用法: + +```css +.button { + padding: 10px 12px 15px 10px; +} +``` + +我们可能会以这种方式为按钮设置 `padding`,因为它有一个奇怪的字体导致一些对齐问题。 + +## 定位 + +CSS 定位问题通常发生是因为不正确使用 position 属性,无论是因为作者没有完全理解它还是因为普通的旧错误。 + +### 使用定位偏移属性 + +当使用 `top`、`right`、`bottom` 或 `left` 属性之一时,确保位置不是 `static`(默认值)。如果是,那么 `offset` 属性对元素不会有任何效果。 + +### 图标对齐 + +有时,将图标与其旁边的文本对齐是一个挑战。即使使用像 `vertical-align` 和 `flexbox` 这样的属性,它仍然不容易。这种问题的原因各不相同,但最令人恼火的原因是有一个在其字符上方和下方有空间的字体。在这种情况下,使用 `position` 是一个好的解决方案。 + +![image-20250325222910743](/img/image-20250325222910743.png) + +```css +.icon { + position: relative; + top: 3px; +} +``` + +我们将图标向底部推 3 像素。当然,我们在这里硬编码值,但在这种情况下这是有效的用法。缺点是当字体更改时,图标的对齐可能会破坏,所以要注意这一点。 + +### 使用 `width` 和 `height` 属性 + +我注意到为定位元素使用 `width` 或 `height` 属性的不必要模式。 + +```css +.element { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; +} +``` + +这个元素已经定位到四个边。它已经占用了全部空间,所以设置宽度和高度是不需要的。 + +**提醒:**这不适用于 HTML 替换元素,如 `img`。如果上面的元素是大图像,那么需要宽度和高度,否则你可能会遇到水平滚动。 + +### 内边距如何为定位元素工作 + +定位元素可以有间距,使其从其父元素的四个边偏移。如果我们希望元素有 10 像素偏移,那么 `top`、`right`、`bottom` 和 `left` 的每个属性都应该有 `10px` 的值。 + +涉及内边距和偏移属性有一些棘手的情况。 + +![image-20250325223007048](/img/image-20250325223007048.png) + +这里我们有一个卡片,其页脚从左、右和底部边偏移 12 像素。我们如何在 CSS 中做到这一点? + +```css +.card-footer { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 0 12px 12px 12px; +} +``` + +我们将页脚定位到四个边,我们依赖 `padding` 而不是偏移属性来获得 12 像素。这样,在测试时更容易控制。你可能需要调整 `padding` 值。 + +### 使用 `z-index` + +`z-index` 属性负责设置定位元素及其后代在 z 轴上的顺序。除非位置设置为 `static` 以外的其他内容,或者元素具有触发新堆叠上下文的属性(如:`transform`、小于 1 的 `opacity` 等),否则它不起作用。我们稍后会深入了解。 + +### 重置位置 + +将元素的位置从 `absolute` 或 `fixed` 重置为另一个值可能会令人困惑。例如,如果我们有一个只应在移动设备上为 `absolute` 的元素,我们可以执行以下操作: + +```css +.element { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +@media (min-width: 700px) { + .element { + position: static; + } +} +``` + +将值设置为 `static` 将重置 `position` 的值。完成后,保留 `top`、`right`、`bottom` 和 `left` 的值是可以的,因为它们不会有任何效果。 + +## `z-index` 属性 + +`z-index` 属性使我们能够使用数值控制 HTML 元素如何彼此定位在顶部。起初,可能看起来将元素定位在其兄弟元素或父元素之上就像将 `z-index` 设置为 `999999` 一样简单。情况并非总是如此——`z-index` 有某些规则要遵循。让我们探索它的最常见问题。 + +### 忘记设置位置 + +`z-index` 属性不会与位置的默认值 `static` 一起工作。值必须是 `relative`、`absolute`、`fixed` 或 `sticky`。确保设置位置或仔细检查堆叠上下文是否存在。 + +### 默认堆叠顺序 + +在 HTML 中,位于容器底部的元素将定位在前面的元素之上。 + +```html +
+ +
+
+
+ +
+``` + +![image-20250325223056413](/img/image-20250325223056413.png) + +希望这个现实生活的例子使它更清楚。笔记本电脑在桌子上,桌子在地板上。也就是说,默认情况下,最后一个子元素将定位在其兄弟元素之上。不理解这一点,事情可能会变得令人困惑。 + +伪元素也是如此。在 HTML 标记中,注意 `::before` 和 `::after` 伪元素如何添加到 `.home` 元素。`::after` 元素将默认在布局中出现在顶部,`::before` 元素将在正常堆叠上下文中出现在其他所有内容下面。 + +### 创建堆叠上下文的 CSS 属性 + +一些属性会触发新的堆叠上下文。CSS 规范列出了触发堆叠上下文的属性。它们包括除 `static` 以外的位置值、`opacity`、`transform`、`filter`、`perspective`、`clip-path`、`mask` 和 `isolation`。 + +```html +
+
+``` + +```css +.elem-1 { + position: absolute; + left: 10px; + top: 20px; +} + +.elem-2 { + opacity: 0.99; +} +``` + +哪个元素会出现在另一个之上?在这种情况下,`elem-2` 会在顶部,因为添加 `opacity` 值会触发新的堆叠上下文,从而将 `elem-2` 放在 `elem-1` 之上,即使 `elem-1` 是绝对定位的。 + +![image-20250325223127123](/img/image-20250325223127123.png) + +当 `z-index` 没有按预期行为时,检查是否有任何属性触发了新的堆叠上下文。 + +### 元素无法出现在其父元素兄弟元素之上 + +对于这个问题,让我们先从 HTML 开始,这样你可以更好地想象它。 + +```html +
+ + +
+
+``` + +```css +.wrapper { + position: relative; + z-index: 1; +} +.modal { + position: fixed; + z-index: 100; +} + +.help-widget { + position: fixed; + z-index: 10; +} +``` + +从标记中,你能判断哪个元素会在顶部吗?很容易认为 `.modal` 元素会在顶部,因为它有更高的 `z-index`,对吧? + +![image-20250325223309752](/img/image-20250325223309752.png) + +错误。`.modal` 元素是 `.wrapper` 的子元素,而 `.help-widget` 元素是 `.wrapper` 的兄弟元素。将 `.modal` 定位在 `.help-widget` 之上是不可能的,除非我们更改标记: + +```html +
+ +
+ +
+``` + +这样,我们可以将 `.modal` 定位在 `.help-widget` 之上。作为经验法则,如果你有一个元素,如模态或弹出窗口,将其保持在页面主包装器之外,以避免这种混淆。 + +### 元素浮动在其兄弟元素之上 + +一个棘手的情况是当元素的 `z-index` 比固定头部更高时。这个问题很容易让我们犯错,因为它不容易注意到。 + +![image-20250325223340059](/img/image-20250325223340059.png) + +这些卡片在左上角有一个绝对定位的蓝色元素(它们可能表示卡片的类别)。当用户向下滚动时,类别将滚动到固定头部之上。修复这个问题很简单:你只需要设置一个适当的 `z-index` 值。 + +## calc() 函数 + +CSS 的 `calc()` 函数允许我们计算某些 CSS 属性的值。编写 `calc()` 的一个常见错误是省略空格。 + +```css +.element { + width: calc(100%-30px); /* 无效 */ +} +``` + +这个值是无效的。你必须在减法符号周围添加空格。 + +```css +.element { + width: calc(100% - 30px); /* 有效 */ +} +``` + +## 文本对齐 + +### 忘记居中按钮的内容 + +假设你想要向 HTML `button` 或作为按钮功能的 `a` 链接添加一些 CSS 类。`button` 的内容默认是居中的,但 `a` 元素不是。所以,你应该手动居中后者。 + +![image-20250325223409012](/img/image-20250325223409012.png) + +```html + +Read more +``` + +```css +.button { + /* Other styles */ + text-align: center; +} +``` + +如果不这样做,你可能会惊讶地发现网站上的一些按钮是左对齐的! + +## 视口单位 + +### 使用 height: `100vh` 是有风险的 + +当你向类名为hero的元素添加 height: `100vh` 时,其中的元素在视口足够高时可能看起来很好。我曾经在 15 英寸笔记本电脑上浏览某人的网站。英雄部分占用了视口高度的 `100%`。它看起来很棒! + +我很好奇,打开了 DevTools 看看它是如何构建的,然后——砰!——英雄部分破坏了。其中的元素与下一个部分重叠。一旦我打开 DevTools,英雄部分中的元素就不适合可用高度了。为什么?这是因为当使用 `100vh` 时,缩小浏览器的高度会减少高度。 + +![image-20250325223431349](/img/image-20250325223431349.png) + +说到这里,当你测试视口高度时,DevTools 可能会很烦人。在调试视口高度时,我通常将 DevTools 解锁到单独的窗口。 + +## 伪元素 + +CSS 伪元素是 CSS 最有用的补充之一。误用它们可能会令人困惑,所以让我们探索一些常见问题。 + +### 忘记 `content` 属性 + +伪元素的核心是 `content` 属性。我们经常忘记它并设置以下内容: + +```css +.element::before { + display: block; + background: #ccc; +} +``` + +然后,我们想知道为什么元素没有出现。我花了一些时间修复这样的错误。为了避免这种情况,确保在创建伪元素时,`content` 属性是你添加的第一件事,然后再急于添加其他属性。 + +### 使用宽度或高度 + +伪元素的默认 `display` 值是 `inline`。所以,当你添加宽度、高度、垂直内边距或垂直边距时,除非显示类型更改为 `inline` 以外的值,否则它不会起作用。 + +```css +.element::before { + content: ''; + background: #ccc; + width: 100px; + height: 100px; /* 宽度和高度都不会起作用。 */ +} +``` + +### 在 Grid 或 Flexbox 中使用伪元素 + +当你向容器应用 `display: flex` 或 `display: grid` 时,其中的任何伪元素都将被视为普通元素。这可能会令人困惑,并可能导致意外问题。 + +我记得的一个常见问题是在 Bootstrap 3 中向 `.row` 元素应用 `display: flex`。因为列是用 float 属性构建的,`.row` 元素有 `::before` 和 `::after` 伪元素: + +```css +.row::before, +.row::after { + content: ''; + display: table; +} +``` + +这是"clearfix"技巧,它修复了浮动元素的布局,而无需添加表现性标记。 + +当 flexbox 应用于 `.row` 元素时,两个伪元素将被视为普通元素,这可能在布局中创建一些奇怪的空间。在这种情况下,伪元素不会有任何好处,所以它们应该被隐藏。 + +```css +.row::before, +.row::after { + display: none; +} +``` + +### 何时使用 `::before` 和何时使用 `::after` + +`::before` 伪元素成为其父元素的第一个子元素,而 `::after` 作为最后一个子元素添加。你可能想知道这是否有用? + +这是伪元素的一个常见用例,即在卡片组件顶部绝对定位覆盖层。在这种情况下,使用 `::before` 还是 `::after` 很重要,因为其中一个会更容易处理。你能猜到是哪个吗?考虑以下示例: + +![image-20250325223609237](/img/image-20250325223609237.png) + +```html +
+ +

Title here

+
+``` + +我们需要添加渐变覆盖层以使文本易于阅读。绝对定位元素(标题和 `::after` 元素)的堆叠顺序从下到上开始。最底部的元素 `h2` 将出现在图像顶部。如果我们使用 `::after` 作为渐变覆盖层,它将是最后一个元素,这将把它放在所有内容之上,所以我们需要使用 `z-index: -1` 将其移动到标题下方。 + +但是,如果我们使用 `::before`,那么渐变默认会出现在标题下方,无需对 `z-index` 进行任何调整。因此,我们节省了额外的工作并避免了错误。 + +```css +.card::before { + content: ''; + /* The CSS for the gradient overlay */ +} +``` + +## 颜色 + +`color` 属性在 CSS 中是一个重要属性,因为它设置文本元素的颜色。这可能听起来很简单,但事实并非如此。错误使用它可能会导致问题和额外的工作。 + +### `transparent` 关键字 + +`transparent` 关键字是 `rgba(0, 0, 0, 0)` 的快捷方式。一些浏览器将其计算为 alpha 值为 `0` 的黑色。这可能使透明渐变看起来有点黑色。 + +![image-20250325223653917](/img/image-20250325223653917.png) + +这种行为在 Chrome 和 Safari 等浏览器的旧版本中出现。为了防止这种情况,避免使用 `transparent` 关键字,特别是在 CSS 渐变中。为了解决这个问题,建议使用以下内容: + +```css +.element { + background: linear-gradient(to top, #fff, rgba(0, 0, 0, 0)); +} +``` + +### 不利用级联 + +默认情况下,`color` 属性被诸如 `p` 和 `span` 之类的子元素继承。与其在每个元素上设置 `color` 属性,不如将其添加到 `body` 元素,然后所有 `p` 和 `span` 元素都将继承该颜色,除非你覆盖它。 + +```css +body { + color: #222; /* 所有元素都将继承这个颜色。 */ +} +``` + +但是,`a` 元素默认不继承颜色。你可以覆盖其颜色或使用 `inherit` 关键字。 + +```css +a { + color: #222; /* … 或者… */ + color: inherit; +} +``` + +我认为开发者不利用级联是一个错误,因为它非常重要。为什么要添加比你需要的更多的 CSS? + +### 忘记哈希标记 + +颜色十六进制值前面的井号标记很重要。我经常从设计应用程序(如 Adobe Experience Design (XD) 或 Sketch)复制和粘贴颜色。从 Sketch 复制时,颜色被复制为 `275ED5`,而 Adobe XD 添加井号:`#275ED5`。如果你不是 100% 专注,这种差异可能导致意外结果。 + +```css +a { + color: 275ed5; /* 忘记哈希标记 */ + color: ##275ed5; /* 双哈希标记 */ +} +``` + +注意第二个规则中哈希标记错误地重复了。在项目工作时,你可能粘贴带有哈希标记的颜色值,然后,在另一个应用程序中编辑颜色后,你可能双击值(包括哈希标记)并盲目地将其粘贴回 CSS 中,导致双哈希标记。 + +![image-20250325223809698](/img/image-20250325223809698.png) + +当然,使用样式检查器可以避免这样的问题。但是,训练自己在复制和粘贴内容到代码编辑器时保持警惕很重要。 + +## CSS Backgrounds + +CSS 中的背景通常用于添加背景颜色、添加图像或用于装饰。让我们探索一些与它们相关的问题。 + +### 背景大小和位置的顺序 + +在 `background` 属性的简写中,写出背景大小和位置可能会令人困惑。它们有特定的顺序,用斜杠分隔。如果顺序错误,`background` 的整个定义将变为无效。 + +根据 Mozilla Developer Network (MDN): + +> `` 值只能紧跟在 `` 之后, +> 用 '/' 字符分隔,像这样:"center/80%"。 + +```css +background: url('image.png') center/50px 80px no-repeat; +``` + +注意 `center/50px 80px`。第一个是 `background-size`,第二个是 `position`。顺序不能颠倒。斜杠周围的空格是可以的。 + +### 不要使用简写仅设置颜色 + +使用 `background` 的简写来添加背景颜色可能很诱人,但不建议这样做,因为这会重置所有其他与背景相关的属性。 + +### 动态背景 + +如果背景是用 JavaScript 设置的,请使用 `background-size`、`position` 和 `repeat` 的专用属性。`background-image` 是唯一需要用 JavaScript 动态设置的属性。如果你用 JavaScript 设置整个背景,那将是很多不必要的工作。 + +### 忘记 `background-repeat` + +设置背景时,我们很容易忘记 `background-repeat`。例如,部分的背景在 15 英寸笔记本电脑上可能看起来很好,但在 27 英寸桌面上可能会重复。记住指定背景是否应该重复。 + +```css +.element { + background: url('image.png') cover/center no-repeat; +} +``` + +一般来说,我建议结合使用完整属性和简写属性。见下文: + +```css +.element { + background: url('image.png') center no-repeat; + background-size: cover; +} +``` + +### 打印 CSS 和背景 + +CSS 背景默认不包含在打印中。我们可以覆盖该行为并强制背景包含在打印中,使用以下 CSS 属性: + +```css +.element { + background: url('cheesecake.png') center/cover no-repeat; + -webkit-print-color-adjust: exact; /* 强制浏览器在打印模式下渲染背景 */ +} +``` + +## CSS 选择器 + +定位和样式化 HTML 元素是 Web 开发者的核心技能。如果我们不学会如何正确使用 CSS 选择器,我们会遇到错误。 + +### 忘记类的点标记 + +通过类名选择元素在没有点标记的情况下不会起作用。这通常发生在我们不专注的时候。 + +```css +button-primary { + /* 样式不会起作用。 */ +} +``` + +### 分组选择器 + +这是一个有趣的错误,你可能不会想到它。将有效和无效选择器分组在一起可能导致整个声明被忽略。 + +```css +a, +..button-primary { +} +``` + +根据 [CSS 规范](https://www.w3.org/TR/selectors/#grouping): + +> 如果这些选择器中只有一个无效,整个选择器列表就会无效。 + +`..button-primary` 类有两个点标记,这使其无效。将其与 `a` 元素分组会使浏览器忽略整个声明。 + +在选择 `::selection` 伪元素(用于定位选定文本)或 `::placeholder` 伪元素(用于定位输入占位符)时,很容易犯这个错误。我们在供应商前缀选择器中也看到这种情况,用于跨浏览器支持;当组中的一个供应商前缀选择器不正确时,整个样式声明将被忽略。 + +### 多次调用 CSS 选择器 + +CSS 特异性的一个常见错误是多次调用选择器。 + +```css +.title { + /* Some styles */ +} +/* 300 lines and.. */ +.title { + /* Another style */ +} +``` + +这本身不是错误,但很容易为错误开路。避免这种模式,并使用警告此类事情的样式检查器。 + +### 自定义输入的占位符 + +Firefox 使输入元素的占位符文本半透明。为占位符文本设置自定义颜色时,请记住它会显得有点暗淡。这对可访问性不好。确保通过重置半透明度来修复: + +```css +::-webkit-input-placeholder { + color: #222; + opacity: 1; +} +::-moz-placeholder { + color: #222; + opacity: 1; +} +:-ms-input-placeholder { + color: #222; + opacity: 1; +} +``` + +### 用户操作伪类的顺序 + +`:visited`、`:focus`、`:hover` 和 `:active` 伪类的顺序很重要。如果它们不按以下方式出现,那么它们不会按预期工作: + +```css +a:visited { + color: pink; +} +a:focus { + outline: solid 1px dotted; +} +a:hover { + background: grey; +} +a:active { + background: darkgrey; +} +``` + +### 定位具有多个类的元素 + +我在初学者中看到的一个常见错误是错误地同时定位两个类以选择元素。考虑以下内容: + +```html +
+
+``` + +```css +.alert.success { + background-color: green; +} +.alert.danger { + background-color: red; +} +``` + +第一个样式将与同时具有 `.alert` 和 `.success` 类的元素一起工作,而第二个样式只与同时具有 `.alert` 和 `.danger` 类的元素一起工作。但是,假设我们做了以下事情: + +```css +.alert .success { + background-color: green; +} +``` + +这里的错误是**在两个类之间添加空格**。这个空格改变了整个事情,它不会起作用。我们基本上是在选择 `.alert` 类元素内部具有 `.success` 类的元素。它假设如下的 HTML 结构: + +```html +
+
+
+``` + +使用正确的选择器,否则你可能会浪费很多时间想知道为什么它不起作用。 + +### 在特定元素上定位类 + +网站对所有用户的可访问性是网站设计的核心原则。忽视它可能导致糟糕的结果。我们看到的一个常见问题是使用 `div` 元素作为按钮,并仅通过 JavaScript 使其可点击。 + +防止与你合作的开发者向任何元素添加类并称其为按钮的一种方法是将类与其元素一起定位。 + +```css +button.btn { +} +``` + +这样,`.btn` 类除了在 button 元素上不会在任何元素上起作用。这是将类的使用限制为实际按钮元素的好方法。 + +### `!important` 的替代方案 + +有时,样式不起作用,因为它被 CSS 文件中的另一个样式覆盖。不建议使用 `!important`。这是一个更好的方法,仅使用 CSS 类。 + +```css +.btn.btn { +} +``` + +调用类两次会增加选择器的**特异性**,从而使规则在没有 `!important` 的情况下工作。确保类之间没有空格。注意你可以调用它三次、四次或任意多次。 + +## CSS 边框 + +### 悬停时的边框 + +显示悬停边框的一个常见错误是仅在悬停时添加边框。如果边框是 1 像素,那么当用户悬停在元素上时,元素会跳动。为了避免跳动,在正常状态下添加透明颜色的边框。 + +```css +.nav-item { + border: 2px solid rgba(0, 0, 0, 0); +} +.nav-item:hover { + border-color: #222; +} +``` + +这样,边框已经被添加并保留空间,悬停时边框的出现将基于 `border-color`。 + +我们经常在内联导航菜单中看到这种情况,其中项目应该在悬停时有边框。注意图中元素在导航项目悬停时如何稍微向右推。 + +### 多重边框 + +当你向元素添加多个 CSS 边框时——例如,左边和底边的边框——你可能会注意到两个边框相交的点有点奇怪。左边框的底端和底边框的左端看起来像被切断的三角形。 + +![image-20250325224211829](/img/image-20250325224211829.png) + +这是正常和预期的。CSS 边框就是这样工作的。如果你想要多重边框,那么你可以结合边框和阴影来解决问题。左边框可以保持原样,底边框可以是阴影。 + +### 边框和 `currentColor` 关键字 + +这不一定是错误,但值得一提。`currentColor` 关键字是 `border-color` 的默认值。 + +```css +.element { + color: #222; + border: 2px solid; +} +``` + +注意边框规则中没有声明颜色。默认值是 `currentColor`,它从 `color` 属性继承其值。我们的示例可以写成如下: + +```css +.element { + color: #222; + border: 2px solid currentColor; +} +``` + +重点是当边框规则与 `color` 的值相同时,向边框规则添加颜色是**不必要的**。 + +### 悬停时的边框过渡 + +有很多方法可以用 CSS 过渡边框。一种常见方法是修改 `border-width`。假设我们有两个按钮: + +![image-20250325224307413](/img/image-20250325224307413.png) + +我们想要扩展第一个按钮的边框,所以我们使用 `border-width`。悬停在按钮上会因为扩展的边框宽度而移动另一个按钮的位置。这种方法有两个主要问题: + +- 过渡很慢。也就是说,浏览器不会平滑地动画宽度。而不是像 `1, 1.1, 1.2 … 3` 那样增加,它会像 `1, 2, 3` 那样增加。这是阶梯动画。 +- 对性能也不好。`border-width` 的更改会触发浏览器中布局的重绘。兄弟按钮会因为新的边框宽度而移动。在动画的每一帧中,浏览器都会重绘它们的位置。 + +首选解决方案是使用 `box-shadow`。阴影更容易过渡,性能也足够好。 + +假设我们想要将元素边框的底部宽度从 3 像素动画到 6 像素。为此,我们可以使用 `box-shadow`,使用其 `y` 值作为 `border-width` 的替代。 + +```css +:root { + --shadow-y: 3px; +} +.element { + box-shadow: 0 var(--shadow-y) 0 0 #222; + transition: box-shadow 0.3s ease-out; +} +.element:hover { + --shadow-y: 6px; +} +``` + +进一步,我定义了一个 CSS 变量来保存阴影的 `y` 值,并在悬停时更改它。使用 CSS 变量,而不是再次复制整个 `box-shadow` 规则,减少了代码中的冗余。 + +### 基于屏幕尺寸更改边框宽度 + +适用于笔记本电脑或桌面屏幕的 `border-width` 对于移动设备可能太大。通常,我们会使用媒体查询在特定屏幕尺寸下更改 `border-width`。虽然这有效,但使用当前可用的 CSS 工具,我们有更好的替代方案。 + +![image-20250325224347114](/img/image-20250325224347114.png) + +通过使用 CSS 的比较函数,我们可以创建响应屏幕尺寸的阴影,而无需使用媒体查询。 + +```css +.element { + border: min(1vw, 10px) solid #468eef; +} +``` + +`border-width` 的最大值将是 10 像素,随着屏幕变窄它会变小。 + +### 向文本内容添加边框 + +当我开始学习 CSS 时,我认为可以向文本添加边框。事实并非如此。这可能会让任何 CSS 新手感到困惑。但是,使用 `text-stroke` 或 `text-shadow` 属性是可以做到的。让我们探索两种解决方案。 + +![image-20250325224406957](/img/image-20250325224406957.png) + +最常见的解决方案是将 `color` 设置为 `transparent`,然后添加边框。 + +```css +.element { + color: transparent; + -webkit-text-stroke: 1px #000; +} +``` + +虽然这有效,但在不支持的浏览器中(如 Internet Explorer 和旧版本的 Chrome、Firefox 和 Safari),文本将无法访问。 + +我们可以使用 CSS 的 `@supports` 查询来检测是否支持 `-webkit-text-stroke`,如果支持,则使用它。 + +```css +@supports (-webkit-text-stroke: 1px black) { + .element { + color: transparent; + -webkit-text-stroke: 1px #222; + } +} +``` + +另外,我们可以用其他东西替换 `color: transparent`。 + +```css +@supports (-webkit-text-stroke: 1px black) { + .element { + -webkit-text-fill-color: #fff; + -webkit-text-stroke: 1px #222; + } +} +``` + +### `border: none` vs. `border: 0` + +`border: none` 和 `border: 0` 都会将边框重置为其初始状态。它将 `border-width` 重置为 `0px`,`border-style` 重置为 `none`,`border-color` 将从 `color` 属性继承其值。 + +我更喜欢使用 `border: 0`。但是,如果我们看另一个属性,如 `box-shadow`,将其重置为 `box-shadow: 0` 是无效的。这令人困惑,因为你会期望 `0` 会重置这两个属性。但是 `none` 关键字对两者都有效。我建议使用它而不是 `0`。 + +### Focus 轮廓 + +这与 `border` 属性没有直接关系,但很容易混淆 `border` 和 `outline`。例如,在 StackOverflow 上快速搜索"css border"会返回几个标题包含"focus border, blue border"的问题。所以,我决定在这里涵盖它。 + +当元素获得焦点时出现的蓝色边框或轮廓不是错误,而是帮助键盘用户知道他们在哪里、采取行动等的功能。其实现在浏览器中不一致。 + +![image-20250325224538472](/img/image-20250325224538472.png) + +我们可以用自定义轮廓覆盖该轮廓,而不是删除它。 + +```css +.nav-item a:focus { + outline: dotted 2px blue; +} +``` + +可能性是无穷的。但请在任何情况下都不要删除该轮廓,因为它会影响网站的可访问性。 + +## 盒子阴影 + +### 元素一侧的阴影 + +当你在 CSS 中添加阴影时,默认情况下它会从元素的四个边扩散。我在研究中看到一个常见请求,即如何在一个方向添加阴影。 + +![image-20250325224628148](/img/image-20250325224628148.png) + +使用 `box-shadow`,扩散值控制阴影覆盖区域的大小。通过使用负值,我们可以向元素的一个方向添加阴影。 + +```css +.element { + /* -5px 的值是阴影的扩散。 */ + box-shadow: 0 7px 7px -5px #ccc; +} +``` + +### `box-shadow` 和 `overflow: hidden` 不能很好地混合 + +如果你需要为元素使用 `overflow`,并且其一些子元素有 `box-shadow` 属性,阴影将在左右两侧被切断。 + +![image-20250325224651905](/img/image-20250325224651905.png) + +在这个例子中,缩略图的阴影在左右两侧被切断。原因是 `overflow: hidden` 被应用于父元素(卡片)。当你希望子元素上的阴影可见时,避免使用 `overflow: hidden`。 + +### 多重盒子阴影 + +有时我们需要向元素添加多个阴影。这是支持的,可以在没有额外 HTML 元素或伪元素的情况下完成。每个阴影都用逗号分隔。 + +```css +.element { + box-shadow: + 0 5px 10px 0 #ccc, + 0 10px 20px #222; +} +``` + +### 盒子阴影和内联图像的空白问题 + +你还记得我们谈论图像的 `display` 以及图像下方如何有一点空白吗?原因是它是一个内联元素。当我们对内联图像的父元素使用 `box-shadow` 时也会发生这种情况。 + +```html +
+ +
+``` + +```css +.img-wrapper { + box-shadow: 0 5px 10px 0 #ccc; +} +``` + +![image-20250325224718890](/img/image-20250325224718890.png) + +在这个例子中,图像下方有空白,只有在添加阴影后才变得可见。确保重置图像的 `display` 值以避免这个问题。 + +### 头部元素的盒子阴影 + +当网站的头部直接跟随,比如说,英雄图像时,添加阴影可能会很棘手。如果你尝试向头部添加阴影,它会被英雄部分覆盖。 + +![image-20250325224740951](/img/image-20250325224740951.png) + +解决这个问题可以通过更改头部元素的堆叠上下文来完成。最简单的解决方案是添加以下内容: + +```css +.site-header { + position: relative; +} +``` + +确保这个修复没有意外的副作用。 + +### 语音气泡箭头的阴影 + +工具提示和下拉菜单的常见设计模式是添加指向工具提示或下拉菜单父元素的箭头。在 CSS 中制作箭头有很多方法,最常见的是创建一个在一侧有边框的伪元素。 + +![image-20250325224801169](/img/image-20250325224801169.png) + +我们如何向箭头添加 `box-shadow`?我们可以通过对 x 和 y 值使用负值来模拟来自一个方向的阴影。 + +```css +.element::before { + content: ''; + width: 20px; + height: 20px; + background-color: #fff; + position: absolute; + left: 50%; + top: -10px; + transform: translateX(-50%) rotate(45deg); + box-shadow: -1px -1px 1px 0 lightgray; +} +``` + +### 图像元素的 `inset` 阴影 + +假设我们有一个图像,我们想要向其添加半透明的内边框,作为后备,以防图像加载失败。 + +![image-20250325224822446](/img/image-20250325224822446.png) + +边框也有助于防止明亮的图像与浅色背景融合。你可能想到的第一个解决方案是使用内嵌 `box-shadow`: + +```css +img { + box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.2); +} +``` + +不幸的是,内嵌盒子阴影对图像不起作用。我们需要一个解决方法。我在分析 [facebook.com 的新设计](https://ishadeed.com/article/new-facebook-css/) 时学到了一些解决方案,我们将在下面探索。 + +#### 使用额外的 HTML 元素作为边框 + +使用额外的元素,我们会保持其背景透明并仅添加边框。以下显示了 HTML 的结构和 CSS: + +```html +
+ +
+
+``` + +```css +.avatar-wrapper { + position: relative; +} +.avatar { + display: block; + border-radius: 50%; +} +.avatar-outline { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + border-radius: 50%; +} +``` + +#### 使用 SVG `image` + +另一个有趣的解决方案是使用 `svg` 元素,而不是 `img`。这个解决方案在浏览器中得到很好的支持,并且更容易控制。这是 HTML: + +```html + + + + + + + + + +``` + +让我们回顾一下 SVG 代码: + +1. 我们有一个作为圆形的 `mask` 元素。 +2. 接下来是一个 `group`,包含 `image` 本身和一个 `circle` 元素。`circle` 元素充当边框,它将位于 `image` 之上。 + +```css +.border { + stroke-width: 2; + stroke: rgba(0, 0, 0, 0.1); + fill: none; +} +``` + +Facebook 在其 2020 年重新设计中使用了这两种解决方案。SVG 解决方案很少用于侧边栏中的个人资料图片和用户头像等内容。带有额外 HTML 元素的解决方案在社交动态、评论等中大量使用。 + +## CSS 变换 + +### 应用多个变换 + +在 CSS 变换中,我们可以在元素上使用一个或多个变换。例如,可以将元素向右移动 10 像素然后旋转它。 + +```css +.element { + transform: translateX(10px) rotate(20deg); +} +``` + +偶尔,你可能需要在元素上使用多个变换。 + +但是,有时你需要在移动设备上使用一个变换,在桌面上使用两个。在这里,我们遇到了一个常见问题。 + +假设我们有一个模态,应该在移动设备上水平居中。在较大的视口中,它应该水平和垂直居中。一个常见错误是意外重置变换。 + +```css +.modal-body { + transform: translateX(-50%); +} +@media (min-width: 800px) { + .modal-body { + transform: translate(0, -50%); + } +} +``` + +变换通过 `translate(0, -50%)` 得到意外重置。这很容易造成混淆。解决方案很简单:我们需要保持 `translateX(-50%)`。 + +```css +@media (min-width: 800px) { + .modal-body { + transform: translate(-50%, -50%); + } +} +``` + +### CSS 变换的顺序很重要 + +根据 MDN: + +> 变换函数从左到右相乘,这意味着复合变换实际上从右到左按顺序应用。 + +变换函数的顺序很重要。注意它们以避免问题。 + +![image-20250325225012217](/img/image-20250325225012217.png) + +注意变换函数的顺序如何影响每个矩形的视觉位置。在第一个中,元素首先被缩放,然后向右变换 20 像素。第二个矩形发生相反的情况。调试 CSS 变换时,确保顺序满足你的需求。不要随机添加变换函数。 + +### 错误地覆盖变换 + +当我开始学习 CSS 时,我并不完全意识到 `transform` CSS 属性可以包含多个变换,并且我们需要在每次声明属性时指定我们想要的所有变换。以下是一个错误: + +```css +.element { + transform: translateY(100px); +} +.element:hover { + transform: rotate(45deg); +} +``` + +你可能期望 `translateY` 和 `rotate` 函数都会工作,但事实并非如此。第二个变换会覆盖第一个;因此,我们会失去 `translateY`。相反,我们会组合它们: + +```css +.element:hover { + transform: translateY(100px) rotate(45deg); +} +``` + +并记住顺序的重要性,如前所述。 + +### 单独的变换属性 + +在撰写本文时,只有 Firefox 72 支持单独声明的变换。这将很好地解决上面提到的问题,因为我们不必将所有变换组合在一起。这是前面示例的重新工作版本: + +```css +.element { + translate: 0 100px; +} +.element:hover { + rotate: 45deg; +} +``` + +这不是更简单吗?你可以使用 `@supports` 媒体查询检测是否支持这个。 + +```css +@supports (translate: 10px 10px) { + /* Add the individual transform properties */ +} +``` + +### 变换 SVG 元素 + +HTML 元素的坐标系从 `50% 50%` 开始。在 SVG 中,它完全不同;它从 `0 0` 开始。由于这种差异,在 SVG 元素上使用 CSS 变换可能会令人困惑。 + +要按预期变换 SVG 元素,请使用像素值并避免百分比。并记住 CSS 变换在 Internet Explorer 中不受支持,但从 Microsoft Edge 17 开始受支持。 + +取自 [Ana Tudor on CSS-Tricks](https://css-tricks.com/transforms-on-svg-elements/),以下示例说明了这个问题: + +```css +rect { + /* This doesn't work in Internet Explorer and old versions of Edge. */ + transform: translate(295px, 115px); +} +``` + +```html + + +``` + +我们可以为 SVG 子元素使用内联 `transform` 属性。它与 CSS 变换有点不同,值之间没有逗号。 + +### 使用变换将文本旋转 90 度 + +我不会认为这是一个错误,而是寻找更好方法来解决这个需求的问题。假设我们想要旋转一个文本元素。 + +![image-20250325225108895](/img/image-20250325225108895.png) + +你可能考虑的第一种方法是定位文本并旋转它。虽然这会起作用,但有更好的解决方案。通过使用 CSS 的 `writing-mode`,我们可以轻松地将书写方向从从左到右更改为从上到下。`writing-mode` 属性设置文本元素的方向(水平或垂直)。它是为日语和中文等语言设计的。 + +```css +/* Without writing-mode */ +.title { + position: absolute; + left: 40px; + transform-origin: left top; + transform: rotate(90deg); +} +/* With writing-mode */ +.title { + writing-mode: vertical-lr; +} +``` + +使用 `writing-mode`,我们可以用一行 CSS 旋转标题。[浏览器支持](https://caniuse.com/css-writing-mode) 也很好。 + +## CSS 自定义属性(变量) + +### 作用域变量 vs. 全局变量 + +作用域变量是只能在元素内部使用的变量,而全局变量,顾名思义,可以全局使用。 + +```html +
+
+
+``` + +```css +.header { + --brand-color: #222; +} +``` + +我们定义了一个变量 `--brand-color`,它只能与 `.header` 元素及其子项目一起工作。具有 `.item` 类的元素可以看到该变量。 + +在研究这个主题时,我注意到 StackOverflow 上有一个被标记为正确的问题,但实际上并不正确。答案声称以下应该工作: + +```css +.header { + --brand-color: #222; +} +body { + background-color: var(--brand-color); +} +``` + +这永远不会工作。`body` 元素看不到 CSS 变量,因为它的作用域是 `.header` 元素。为了使其工作,CSS 变量必须**全局**定义: + +```css +:root { + --brand-color: #222; +} +body { + background-color: var(--brand-color); +} +``` + +这完美地工作。 + +### 为变量设置兜底值 + +有时在谈论变量的兜底值时,可能会混淆我们是指不支持 CSS 变量的旧浏览器的兜底值,还是 CSS 变量本身的兜底值。 + +```css +.title { + color: #222; + color: var(--brand-color); +} +``` + +上面的第一个规则是旧浏览器的兜底值,可以使用 PostCSS 等工具自动化。 + +但是,我们这里的重点是 CSS 变量本身的兜底值: + +```css +.title { + color: var(--brand-color, #222); +} +``` + +如果由于某种原因,变量 `--brand-color` 不可用,那么逗号后的值将被使用。注意你可以使用多个兜底值。见下文: + +```css +.title { + color: var(--brand-color, var(--secondary-color, #222)); +} +``` + +### 检索文档中定义的所有 CSS 变量 + +有时,你可能想要查看应用程序或网站中的所有全局 CSS 变量。幸运的是,我们可以从浏览器的 DevTools 中获取它们。 + +选择 `html` 元素,在右侧,你应该看到其中定义的所有 CSS 变量。 + +![image-20250325225200821](/img/image-20250325225200821.png) + +该图突出显示了当我们检查页面的根元素(`html` 元素)时 CSS 变量的外观。我喜欢 Firefox 的是你可以切换变量!这对于调试或测试 CSS 变量的后备值非常有用。 + +### 计算值时的无效化 + +如果声明使用有效的自定义属性,但在替换 `var()` 函数后,属性值无效,则声明在计算值时将无效。当这种情况发生时,属性计算为其初始值。考虑以下示例,取自 [Lea Verou 的](https://lea.verou.me/blog/2020/06/hybrid-positioning-with-css-variables-and-max/) 博客: + +```css +#toc { + position: fixed; + top: 11em; + top: max(0em, 11rem - var(--scrolltop) * 1px); +} +``` + +如果浏览器不支持 `max()` 比较函数,它将使属性在计算值时无效,这将计算为 `initial`;对于 `top` 属性,初始值将是 0。这将破坏设计。解决方法是使用 `@supports` 函数检测对 `max()` 函数的支持。如果支持,则将使用声明。 + +```css +#toc { + position: fixed; + top: 11em; +} +@supports (top: max(1em, 1px)) { + #toc { + top: max(0em, 11rem - var(--scrolltop) * 1px); + } +} +``` + +## 水平滚动 + +这是前端开发中最常见的问题之一。水平滚动表明元素位于视口边界之外或元素比视口宽。原因各不相同。我将尝试在这里总结它们,以及你可以用来查找和修复问题的策略。 + +### Firefox 显示 `scroll` 标签 + +值得强调的一个小帮助是 Firefox 为比视口宽的元素显示"scroll"标签。该标签将指导你调试导致水平滚动的元素。 + +![image-20250325225257176](/img/image-20250325225257176.png) + +当你点击 `scroll` 标签时,Firefox 将突出显示导致水平滚动的元素。 + +![image-20250325225311812](/img/image-20250325225311812.png) + +`h2` 和 `p` 元素导致水平滚动,因为它们比视口宽。因此,当点击"scroll"标签时,Firefox 会突出显示它们。 + +### 查找水平滚动错误 + +让我们首先关注如何查找水平滚动问题。首先要做的是确保默认显示滚动条。例如,macOS 在你开始滚动(垂直或水平)之前不会显示滚动条。使滚动条可见可以帮助我们更快地发现滚动问题。 + +转到"System Preferences" > "General" > "Show scroll bars" > "Always"。 + +Windows 默认显示滚动条,所以那里不需要做任何事情。 + +#### 向左或向右滚动 + +在你想要测试的页面上,尝试用鼠标或触控板向左或向右滚动。继续缩小屏幕,并重复该过程。如果没有滚动,则在 DevTools 中激活移动模式。移动模式下的水平滚动问题可能如下所示: + +![image-20250325225341032](/img/image-20250325225341032.png) + +这意味着有一个元素比 `body` 或 `html` 元素宽。 + +#### 使用 JavaScript 获取比 Body 宽的元素 + +我们可以进一步使用脚本来检测元素是否比 `body` 或 `html` 元素宽。这在大型项目或你不熟悉的项目中很有用。 + +```js +[].forEach.call(document.querySelectorAll('body *'), function (el) { + if (el.offsetWidth > document.body.offsetWidth) { + console.log(el.className); + } +}); +``` + +在这里,我们选择了 `body` 内的所有元素,检查元素是否比它宽,并将其打印出来。 + +#### 使用 `outline` + +通过使用 CSS 的 `outline` 属性,我们可以在布局中的每个元素周围添加轮廓。这对于揭示任何问题都有很大帮助。例如,它可以揭示任何元素是否占用了超过允许的空间。 + +```css +*, +*:before, +*:after { + outline: solid 1px; +} +``` + +这完美地工作,但我们可以用 Addy Osmani 创建的[脚本](https://gist.github.com/addyosmani/fd3999ea7fce242756b1)将其提升到下一个级别: + +```js +[].forEach.call($$('*'), function (a) { + a.style.outline = '1px solid #' + (~~(Math.random() * (1 << 24))).toString(16); +}); +``` + +这个脚本将为页面上的每个元素添加轮廓,每个元素都有不同的颜色。(在复杂布局中,所有轮廓都使用相同颜色会有点混乱。) + +![image-20250325225415836](/img/image-20250325225415836.png) + +注意使用 `outline` 比 `border` 好得多,原因如下: + +- 如果元素有边框,`outline` 将在边框之后添加。换句话说,`outline` 不会占用空间,因为它绘制在元素内容之外。 +- 如果 `box-sizing` 未设置为 `border-box` 或元素已经有边框,使用 `border` 可能会破坏一些设计组件。这会令人困惑。 +- `outline` 不会受到元素的 `border-radius` 影响。添加到页面的所有轮廓都将是矩形的。 + +### 修复水平滚动 + +现在我们已经识别了水平滚动问题,是时候学习如何调试它们了。当你发现水平滚动时,你可能一眼看不出问题的原因,所以你需要实验。 + +打开浏览器的 DevTools 并开始逐个删除主要 HTML 元素,看看滚动是否消失(提示:你可以在 macOS 上使用 `CMD + z` 或在 Windows 上使用 `CTRL + z` 来取消元素的删除)。一旦你看到滚动消失了,记下你刚刚删除的元素。刷新页面,并深入该元素以查看那里有什么。水平滚动可能有几个原因。让我们探索它们。 + +#### 固定宽度 + +固定宽度肯定会导致水平滚动。例如,当视口窄于 1000 像素时,以下内容会导致错误。 + +```css +.section { + width: 1000px; +} +``` + +要修复这个问题,我们需要使用 `max-width` 为元素设置最大宽度: + +```css +.section { + width: 1000px; + max-width: 100%; /* 当视口小时,防止元素变得比 1000 像素宽。 */ +} +``` + +#### 具有负值的定位元素 + +![image-20250325225452415](/img/image-20250325225452415.png) + +将位置属性(`top`、`right`、`bottom`、`left`)之一设置为负值的元素将导致水平滚动。 + +```css +.element { + position: absolute; + right: -100px; +} +``` + +当你使用 CSS 变换将元素移出视口时,也会发生同样的事情。 + +```css +.element { + position: absolute; + right: 0; + transform: translateX(1500px); +} +``` + +如果有必要将元素放置在其父元素之外,那么最好使用以下方法: + +- 应用 CSS `transform` +- 在父元素上使用 CSS `overflow: hidden`,以防你没有其他选择 + +#### 没有换行的 Flexbox 容器 + +使用 `flexbox` 时,行默认不会元素换行。当视口变小时,会发生水平滚动,因为没有足够的空间在一行上显示所有元素。这是 `flexbox` 的常见问题。要解决它,你需要在某些屏幕尺寸上强制元素换行。 + +```css +.section { + display: flex; + flex-wrap: wrap; /* 在没有足够空间的情况下强制 flex item到新行。 */ +} +``` + +![image-20250325225531807](/img/image-20250325225531807.png) + +使用 CSS grid 时,有水平滚动的可能性。假设我们有一个具有动态列且最小宽度为 200 像素的网格。 + +```css +.wrapper { + display: grid; + grid-template-columns: 200px 1fr; + grid-gap: 16px; +} +``` + +一切看起来都很好,直到视口变窄。空间不够,结果发生水平滚动。 + +![image-20250325225556349](/img/image-20250325225556349.png) + +要修复这个问题,我们可以使用媒体查询仅在有足够空间时应用网格。 + +```css +@media (min-width: 400px) { + .wrapper { + /* The grid goes here. */ + } +} +``` + +#### 长单词或内联链接 + +如果文章有很长的单词或链接,如果处理不当,很容易导致水平溢出。 + +![image-20250325225620383](/img/image-20250325225620383.png) + +如你所见,长单词导致水平滚动。解决方案是使用 `overflow-wrap` CSS 属性。它防止长单词溢出其行框。 + +```css +.content p { + overflow-wrap: break-word; +} +``` + +值得一提的是,该属性已从 `word-wrap` 重命名为 `overflow-wrap`。 + +#### 没有 `max-width: 100%` 的图像 + +如果由于任何原因你没有使用 CSS 重置文件,那么你需要确保网站上的任何图像都不超过其父元素的宽度。要做到这一点,你只需要以下内容: + +```css +img { + max-width: 100%; +} +``` + +你猜对了——忘记包含该行会导致水平滚动。 + +## 过渡 + +CSS 过渡使我们能够平滑地将元素从一种状态动画到另一种状态。让我们探索一些常见问题。 + +### 调整大小时的过渡 + +过渡的一个烦人问题是在调整浏览器窗口大小时看到元素移动和动画。这是因为你将过渡应用于所有属性,这破坏了行为,甚至可能导致性能问题。 + +```css +.element { + transition: all 0.2s ease-out; +} +``` + +`all` 关键字告诉我们过渡将应用于元素的所有属性。这对于一个元素可能是可以的,但在规模上使用这种模式是不推荐的。当我开始学习 CSS 时,我习惯于犯以下错误: + +```css +* { + transition: all 0.2s ease-out; +} +``` + +这段 CSS 为页面上的每个元素添加过渡。请不要这样做!这不是一个好主意。 + +### 过渡高度 + +一个常见需求是过渡元素的高度——例如,从 `0` 到 `auto`。`auto` 值会使元素的高度等于其中的内容。 + +```css +.element { + height: 0; + transition: height 0.2s ease-out; +} + +.element:hover { + height: auto; +} +``` + +不幸的是,这在 CSS 中是不可能的。你不能过渡到 `auto`。但是,有一个解决方法。我们可以使用大于内容高度的 `max-height` 值,而不是使用 `height`。例如,如果我们有一个高度为 200 像素的移动菜单,那么 `max-height` 的值应该至少为 300 像素。更大值的原因是确保元素的高度永远不会达到那个点。 + +```css +.element { + max-height: 0; + overflow: hidden; + transition: max-height 0.2s ease-out; +} + +.element:hover { + max-height: 300px; +} +``` + +我添加了 `overflow: hidden` 来裁剪当元素设置 `max-height: 0` 时可能可见的任何内容。 + +### 过渡可见性和显示 + +过渡元素的 `display` 属性是不可能的。但是,我们可以结合 `visibility` 和 `opacity` 属性来模拟以可访问的方式隐藏元素。 + +![image-20250325225732667](/img/image-20250325225732667.png) + +这里我们有一个应该在鼠标悬停和键盘焦点时显示的菜单。如果我们只使用 `opacity` 来隐藏它,那么菜单仍然在那里,其链接可点击(尽管不可见)。这种行为不可避免地会导致混乱。更好的解决方案是使用如下内容: + +```css +.menu { + opacity: 0; + visibility: hidden; + transition: + opacity 0.3s ease-out, + visibility 0.3s ease-out; +} + +.menu-wrapper:hover .menu { + opacity: 1; + visibility: visible; +} +``` + +CSS 的 `visibility` 属性是可动画的。当添加到 `transition` 组中时,它将被动画,菜单将很好地淡入和淡出,而不会突然消失。 + +## 溢出 + +`overflow` 属性的值默认为 `visible`。其他值是 `hidden`、`scroll` 和 `auto`。 + +### overflow-y: auto vs. overflow-y: scroll + +当我们有一个具有固定高度和可滚动内容的组件时,使用 `overflow-y: scroll` 很诱人。缺点是当内容太短时,在 Windows 操作系统上会显示滚动条。对于 macOS,滚动条默认是隐藏的。 + +```css +.section { + overflow-y: scroll; +} +``` + +![image-20250325225816568](/img/image-20250325225816568.png) + +要修复这个问题并仅在内容变长时显示滚动条,请使用 `auto`。 + +```css +.section { + overflow-y: auto; +} +``` + +![image-20250325225835849](/img/image-20250325225835849.png) + +### 移动设备上的滚动 + +当我们有,比如说,一个滑块时,仅添加 `overflow-x` 并称其为一天是不够的。在 iOS 上的 Chrome 中,我们需要继续滚动并手动移动内容。幸运的是,有一个属性可以增强滚动体验。 + +```css +.wrapper { + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} +``` + +这被称为**基于动量的滚动**。[MDN 这样描述它](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling): + +> 内容在完成滚动手势并从触摸屏上移开手指后继续滚动一段时间。 + +作为建议,确保避免在大的滚动上下文(例如全页布局)中使用 `-webkit-overflow-scrolling: touch`,因为这可能在 Safari iOS 上导致一些随机错误。 + +### 具有 `overflow: hidden` 的内联块元素 + +根据 CSS 规范: + +> "inline-block" 的基线是其正常流中最后一个行框的基线,除非它没有流内行框或其 "overflow" 属性的计算值不是 "visible",在这种情况下基线是底部边距边缘。 + +当 `inline-block` 元素的 `overflow` 值不是 `visible` 时,这将导致元素的底边根据其兄弟元素的文本基线对齐。 + +![image-20250325225902504](/img/image-20250325225902504.png) + +要解决这个问题,更改具有 `overflow: hidden` 的按钮的对齐方式。 + +```css +.button { + vertical-align: top; +} +``` + +## 文本溢出 + +`text-overflow` 属性设置如何显示溢出的文本。最常见的值是 `ellipsis`:文本将被裁剪,在其末尾将有三个点,`像这样…`。 + +![image-20250325225922342](/img/image-20250325225922342.png) + +该属性有时使用起来令人困惑。一个常见障碍是 `text-overflow: ellipsis` 的声明不会按你期望的方式工作。 + +```css +span { + text-overflow: ellipsis; +} +``` + +要使 `text-overflow` 工作,需要以下条件: + +- 元素的 `display` 类型应设置为 `block`, +- 元素必须设置 `overflow` 和 `white-space` 属性。 + +```css +span { + display: block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} +``` + +设置了这些,它将按预期工作。出于好奇,我测试了其他显示类型,包括 `inline-block` 和 `flex`,它们都不能按预期工作。 + +## `!important` 规则 + +没有充分理由使用 `!important` 规则可能导致错误并浪费你的时间。为什么?因为它破坏了 CSS 的自然级联。你可能尝试样式化一个元素并发现样式不起作用。原因可能是另一个元素正在覆盖该样式。 + +```css +.element { + color: #222 !important; +} +.element { + color: #ccc; +} +``` + +元素的颜色是 `#222`,即使第二次声明了不同的颜色。在大型项目中,随机使用 `!important` 可能导致很多混乱。 + +一般避免 `!important`。在使用它之前考虑以下事项: + +- 尝试使用 DevTools 识别特异性问题的来源。 +- 当你使用第三方 CSS 文件时,有时是有道理的。你可能没有其他选择,只能覆盖外部样式。 + +CSS 原子类最近变得更受欢迎。我认为这些是 `!important` 的良好使用场景。 + +```html +
+``` + +```css +.d-block { + display: block !important; +} +``` + +`d-block` 类将元素设置为显示为 `block` 类型。添加 `!important` 确保它将按预期应用。 + +## Flexbox + +flexbox 布局模块为我们提供了一种水平或垂直布局一组项目的方法。flexbox 有很多常见问题:一些是开发者错误地做的,其他是浏览器实现中的错误。 + +### 用户制造的错误 + +#### 忘记 `flex-wrap` + +当将元素设置为 flexbox 项目的包装器时,很容易忘记 item 元素应该如何换行。一旦你缩小视口,你会注意到水平滚动。原因是 flexbox 默认不换行。 + +```html +
+
+
+
+
+``` + +```css +.section { + display: flex; +} +``` + +![image-20250325230112066](/img/image-20250325230112066.png) + +注意 item 元素不换行,从而导致水平滚动。始终确保添加 `flex-wrap: wrap`。 + +```css +.section { + display: flex; + flex-wrap: wrap; +} +``` + +#### 使用 `justify-content: space-between` 进行间距 + +当我们使用 flexbox 制作,比如说,卡片网格时,使用 `justify-content: space-between` 可能很棘手。 + +![image-20250325230135989](/img/image-20250325230135989.png) + +上面的卡片网格被给予 `justify-content: space-between`,但注意最后一行看起来很奇怪?好吧,设计师假设卡片数量总是四的倍数(4、8、12 等)。 + +CSS grid 推荐用于此场景。但是,如果你没有其他选择,只能使用 flexbox 创建网格,这里有一些你可以使用的解决方案。 + +##### 使用内边距和负边距 + +```html +
+
+
+
+ +
+``` + +```css +.grid { + display: flex; + flex-wrap: wrap; + margin-left: -1rem; +} +.grid-item { + padding: 1rem 0 0 1rem; + flex: 0 0 25%; + margin-bottom: 1rem; +} +``` + +每个 `grid-item` 在左侧有内边距,但每行的第一个网格项目不需要它。为了避免使用复杂的 CSS 选择器,我们可以通过在左侧使用负边距将包装器推到左边。 + +![image-20250325230210839](/img/image-20250325230210839.png) + +##### 添加空的间隔元素 + +在研究 Facebook 的新 CSS 时,我注意到间隔元素对我们现在正在解决的问题的有趣用例。 + +![image-20250325230228893](/img/image-20250325230228893.png) + +如果我们有六个项目的网格,最后两个将作为空间隔元素添加。这确保 `space-between` 按预期工作。 + +```html + +
+
+
+
+
+ + +
+
+
+
+
+
+``` + +再次,空元素的目的是保持间距按预期工作。当然,这应该动态完成。 + +#### 在某些视口中隐藏 Flexbox 元素 + +在移动设备上隐藏 flexbox 元素并在桌面上显示它可能很棘手。 + +```css +.element { + display: none; +} +@media (min-width: 768px) { + .element { + display: block; + } +} +``` + +你可能不假思索地输入 `display: block`,因为这是显示隐藏元素的常见方法。但是,因为元素是 flex 包装器,`block` 的显示值可能会破坏布局。这个错误可能导致一些调试时间。 + +```css +@media (min-width: 768px) { + .element { + display: flex; + } +} +``` + +#### 拉伸的图像 + +默认情况下,如果方向设置为 `row`,flexbox 会拉伸其子项目以使它们在高度上相等,如果方向设置为 `column`,它会使它们在宽度上相等。这可能使图像看起来拉伸。 + +```html +
+ +

Recipe title

+
+``` + +```css +.recipe { + display: flex; +} +img { + width: 50%; +} +``` + +简单的在线搜索显示这个问题很常见,并且有不一致的浏览器行为。默认情况下仍然拉伸图像的唯一浏览器是 Safari 版本 13。要修复它,我们需要重置图像本身的对齐方式。 + +```css +.recipe img { + align-self: flex-start; +} +``` + +![image-20250325230308168](/img/image-20250325230308168.png) + +虽然 Safari 版本 13 是唯一具有拉伸图像不一致行为的浏览器,但 `button` 元素在所有浏览器中都被拉伸。修复是相同的(`align-self: flex-start`),但像这样的小细节让你思考浏览器的奇怪之处。 + +当 flex 包装器的方向设置为 `column` 时,我们看到相关问题。 + +```html +
+

+

+ +
+``` + +```css +.card { + display: flex; + flex-direction: column; +} +``` + +`.card__category` 元素将拉伸以占用其父元素的全宽。如果这种行为不是预期的,那么你需要使用 `align-self` 强制 `span` 元素与其内容一样宽。 + +![image-20250325230338410](/img/image-20250325230338410.png) + +```css +.card__category { + align-self: flex-start; +} +``` + +#### Flexbox Items 宽度不相等 + +一个常见的困难是使 flexbox item 元素宽度相等。 + +![image-20250325230356445](/img/image-20250325230356445.png) + +根据规范: + +> 如果指定的 `flex-basis` 是 `auto`,使用的 `flex-basis` 是 flex 项目主尺寸属性的值。(这本身可以是关键字 auto,它根据其内容调整 flex 项目的大小。) + +每个 flex item 元素都有一个 `flex-basis` 属性,它充当该元素的大小属性。当值是 `flex-basis: auto` 时,基础是内容的大小。所以,有更多文本的子项目将——你猜对了——更大。这可以通过以下方式解决: + +```css +.item { + flex-grow: 1; + flex-basis: 0%; +} +``` + +这样,每个 item 元素将占用与其兄弟项目相同的空间。 + +#### 使用 Flexbox 将最小宽度设置为零 + +`min-width` 的默认值是 `auto`,它计算为 `0`。flex item 元素的 `min-width` 等于其内容的大小。 + +根据 [CSS 规范](https://www.w3.org/TR/css-flexbox-1/): + +> 默认情况下,flex item 元素不会收缩到其最小内容大小以下(最长单词或固定大小元素的长度)。要更改此设置,请设置 min-width 或 min-height 属性。 + +考虑以下示例: + +![image-20250325230420478](/img/image-20250325230420478.png) + +人名很长,导致水平滚动。所以,我们添加以下内容来截断它: + +```css +.c-person__name { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +``` + +技巧是向元素添加 `min-width: 0`。 + +```css +.c-person__name { + /* Other styles */ + min-width: 0; +} +``` + +这是修复后的外观: + +![image-20250325230441017](/img/image-20250325230441017.png) + +#### Flex 格式化上下文 + +值得一提的是,当我们为元素分配 `display: flex` 时,flex 容器建立新的 flex 格式化上下文。添加 `float` 不会起作用。此外,flex 容器的 item 元素没有边距折叠。 + +### 浏览器实现错误 + +让我们走过一些与 flexbox 相关的最常见问题,这些问题与不正确或不一致的浏览器实现有关。 + +在本节中,我将大量依赖 Philip Walton 的 [flexbugs](https://github.com/philipwalton/flexbugs),这是所有与 flexbox 相关的浏览器错误的绝佳资源。 + +#### `flex-basis` 不支持 `calc()` + +![image-20250325230509728](/img/image-20250325230509728.png) + +当使用 flex 属性的简写版本时,Internet Explorer 从 10 到 11 版本都忽略任何 `calc()` 函数。 + +```css +.element { + flex: 0 0 calc(100% / 3); +} +``` + +解决方案是写出完整版本。 + +```css +.element { + flex-grow: 0; + flex-shrink: 0; + flex-basis: calc(100% / 3); +} +``` + +在 Internet Explorer 10 中,`calc()` 函数在完整的 `flex-basis` 声明中不起作用。为了解决这个问题,我们做以下事情: + +```css +.element { + flex: 0 0 auto; + width: calc(100% / 3); +} +``` + +#### 一些 HTML 元素不能作为 Flex 容器 + +![image-20250325230532690](/img/image-20250325230532690.png) + +诸如 `button`、`fieldset` 和 `summary` 之类的元素不能作为 flex 容器工作。flexbugs 存储库给出以下原因: + +> 浏览器对这些元素 UI 的默认渲染与 `display: flex` 声明冲突。 + +考虑以下示例: + +```html +
+ Enter your information +

+ + +

+

+ + +

+
+``` + +```css +fieldset { + display: flex; + flex-wrap: wrap; +} +``` + +你会假设输入将彼此相邻显示,对吧?这个错误不是这种情况。它不会起作用。解决方法是将输入包装在另一个可以充当 flex 容器的元素中。 + +```html +
+
+ +
+
+``` + +```css +.inputs-group { + display: flex; + flex-wrap: wrap; +} +``` + +在 Chrome、Firefox 和 Safari 中的 `button` 元素错误已修复。 + +#### 内联元素不被视为 Flex Items + +所有内联元素,包括 `::before` 和 `::after` 伪元素,在 Internet Explorer 10 中不能作为 flex item 元素工作。在版本 11 中,这个错误对常规内联元素已修复,但仍然影响 `::before` 和 `::after` 伪元素。 + +```html +
+``` + +```css +.element { + display: flex; +} + +.element::before { + content: 'Hello'; + flex-grow: 1; +} +``` + +`::before` 伪元素不会作为 flex item 元素工作。解决方法是向项目添加除 `inline` 以外的 `display` 值(例如,`inline-block`、`block` 或 `flex`)。 + +```css +.element::before { + content: 'Hello'; + flex-grow: 1; + display: block; +} +``` + +#### 使用 `flex` 简写时 `flex-basis` 中的 `!important` 被忽略 + +![image-20250325230617598](/img/image-20250325230617598.png) + +在 Internet Explorer 10 中,`!important` 规则在简写版本中与 `flex-basis` 不起作用。 + +```css +.element { + flex: 0 0 100% !important; +} +``` + +这不会起作用。`100%` 的 `flex-basis` 设置将被忽略。我们需要写出完整版本。 + +```css +.element { + flex: 0 0 100% !important; + flex-basis: 100% !important; +} +``` + +注意这个错误在 Internet Explorer 11 中已修复。 + +#### 使用 `margin: auto` 居中 Flex Item 在 Flexbox 容器设置为列时不起作用 + +![image-20250325230640393](/img/image-20250325230640393.png) + +你可以使用 `margin: auto` 在其容器中居中 flex item 元素。在 Internet Explorer 从 10 到 11 版本中,当 flexbox 容器的方向是列时,此功能不起作用。 + +```html +
+
+
+``` + +```css +.wrapper { + display: flex; + flex-direction: column; +} +.item { + margin: auto; +} +``` + +`.item` 不是居中的,而是根据 `align-self: stretch`(默认值)渲染。解决方案是: + +- 在 item 元素本身上使用 `align-self: center`, +- 在容器上使用 `align-items: center`。 + +这个问题在 Microsoft Edge 中已修复。 + +#### Flex Item 使用 `max-width` 不能正确对齐 + +![image-20250325230705998](/img/image-20250325230705998.png) + +当 `max-width` 用于 flex item 元素,与 flex 容器上的 `justify-content` 结合使用时,间距计算不正确。 + +```css +.item { + flex: 1 0 0%; + max-width: 25%; +} +``` + +这里的预期结果是元素的大小将从 `0% (flex-basis)` 开始,不会超过 `25% (max-width)`。我们可以通过为 `max-width` 而不是 `flex-basis` 设置值来实现相同效果,我们可以通过设置最小大小(行方向的 `min-width`,列方向的 `min-height`)让它收缩。 + +```css +.item { + flex: 0 1 25%; + min-width: 0%; +} +``` + +### Firefox 的 Flexbox 检查器 + +Firefox 在其 DevTools 中有一些调试 flexbox 组件的绝佳资源。它在每个作为 flex 容器的元素旁边显示"flex"标签。当在"检查器"面板中悬停元素时,信息栏(深灰色的那个)显示 flex 元素的类型。 + +![image-20250325230734257](/img/image-20250325230734257.png) + +很棒的是"flex"标签是可点击的。当它被点击时,Firefox 将突出显示 flex 布局项目。它也可以从"规则"面板中 CSS 声明旁边的小 flexbox 图标访问。 + +![image-20250325230751768](/img/image-20250325230751768.png) + +当你对 flexbox 布局如何工作有疑问时,突出显示很有用。利用这些工具——它们使你能够确保没有奇怪的事情发生,并清除对 flexbox 容器的任何混淆。 + +## CSS Grid + +### 意外的隐式轨道 + +CSS grid 的一个常见错误是通过将项目放置在网格的显式边界之外来创建额外的网格轨道。首先,**隐式**和**显式**网格之间有什么区别? + +![image-20250325230813400](/img/image-20250325230813400.png) + +```css +.wrapper { + display: grid; + grid-template-columns: 1fr 1fr; +} +.item-1 { + grid-column: 1 / 2; +} +.item-2 { + grid-column: 3 / 4; +} +``` + +`.item-1` 元素有一个**隐式**网格轨道,它被放置在网格的边界内。`.item-2` 元素有一个**显式**网格轨道,它将元素放置在定义的网格之外。 + +CSS grid 允许这样做。问题是当开发者不知道已创建隐式网格轨道时。在使用 CSS grid 时,确保为 `grid-column` 或 `grid-row` 使用正确的值。 + +### 具有 `1fr` 的列计算为零 + +有一种情况,具有 `1fr` 的列将计算为 `0` 的宽度,这意味着它是不可见的。 + +```html +
+
+
+
+
+
+``` + +```css +.wrapper { + display: grid; + grid-template-columns: repeat(3, minmax(50px, 200px)) 1fr; + grid-template-rows: 200px; + grid-gap: 20px; +} +``` + +我们有三个项目,最小 50 像素,最大 200 像素。最后一个项目应该占用剩余空间,`1fr`。如果前三个项目的宽度总和小于 600 像素,那么如果最后一列: + +- 完全没有内容, +- 没有边框或内边距。 + +则最后一列将**不可见**。 + +![image-20250325230843045](/img/image-20250325230843045.png) + +在使用 CSS grid 时记住这一点。这个问题起初可能令人困惑,但当你理解它如何工作时,你会没事的。 + +### 相等的 `1fr` 列 + +你可能认为 CSS grid 分数单位 `1fr` 像百分比一样工作。它不是。 + +```html +
+
Item 1
+
Item 2
+
Item 3
+
+``` + +```css +.wrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 200px; + grid-gap: 20px; +} +``` + +![image-20250325230908174](/img/image-20250325230908174.png) + +项目看起来相等。但是,当其中一个有很长的单词时,其宽度会扩展。 + +```html +
+
Item 1
+
+ I'm special because I have averylongwordthatmightmakemebiggerthanmysiblings. +
+
Item 3
+
+``` + +![image-20250325230926584](/img/image-20250325230926584.png) + +为什么会发生这种情况?默认情况下,CSS grid 的行为方式是给 `1fr` 单位一个 `auto` 的最小大小(`minmax(auto, 1fr)`)。我们可以覆盖这个并强制所有项目具有相等的宽度。默认行为对某些情况可能是好的,但它并不总是我们想要的。 + +```css +.wrapper { + /* other styles */ + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +``` + +注意上面的内容会导致水平滚动。请参阅水平滚动部分了解解决方法。 + +### 设置百分比值 + +CSS grid 的独特之处在于它有一个**分数**单位,可以用来分割列和行。使用百分比违背了 CSS grid 的工作方式。 + +```css +.wrapper { + display: grid; + grid-template-columns: 33% 33% 33%; + grid-gap: 2%; +} +``` + +为 `grid-template-columns` 和 `grid-gap` 使用百分比值会导致水平滚动。相反,使用 `fr` 单位。 + +```css +.wrapper { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-gap: 1rem; +} +``` + +### 误用 `auto-fit` 和 `auto-fill` + +我不会认为这是一个错误,但误用 `auto-fit` 和 `auto-fill` 可能导致意外结果。让我们首先区分它们。采用以下网格: + +```css +.wrapper { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-gap: 1rem; +} +``` + +我们的目标是 grid item 元素的最小宽度为 200 像素。 + +在 `auto-fill` 中,空轨道不会折叠到 `0`,从而保持空间原样。 + +在 `auto-fit` 中,浏览器将保持 200 像素的最小大小,如果有可用空间,空轨道将折叠到 `0`。因此,grid item 元素将占用剩余空间。 + +我正在为客户编写新部分的布局,在测试时,我发现一个错误告诉我右侧有**空**空间。我打开 DevTools 并意识到我正在为网格使用 `auto-fill`。 + +![image-20250325231002885](/img/image-20250325231002885.png) + +### 水平滚动和 minmax + +正如我在水平滚动部分中提到的,在没有适当测试的情况下使用 `minmax()` 可能导致 grid item 元素比视口宽,这将导致水平滚动。 + +```css +.wrapper { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + grid-gap: 16px; +} +``` + +如果视口窄于 350 像素,则会发生水平滚动。我们可以通过设置媒体查询来避免这种情况。 + +```css +.wrapper { + display: grid; + grid-template-columns: 1fr; + grid-gap: 16px; +} +@media (min-width: 400px) { + .wrapper { + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + } +} +``` + +这样,`minmax()` 函数只有在有足够空间时才会应用。 + +#### 浏览器实现问题 + +尽管 CSS grid 相对较新(于 2017 年 3 月发布),但它仍然可能具有挑战性,特别是在支持 Internet Explorer 11 中发布的旧版本时。为了避免任何问题,我建议使用 `@supports` 查询来检测浏览器是否支持新的网格规范。 + +```css +@supports (grid-area: auto) { + /* CSS grid code goes here */ +} +``` + +我使用 `grid-area` 是因为它是新网格规范的一部分。这样,Internet Explorer 11 不会应用 CSS grid。在 Internet Explorer 中支持网格并非不可能,但你需要坚持其旧实现。[Rachel Andrew](https://rachelandrew.co.uk/archives/2016/11/26/should-i-try-to-use-the-ie-implementation-of-css-grid-layout/) 详细写过这个主题。 + +## 处理长和意外内容 + +本节的标题呼应了我为 CSS-Tricks 写的[一篇文章](https://css-tricks.com/handling-long-unexpected-content-css/)。我将重新审视那篇文章并列出我们日常工作中出现的问题。我们有时努力构建组件而不考虑内容可能运行多长时间。思考这样的问题,并决定在这些情况下该怎么做。 + +> 当你编写 CSS 时,你正在编写抽象规则来获取未知内容并在未知媒体中组织它。- [Keith J Grant](https://twitter.com/keithjgrant/status/842728744653676544) + +### 忘记在文本标签和图标之间设置内边距 + +在某些布局中,我们需要为手风琴元素或输入字段添加图标作为 CSS 背景。 + +![image-20250325231049192](/img/image-20250325231049192.png) + +注意文本如何与图标重叠。那是因为右侧没有内边距。修复这个很简单,但在用户发现错误之前找到错误很困难。我将在下一章中解释一些防止错误发生的技术。 + +```css +.accordion { + padding-right: 50px; +} +``` + +具有图标的输入也可能发生同样的事情。 + +![image-20250325231116630](/img/image-20250325231116630.png) + +### 媒体对象中的长名称 + +"媒体对象"是 [Nicole Sullivan 创造的术语](http://www.stubbornella.org/content/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/)。它由左侧的图像和右侧的描述性文本组成。在我们不费力的情况下,如果文本很长且没有足够的空间适合图像旁边,文本将换行到新行。但是,如果它换行到新行,它可能会破坏设计或看起来很奇怪。 + +![image-20250325231140908](/img/image-20250325231140908.png) + +这个问题有不止一个解决方案。最常见的是: + +- 好老的浮动, +- flexbox。 + +假设我们的标记看起来像这样: + +```html +
+ +
+ Written by +

Ahmad Shadeed

+
+
+``` + +#### 解决方案 1:浮动 + +要做到这一点,我们需要将图像向左浮动,然后添加 clearfix 来解决浮动引起的问题。 + +```css +.card-meta img { + float: left; +} +.card-meta::after { + content: ''; + display: table; +} +``` + +#### 解决方案 2:Flexbox + +Flexbox 更好,因为我们只需要将其应用于父元素。 + +```css +.card-meta { + display: flex; +} +``` + +这将保持图像和文本在同一行。但是,我们应该考虑另一种情况,即如果我们不希望人名换行到新行?在这种情况下,`text-overflow` 来拯救。 + +```css +.card-meta h3 { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +``` + +## 总结 + +现在我们已经到达本章的结尾,我希望你对最常见的 CSS 属性及其问题更加熟悉。当然,我没有提到每一个属性,但我试图包含你在日常工作中会遇到的事情。 + +如果你仔细阅读了前四章,那么你将能够使用你学到的技术从头到尾解决任何 CSS 问题。 diff --git a/content/zh/environments_and_tools.mdx b/content/zh/environments_and_tools.mdx index 713d216..20cd3b8 100644 --- a/content/zh/environments_and_tools.mdx +++ b/content/zh/environments_and_tools.mdx @@ -1,5 +1,1219 @@ # 调试环境和工具 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +每个现代网络浏览器都内置了开发工具,或称为 DevTools。在 [预览](/overview) 章节,我解释了一些关于 Style Master 和 Firebug 工具的内容。浏览器 DevTools 就是基于这些项目开发的。要打开你的开发者工具,右键单击并从菜单中选择"Inspect element"。如果你习惯使用键盘,以下是各个浏览器的快捷键: - +- Chrome:Mac `⌥ + ⌘ + I`,Windows `Ctrl + Shift + I` +- Firefox:Mac `⌥ + ⌘ + C`,Windows `Ctrl + Shift + I` +- Safari:`⌥ + ⌘ + I` +- Edge:Mac `⌥ + ⌘ + I`,Windows `Ctrl + Shift + I` + +在本书中,除非我提到其他网络浏览器,否则浏览器默认指代 Google Chrome。 + +你可以检查任何元素并切换其 CSS 属性。要选择一个元素,右键单击并从菜单中选择"Inspect"。 + +![image-20250324222459019](/img/image-20250324222459019.png) + +当你选择"Inspect"时,浏览器的 DevTools 将在屏幕底部打开。这是它的默认位置。你可以通过点击右上角的点图标将其固定到屏幕的右侧或左侧。 + +![image-20250324222523009](/img/image-20250324222523009.png) + +点击点图标后,会打开一个小的下拉菜单。你可以根据偏好选择 DevTools 的固定位置。但是,当你在移动设备和平板电脑尺寸下测试时,推荐将其停靠到右侧。它看起来是这样的: + +![image-20250324222547869](/img/image-20250324222547869.png) + +## 切换 CSS 声明 + +我们已经打开了 DevTools 并知道如何访问它们。让我们检查一个元素并在基本级别上操作其 CSS。检查元素后,我们可以使用复选框切换其样式(复选框默认不可见)。 + +检查元素后,查看"Styles"选项卡。你会注意到,当你将鼠标悬停在 CSS 属性上时,CSS 声明前会出现一个复选框。当取消选中此框时,样式将被禁用且不会应用于元素。 + +![image-20250324222618529](/img/image-20250324222618529.png) + +当你关闭样式时,复选框将可见,为你提供视觉提示表明它已被禁用。 + +![image-20250324222643787](/img/image-20250324222643787.png) + +打开和关闭 CSS 声明类似于注释 CSS。实际上,如果你复制一个关闭了其中一个样式的 CSS 规则并将其粘贴到某处,编辑器将通过用 `/* */` 注释掉来禁用该样式。复制时 CSS 的外观如下: + +```css +.menu { + /* display: block; */ +} +``` + +## 使用键盘递增和递减值 + +在"Elements"面板中,你可以选择一个包含数字的 CSS 声明,并使用上下箭头键递增或递减值。你也可以手动输入值。 + +![image-20250324222800069](/img/image-20250324222800069.png) + +你还可以按住 `Shift`、`Command` 或 `Alt` 键与上下箭头键一起使用,以设定的间隔更改数字: + +- `Shift + up/down`: ±10 +- `Command + up/down`: ±100 +- `Alt + up/down`: ±0.1 + +这比使用上下箭头一次更改一个数字要快。 + +当你更改值并想要退出编辑模式时,你可以执行以下操作之一: + +- 点击 CSS 声明旁边的空白区域。 +- 按 `Escape` 键。 +- 按 `Enter` 键(这会移动到 CSS 规则中的下一个声明)。 + +## CSS 错误 + +鉴于 CSS 的性质,当出现拼写错误或属性使用不正确的值时,调试变得更加困难。你不会知道属性名称拼写错误,直到你检查显示 bug 的元素。CSS 的工作方式是浏览器将解析所有声明并忽略无效的声明。将此与 JavaScript 进行比较,在 JavaScript 中,错误会破坏整个脚本,打开浏览器的控制台会显示出了问题。 + +幸运的是,Firefox 有一个很棒的功能,当你使用没有效果的 CSS 属性时会显示警告。在撰写本文时,此功能仅在 Firefox 中可用。 + +![image-20250324222830308](/img/image-20250324222830308.png) + +希望更多浏览器会跟进! + +## DevTools 移动端模式 + +使用浏览器的 DevTools,你可以测试当前网站的不同视口大小。在本节中,我们将查看与现代浏览器(Chrome、Firefox、Safari、Edge)相关的移动端测试话题。 + +假设你收到客户或同事的消息说:"嘿,页面 X 上的字体大小在移动设备上太小了,无法阅读。我们能做些什么吗?" + +从他们的消息中,我们可以确定: + +- 字体大小太小无法阅读 +- 我们需要在移动视口中测试 + +我们需要做的第一件事是在浏览器中启动 DevTools,并切换到设备工具栏(在 Chrome 中)。你可以通过点击 DevTools 左上角的移动图标或使用键盘快捷键(`Command + Shift + M`)来访问设备工具栏。从那里,我们可以开始测试不同的尺寸,并最终找到问题的根源。 + +![image-20250324222857995](/img/image-20250324222857995.png) + +其他浏览器,如 Firefox 和 Safari,也有设备模式,但称其为"响应式设计模式"。以下是访问方法: + +- Firefox: Tools > Web Developer > Responsive Design Mode +- Safari: Develop > Enter Responsive Design Mode + +让我们回顾一下测试时需要记住的一些事项。 + +## 移动模式不显示水平滚动条 + +如果元素的宽度大于视口,则会生效水平滚动。尝试随机向左或向右滚动。这可以揭示任何不需要的滚动问题。注意:本书有一整章关于如何破坏布局。 + +## 滚动到视图 + +在移动模式下测试网站时,页面通常会很长,不断滚动到你想要检查的元素是不实际的。幸运的是,Chrome 有一个名为"滚动到视图"的功能,它会将页面滚动到你选择的部分。 + +![image-20250324222922966](/img/image-20250324222922966.png) + +## 截图设计元素 + +有时你需要截取页面的屏幕截图,在线可用的工具并不都很好。Chrome 和 Firefox 内置了截图功能。我特别喜欢 Firefox 的功能,名为"Screenshot Node",它只会截取所选 HTML 元素的屏幕截图。这非常有用且节省时间。 + +对于基于 Chromium 的浏览器(Chrome 和 Edge),过程是: + +1. 选择元素; +2. 按 `Shift + Command + P`(确保没有浏览器扩展使用此命令); +3. 输入"capture node screenshot"并按"Enter" + +在撰写本书时,Chrome Canary 86 在检查器中支持"capture node screenshot"。它很快就会在 Chrome 中正式可用。 + +在 Firefox 中截图: + +1. 打开 Firefox 的 DevTools +2. 右键单击元素 +3. 选择"Screenshot Node" + +## 设备像素比 + +设备像素比(DPR,Device Pixel Ratio)是物理像素与逻辑像素之间的比率。例如,iPhone 5 的 DPR 为 2,因为物理分辨率是逻辑分辨率的两倍。 + +- 物理分辨率:960 × 640 +- 逻辑分辨率:480 × 320 + +在 Chrome 的 DevTools 的移动模式中,你会找到 DPR 的下拉菜单。有两种基本类型的屏幕:标准和"视网膜"。1x 图像在标准屏幕上看起来不错,但在视网膜屏幕上会看起来像素化。DPR 有三个比率:1x、2x 和 3x。Chrome 将其命名为"设备像素比",而 Firefox 和 Safari 列出了提到的比率。这里的好处是我们可以测试图像并模拟它们在不同分辨率下的外观。 + +正如 [Google Developers 所述](https://developer.chrome.com/docs/devtools/device-mode/): + +> 要在标准显示器上模拟此效果,请将 DPR 设置为 2 并通过缩放来缩放视口。2x 资源将继续看起来清晰,而 1x 资源将看起来像素化。 + +如果你有标准屏幕且 `1x` 图像对你来说看起来不错,可以通过将 DPR 设置为 `2` 或选择 `2x` 作为选项然后放大一次来模拟它在 `2x` 屏幕上的外观。 + +![image-20250324222957579](/img/image-20250324222957579.png) + +一般来说,尽可能使用 SVG。这并不总是可行的,所以如果你使用图像,请为它们提供不同的分辨率。例如,你可以使用 HTML `` 元素来加载同一图像的不同分辨率和大小。然后浏览器将提供适合屏幕大小的分辨率。 + +## 切换用户代理 + +根据 Mozilla Developer Network(MDN): + +> User-Agent 请求标头是一个特征字符串,让服务器和网络对等方识别请求用户代理的应用程序、操作系统、供应商和/或版本。 + +用户代理使服务器能够识别访问者正在使用的浏览器。每个浏览器都有自己的用户代理。 + +每个浏览器还允许你测试不同的用户代理。如果你在 Windows 上使用 Chrome,你可以将浏览器切换到"macOS 上的 Safari"。Web 服务器将识别你正在浏览的用户代理。 + +要调试和检查当前浏览器的用户代理,打开 DevTools 的控制台并输入以下内容: + +```js +console.log(navigator); +``` + +我在 macOS 上使用 Chrome。日志显示此字符串: + +> Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36 + +为什么要调试这个?嗯,有一些重要的用例。考虑这个图: + +![image-20250324223030006](/img/image-20250324223030006.png) + +我们有一个应用程序的下载按钮,它应该根据用户的操作系统而改变。 + +另一个用例是在 Chrome 和 Firefox 浏览器中都可用的浏览器扩展: + +![image-20250324223049392](/img/image-20250324223049392.png) + +更改用户代理的过程将取决于你使用的浏览器: + +- Chrome: Network Conditions > 取消选择"Select Automatically" > 选择用户代理 +- Safari: Develop > User Agent +- Firefox: 我发现在 Firefox 中有点复杂,所以我推荐使用扩展。 + +## 调试媒体查询 + +媒体查询是响应式网页设计的基础。没有它们,网络就不会是今天这个样子。要调试媒体查询,我们需要 DevTools 的强大功能。 + +首先,检查你正在调试的 CSS(以防你没有编写它)。代码是否使用 `min-width` 媒体查询多于 `max-width`?你是否看到任何 `max-width` 媒体查询?这很重要,因为移动优先设计,你可能听说过。它需要首先为小屏幕编写 CSS,然后为平板电脑和桌面设备等更大的屏幕增强体验。 + +![image-20250324223115465](/img/image-20250324223115465.png) + +在这里,我们有一个导航切换,默认在小屏幕上显示。当视口足够大以显示导航项目时,切换消失。 + +```css +.nav__toggle { + /* Shown by default */ +} + +.nav__menu { + /* Hidden for mobile */ + visibility: hidden; + opacity: 0; +} + +@media (min-width: 900px) { + .nav__toggle { + display: none; + } + + .nav__menu { + visibility: visible; + opacity: 1; + } +} +``` + +但是,如果这不是移动优先构建的,那么菜单切换将默认隐藏,并通过 `max-width` 媒体查询显示: + +```css +.nav__toggle { + display: none; +} + +@media (max-width: 900px) { + .nav__toggle { + display: block; + } + + .nav__menu { + visibility: hidden; + opacity: 0; + } +} +``` + +在调试项目时,你需要深入了解这些细节,以了解你正在处理的内容。这将帮助你更快地修复问题并减少不必要的副作用。 + +要在 Chrome 的 DevTools 中查看媒体查询,你需要选择受其影响的元素: + +![image-20250324223202989](/img/image-20250324223202989.png) + +注意,当我们选择一个元素时,我们看到它的媒体查询。方便的是,我们可以编辑媒体查询并直接在 DevTools 中测试。上图显示了一个正常情况,没有任何问题。让我们探索与媒体查询相关的最常见错误。 + +### 不要忘记 Meta Viewport 标签 + +`viewport` meta 标签告诉浏览器,"嘿,请考虑这个网站应该是响应式的?"将以下内容添加到 HTML 的 head 元素中: + +```html + +``` + +### 媒体查询的顺序很重要 + +媒体查询的一致排序很重要。 + +```css +@media (min-width: 500px) { + .nav__toggle { + display: none; + } +} + +.nav__toggle { + display: block; +} +``` + +你能猜测 `.nav__toggle` 元素在宽度超过 500 像素的视口中是否可见吗?答案是肯定的,因为 `.nav__toggle` 的第二个声明覆盖了媒体查询中的声明。 + +![image-20250324223256681](/img/image-20250324223256681.png) + +DevTools 将显示类似于上图的内容。媒体查询中的样式将被删除线划掉,意味着它已被取消或覆盖。那么,解决方案是正确排序它们: + +```css +.nav__toggle { + display: block; +} + +@media (min-width: 500px) { + .nav__toggle { + display: none; + } +} +``` + +## 如果媒体查询不起作用怎么办? + +当有人报告错误时,说媒体查询不起作用是不够的。但是,我们可以通过简单的测试检查媒体查询是否起作用。假设我们有这个: + +```css +@media (min-width: 500px) { + .element { + background: #000; + } +} +``` + +要测试媒体查询,我们可以为 `body` 添加背景颜色: + +```css +@media (min-width: 500px) { + body { + background: red; + } +} +``` + +仍然不起作用?然后检查是否: + +- 缓存的 CSS 已清除, +- CSS 文件正确链接, +- 媒体查询没有拼写错误且没有缺少闭合大括号。 + +### 避免双断点媒体查询 + +一个常见的错误是在两个媒体查询中使用相同的值,一个使用 `min-width`,另一个使用 `max-width`。这通常发生在移动导航中。 + +```css +@media (max-width: 500px) { + .nav { + display: none; + } +} +@media (min-width: 500px) { + .nav__toggle { + display: none; + } +} +``` + +乍一看,这些媒体查询可能对你来说看起来不错。但是,99% 的时间,你会忘记测试一个重要的断点:`500px`,两个断点之间的 1 像素间隙。在这个断点,导航和切换都不会可见。 + +![image-20250324223351992](/img/image-20250324223351992.png) + +这 1 像素很难调试,除非在移动模式下手动输入 500px 媒体查询的值。为了防止这个问题,避免在两个媒体查询中使用相同的值。 + +```css +@media (max-width: 499px) { + .nav { + display: none; + } +} + +@media (min-width: 500px) { + .nav__toggle { + display: none; + } +} +``` + +### 列出媒体查询 + +在 Chrome 中,你可以根据 CSS 中定义的媒体查询查看页面,而不是根据浏览器中可用的设备列表。 + +![image-20250324223410895](/img/image-20250324223410895.png) + +如你所见,我们有两个条,蓝色条用于 min-width 查询,橙色条用于 max-width 查询。拥有所有媒体查询的更广泛视图对于测试多个查询大小很有用。方便的是,我们可以在源代码中显示媒体查询。右键单击媒体查询,选择"Reveal in source code",你将被带到该媒体查询的代码行。 + +![image-20250324223424172](/img/image-20250324223424172.png) + +### 垂直媒体查询很重要 + +响应式网页设计的一个常见错误是仅通过调整浏览器宽度或查看多个移动尺寸来测试。但是,减少和增加视口的高度同样重要。 + +就像我们有针对视口宽度的媒体查询一样,高度也适用同样的情况。考虑以下示例: + +![image-20250324223445451](/img/image-20250324223445451.png) + +减少视口高度后,我们发现固定标题占用了屏幕垂直空间的很大一部分。注意内容可用区域有多小。用户会感到恼火,无法轻松使用网站。一个简单的解决方案是仅在有足够垂直空间时固定标题。 + +```css +/* If the height is 800 pixel or more, the header should be fixed. */ +@media (min-height: 800px) { + .header { + position: fixed; + /* Other styles */ + } +} +``` + +### 不要仅依赖浏览器调整大小 + +调整浏览器大小来测试响应性是不够的。例如,Chrome 的窗口缩小到 500 像素的宽度。这是不够的。你需要测试低于该值(至少 320 像素)。相反,在 DevTools 的设备模式下测试网站。 + +## 盒模型 + +如果我们记住盒模型的任何东西,那应该是网页上的每个元素都是一个矩形框,包含以下一个或多个:位置、边距、边框、内边距和内容。 + +如果将内边距和边框应用于元素,它们将被添加到其内容框中,除非该元素的 `box-sizing` 属性设置为 `border-box`。进行此更改以避免任何问题。 + +```css +html { + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} +``` + +![image-20250324223540202](/img/image-20250324223540202.png) + +我检查了一个网站的徽标以查看其盒模型。注意你需要打开"Computed"选项卡来查看盒模型。所有框都有标签,除了宽度和高度的框。在调试元素时,查看盒模型非常有用,因为它会显示元素的所有内部和外部间距。 + +有时,我们可能在没有 CSS 重置文件的情况下构建网页,我们会想知道为什么某些元素有某些间距。查看盒模型是了解正在发生什么的最佳方法。 + +### CSS 中的一切都是盒子 + +网页上的每个元素都是正方形或矩形。即使元素显示为圆形或具有圆角边缘,它仍然是矩形框。在调试时,请记住这一点。 + +![image-20250324223606123](/img/image-20250324223606123.png) + +亲自看到这一点的最简单方法是访问你最喜欢的网站并将 `outline` 属性应用于所有内容: + +```css +*, +*::before, +*::after { + outline: solid 1px; +} +``` + +我们已经标记了页面上的每个元素,包括伪元素(`:before` 和 `:after`)。这样,我们可以看到页面本质上是一些在这里和那里绘制的矩形。 + +## 计算的 CSS 值 + +在 CSS 中,无论你使用 `rem`、`em`、`%` 还是视口单位,一切都计算为像素值。即使无单位的 `line-height` 属性也计算为像素值。在某些情况下,查看元素的计算值很重要。 + +```css +.element { + width: 50%; +} +``` + +`.element` 的宽度是 `50%`,但我们如何看到其计算值?好吧,感谢 DevTools,我们可以做到这一点。让我们深入"Computed"选项卡。 + +![image-20250324223706925](/img/image-20250324223706925.png) + +你会看到各个部分都分配了数字,以便更容易解释 DevTools 的这个区域。 + +1. 这是属性的名称。通常,它的颜色与值不同。 + +2. 这是 CSS 属性的值。 + +3. 我们可以展开属性以查看其继承的样式。此元素具有 `font-size: 1.125rem`,并且它从 `html` 元素继承 `1em` 字体大小。 + +4. 这是预计算值,以及值附加到的元素。 + +5. 这是 CSS 文件的名称和 CSS 规则的行号。 + +6. 当悬停在属性值上时,会出现一个箭头。单击它将带你到源 CSS 文件和行号。 + +7. 此过滤器有助于搜索属性。注意你只能按属性的完整名称搜索。例如,搜索 `grid-gap` 不会显示任何内容,而搜索 `grid-column-gap` 会返回结果。 + +8. 默认情况下,并非所有 CSS 属性都显示在"Computed"选项卡中。你需要选中此框以查看所有属性。 + +## 灰色属性 + +![image-20250324223747376](/img/image-20250324223747376.png) + +你会注意到没有明确设置高度的元素可能有灰色的 `height` 属性。 + +```css +.nav__item a { + padding: 1rem 2rem; +} +``` + +`` 元素没有设置高度,但实际上,它的高度是内容和内边距的总和,这是明确高度的替代方案。 + +这不仅发生在内边距上。下面的示例有两个元素,其中一个是空的。 + +```html +
+
content
+
+
+``` + +```css +.wrapper { + display: flex; +} + +.element { + width: 100px; + padding: 1rem; +} +``` + +Flexbox 默认拉伸子项目。因此,项目的高度将相等,即使是空的。如果你检查该元素并检查其 `height` 属性的计算值,你会注意到它是灰色的。 + +提示:灰色属性意味着其值未明确设置。相反,它受到其他因素的影响,例如元素的内容或内边距,或者作为 flexbox 子项。 + +## Firefox 的样式编辑器 + +![image-20250324223814545](/img/image-20250324223814545.png) + +Mozilla Firefox 浏览器中的样式编辑器是浏览器中的一种设计应用程序。以下是你可以用它做的一些很棒的事情: + +1. 创建新样式表并将其附加到文档。 + +2. 导入 CSS 文件。 + +3. 列出文档的所有 CSS 文件,能够通过单击眼睛图标激活和停用它们(类似于在设计应用程序中显示和隐藏图层)。 + +4. 从列表中保存文件。 + +5. 列出所选 CSS 文件中的所有媒体查询。活动的将以深色突出显示,非活动的将变暗。你可以跳转到具有媒体查询的代码部分。 + +6. 单击媒体查询。 + +我特别喜欢的是你可以隐藏所有 CSS 文件,这相当于禁用 CSS。 + +此外,能够将 CSS 文件导入页面很有用,开辟了很多可能性。想象一下,你正在为网页布局工作,想要在这里和那里更改一些东西,但不想丢失你的工作。你可以导入新的 CSS 文件,将当前 CSS 复制到其中,然后根据需要编辑它。完成后,你可以在旧 CSS 和新 CSS 之间切换以查看完全不同的布局。 + +## 没有效果的 CSS 属性 + +一些 CSS 属性有依赖关系。以下面为例: + +```css +.element { + z-index: 1; +} +``` + +如果你没有将元素的 `position` 更改为 `static` 以外的任何内容,那么它根本不会影响元素。在调试时很难发现这些问题,因为它们不会破坏布局。它们是静默的。 + +Firefox 有一个非常有用的功能,当 CSS 属性不影响应用它的元素时显示警告。 + +![image-20250324223957310](/img/image-20250324223957310.png) + +这非常有用,在撰写本文时,仅在 Firefox 中可用。 + +## Firefox 中的兼容性支持 + +在检查元素的 CSS 时,你可以看到支持特定功能的浏览器以及浏览器版本。你可以通过悬停在其中一个上查看详细信息。我非常喜欢这个功能,因为它为你提供了关于哪些浏览器需要更仔细测试的提示。 + +![image-20250324224020758](/img/image-20250324224020758.png) + +## 在调整浏览器大小时获取计算值 + +仅查看元素的计算值是不够的。更有用的是过滤你需要检查的特定属性,然后调整响应式视图包装器的大小以查看值的变化。 + +```css +.title { + font-size: calc(14px + 2vw); +} +``` + +在这里,我们有一个标题,基础字体大小为 `14px` 加上 `2vw`(视口宽度的 2%)。这里是一个解释器: + +![image-20250324224046855](/img/image-20250324224046855.png) + +我搜索了 `font-size`,然后开始调整左侧的视图大小。这是保持自己与后台发生的事情保持一致的非常有用的方法。 + +## 使用 JavaScript 获取计算值 + +通过使用 JavaScript 的 `getComputedStyle` 方法,可以获取特定属性的值。考虑以下示例: + +```css +.element { + font-size: 1.5rem; +} +``` + +我们使用 `rem` 单位设置了字体大小。要获取计算的像素值,我们会这样做。 + +```js +let elem = document.querySelector('.element'); /* [1] */ +const style = getComputedStyle(elem); /* [2] */ +const fontSize = style.fontSize; /* [3] */ +``` + +代码的作用如下: + +1. 它选择元素。 +2. 它将元素的样式存储在 `style` 变量中。 +3. 现在 `style` 变量是一个对象,保存元素的所有样式,我们可以获取属性的计算值。 + +很酷!如果我们想要检查的属性具有基于视口或百分比的值(例如,`font-size: calc(14px + 2vw)`)怎么办?该字体大小的值会随着每次视口调整大小而改变。 + +```js +let elem = document.querySelector('h1'); +window.addEventListener('resize', checkOnResize); +function checkOnResize() { + const style = getComputedStyle(elem); + console.log(style.fontSize); +} +checkOnResize(); +``` + +如你所见,这是相同的概念,但这次使用了 `resize` 事件。这对于跟踪事物非常有用,你甚至可以在页面本身上呈现值: + +![image-20250324224129520](/img/image-20250324224129520.png) + +## 重新排序 HTML 元素 + +在 Chrome 的 DevTools 中,你可以单击并拖动元素来重新排序。这对于更改整个页面或组件的结构很有用。重新排序后,我们可以开始测试各种事情,例如: + +- flexbox `order` 属性, +- 相邻兄弟组合器(`.item + .item`), +- 具有 `margin-bottom` 或 `margin-top` 的元素。 + +让我们深入了解重新排序的工作原理。 + +1. 打开 DevTools。 +2. 选择要重新排序的元素。 +3. 单击并将元素拖动到你想要的任何位置。 + +这是你如何拖动 section 元素及其兄弟元素的方法: + +![image-20250324224157223](/img/image-20250324224157223.png) + +我们也可以重新排序子项目。假设每个 section 都有标题和描述。我们可以在其父级内重新排序它们。 + +![image-20250324224216426](/img/image-20250324224216426.png) + +子元素可以通过多种方式拖动: + +- 在其父级内(这只会在同一级别重新排序), +- 在其他父元素之间, +- 在另一个父元素内。 + +![image-20250324224303804](/img/image-20250324224303804.png) + +## 在 DevTools 中编辑元素 + +在 DevTools 中编辑 HTML 元素有多种方法。在你想要添加类或属性或者可能删除整个元素的情况下,它们非常有用。以下是执行此操作的方法: + +- 添加或删除 CSS 类, +- 更改元素类型(例如,`
` 到 `

`), +- 添加或删除属性, +- 删除元素。 + +### CSS 类 + +要添加、编辑或删除 CSS 类,你可以双击元素的类名,它将变为可编辑。但这是不太推荐的方法。更好的方法是选择元素,然后在打开 DevTools 的情况下单击 `.cls` 标签。被单击后,它将显示与所选元素关联的所有类,我们可以通过选中和取消选中框来添加或删除它们。 + +![image-20250324224325917](/img/image-20250324224325917.png) + +### 基于实用程序的 CSS 网站 + +如果你正在处理的网站是用基于实用程序的 CSS 构建的,那么在浏览器中调试其 CSS 将与调试具有常规类名的网站不同。 + +这是一个具有类名的元素: + +```html +
+``` + +这是使用基于实用程序的 CSS 的相同元素: + +```html + +``` + +当整个网站都使用实用程序类构建时,调试会有所不同。假设我们想要检查元素并通过在 DevTools 中取消选中框来删除 `display: flex`。如果我们这样做,任何使用 `d-flex` 类的元素都会损坏。原因当然是我们已经从所有其他元素中删除了 `display` 属性。 + +使用 `.cls` 选项会更好,因为它会列出该元素的所有 CSS 类。 + +![image-20250324224411463](/img/image-20250324224411463.png) + +另一个选项是添加内联 CSS 样式,这将覆盖 CSS 文件中添加的样式。或者你可以双击元素的 class 属性并手动删除你不想要的类。 + +### 更改元素的类型 + +假设你有一个 `div` 元素,但想要在不离开 DevTools 的情况下更改其类型。好吧,这是可能的。要更改它,双击元素类型,然后编辑开始标签。 + +注意:不需要编辑结束标签。DevTools 会自动执行此操作。 + +![image-20250324224434241](/img/image-20250324224434241.png) + +### 添加或删除属性 + +当你需要添加属性时,选择元素,右键单击,然后选择"Add Attribute"。就这么简单。注意你也可以通过双击元素本身来添加它。 + +![image-20250324224451606](/img/image-20250324224451606.png) + +### 删除元素 + +要删除元素,按 `Function + Backspace` 键。这将在所有浏览器和所有平台上工作。如果你使用 Chrome,只需按 `Backspace` 键。鼠标是一个替代方案:右键单击所选元素,然后从列表中选择"Delete"。 + +![image-20250324224507438](/img/image-20250324224507438.png) + +### 键盘优势 + +一些键盘快捷键非常有用并提高生产力: + +- 使用上下箭头键在元素之间导航。 +- 按右箭头键展开元素,按左箭头键折叠它。 +- 当选择元素时,按 `Enter` 编辑 CSS 类名。 + +## H 键 + +在 DevTools 中隐藏元素的最快方法是按 H 键,这将为元素添加 `visibility: hidden`。元素占用的空间将被保留。 + +以这种方式隐藏元素有什么好处?以下是几个用途: + +- 如果你有一个元素的子元素没有按预期显示,我们可以使用 `H` 来调查它。 +- 如果你需要截屏元素或部分,并且不希望所有细节都在图像中,只需使用 `H` 隐藏不需要的元素。 + +## 强制元素状态 + +在 CSS 中,元素可以采用四种状态(伪类)之一:`:visited`、`:focus`、`:hover`、`:active`。幸运的是,我们可以使用 DevTools 调试所有这些状态。 + +在深入了解如何调试它们之前,让我们回顾一下伪类。 + +- `:visited` 是链接被点击时的状态。当用户重新访问网页时,该链接应该有不同的状态,以便用户可以知道他们已经访问过它。 +- `:focus` 是用户通过键盘导航页面时显示的状态。按钮或链接应该采用清楚表明它处于焦点的样式。 +- `:hover` 是元素被鼠标悬停时的状态。 +- `:active` 是元素被鼠标点击按下时的状态。 + +在 CSS 中,伪类的顺序很重要。它应该如下: + +```css +a:visited { + color: pink; +} + +a:focus { + outline: solid 1px dotted; +} + +a:hover { + background: grey; +} + +a:active { + background: darkgrey; +} +``` + +如果不遵循此顺序,某些样式将被覆盖。正确排序样式以避免问题。 + +让我们进入有趣的部分。我们如何在 DevTools 中调试这些伪类?好吧,有两种方法。 + +### 选择元素 + +右键单击元素或单击左侧的点图标,然后选择"Force State"。从选项列表中,选择要激活的状态。见下图: + +![image-20250324224555968](/img/image-20250324224555968.png) + +选中框会在左侧的元素上添加蓝点。此视觉指示器显示元素具有强制状态。 + +### 使用面板 + +强制元素状态的另一种方法是使用 DevTools 面板。单击 `:hov` 将展开包含所有伪类的列表。每个都有一个复选框,这使得在测试时轻松激活或停用状态。 + +![image-20250324224613110](/img/image-20250324224613110.png) + +### 切换元素状态 + +我们也可以手动添加伪类: + +1. 选择元素。 +2. 单击面板中的 `+` 按钮。 +3. 将在样式面板中添加新规则。你现在需要做的就是编辑它并添加你想要的伪类。 + +![image-20250324224628468](/img/image-20250324224628468.png) + +## 调试通过 JavaScript 显示的元素 + +在某些情况下,悬停在元素上会向 DOM 添加子元素。调试这些元素很棘手。它们将在检查器中隐藏,因为你没有主动悬停在它们上面。 + +问题是如何调试隐藏元素?好吧,有几种方法。 + +### 元素在 HTML 中吗? + +在这种情况下,我们想要调试的元素已经在 HTML 中,但通过 CSS 隐藏,只有在其父元素被悬停时才显示。要调试这个,我们需要做的第一件事是检查父元素。什么是父元素,你问?这应该澄清: + +![image-20250324224649740](/img/image-20250324224649740.png) + +在这个例子中,我们有一个通过 JavaScript 在悬停时切换的下拉菜单。要调试下拉菜单本身,我们会检查"Products"菜单项,下拉元素应该在其中。从那里,我们可以为元素添加 `display: block` 并开始调试过程。 + +### 元素在悬停时添加到 HTML 吗? + +这更具挑战性。在这种情况下,元素仅在其父元素被悬停时添加到 HTML,并且当用户停止悬停时完全从 HTML 中删除。我们需要 DevTools 的帮助。要调试,我们需要在我们想要调试的东西变得可见后冻结网站。最好的方法是暂停 JavaScript 的执行。 + +当 JavaScript 执行暂停时,可以跟踪切换菜单的代码。这样,我们可以在元素出现后捕获它,并从那里检查它。 + +一个重要的线索表明元素在悬停时被添加到 DOM 是其父元素闪烁红色。闪烁意味着这个 DOM 元素正在通过添加或删除子项目或可能修改属性来编辑。 + +![image-20250324224713358](/img/image-20250324224713358.png) + +我们如何暂停 JavaScript 执行?简单: + +1. 转到"Sources"面板。 +2. 展开"Event Listener Breakpoints"。 +3. 向"Keyboard"添加事件监听器。 + +现在,悬停在你想要调试的元素上。一旦你这样做,按键盘上的任何键,你会注意到应用程序冻结,你想要检查的东西不会消失。随意深入检查! + +![image-20250324224728393](/img/image-20250324224728393.png) + +## 中断 JavaScript + +在 Chrome 和 Firefox 的 DevTools 中,你可以通过以下任何一种方式中断 JavaScript 的执行: + +- 子树修改, +- 属性修改, +- 节点删除。 + +![image-20250324224747629](/img/image-20250324224747629.png) + +让我们深入了解每一个。 + +### 子树修改 + +这针对所选父级的子项目。如果发生任何 HTML 元素的添加或删除,这被认为是修改。在这种情况下,浏览器将添加断点。 + +### 属性修改 + +这监视所选元素属性的任何修改,例如类名和 HTML 属性。例如,对类或样式属性的更改会导致浏览器添加断点,然后通过 JavaScript 显示菜单。 + +### 节点删除 + +这是相当明显的。一旦元素从 HTML 中删除,JavaScript 执行将暂停。 + +JavaScript 执行将暂停。 + +## 使用 Debugger 关键字 + +有时,CSS 错误会在某个 JavaScript 函数运行时出现。例如,你可能需要在移动菜单切换后调试它。在这种情况下,`debugger` 关键字可能很有用。 + +在切换按钮的 JavaScript 中,你会添加以下内容: + +```javascript +function showNav() { + debugger; + // Add a breakpoint once this function is called. +} +``` + +一旦调用此函数,浏览器将添加断点。然后,你可以开始调试。 + +注意,如果浏览器不支持 `debugger` 关键字,这不会有任何效果。 + +## 格式化源代码以便更容易阅读 + +当你检查元素并想要从 DevTools 检查其 CSS 文件时,你可能会发现文件已被压缩。这使得它难以阅读。幸运的是,我们有小的"Format Code"功能,它可以快速格式化压缩的代码。 + +![image-20250324224820087](/img/image-20250324224820087.png) + +注意开闭大括号图标。单击一下,所有代码都将被格式化并易于阅读。 + +## 复制元素的 HTML 及其 CSS + +唯一允许你复制元素 CSS 样式的浏览器是 Chrome,尽管它并不完美。在撰写本文时的最新版本 Chrome 81 具有该功能。以下是如何做到的: + +1. 右键单击元素。 +2. 选择"Copy"。 +3. 然后,选择"Copy styles"。就是这样! + +![image-20250324224837898](/img/image-20250324224837898.png) + +在上图中,注意原始 CSS 和从浏览器 DevTools 复制的 CSS 之间的区别。复制的 CSS 具有继承的样式,如 `box-sizing` 和 `font-family`。此外,奇怪的是,它复制了文档中的所有 CSS 属性! + +## 渲染字体 + +渲染字体是当前用于网页的字体。要调试它们,检查任何文本元素,如标题或段落。在"Computed"选项卡的底部,将有一个名为"Rendered Fonts"的部分。在这里,你可以检查应用于元素的字体。 + +![image-20250324224902672](/img/image-20250324224902672.png) + +此外,如"Computed"部分所述,你可以搜索 `font-family` 并查看其计算值。此外,你可以展开其旁边的箭头并查看负责添加字体的 CSS。 + +## 检查未使用的 CSS + +Chrome 的 DevTools 中的一个有用功能使你能够检查未使用的 CSS。它被称为"Coverage"。以下是如何使用它: + +1. 打开 DevTools。 +2. 单击点图标,然后选择"More"。 +3. 打开"Coverage"面板并点击重新加载图标。 + +重新加载后,你会看到类似以下内容: + +![image-20250324224925702](/img/image-20250324224925702.png) + +以红色突出显示的代码块是页面上未使用的代码块,而蓝色的代码块正在使用。此外,它显示了已使用 CSS 的百分比。该功能对于重构 CSS 和检查是否有未使用的样式非常有用。 + +## 使用 DevTools 进行颜色切换 + +Chrome 的 DevTools 提供三种类型的颜色系统:hex、RGBa、HSLa。当你为元素选择颜色时,它通常作为十六进制颜色添加。如果你想要该颜色的 RGBa 值而不必使用转换器工具怎么办?好吧,该功能在颜色检查器中可用。 + +![image-20250324224941531](/img/image-20250324224941531.png) + +如果你想要特定的蓝色,设计要求你使用 `50%` 不透明度,将颜色作为十六进制值添加到元素,并且在颜色检查器仍然打开的情况下,单击右侧的双箭头图标。这将在 hex、RGBa 和 HSLa 之间切换,非常方便快速将颜色从一种类型转换为另一种类型。 + +## 从 DevTools 复制 CSS 到源代码 + +当你编辑元素的 CSS 时,你可能希望将其复制并粘贴回代码编辑器,而不是重新编写。有多种方法可以做到这一点。 + +### 直接从内联检查器复制 + +在下图中,我为元素添加了一些内联样式。注意它们如何添加到 DevTools 中的 `element.style` 选择器。此功能对于 Chrome、Firefox 和 Safari 是相同的。 + +![image-20250324225003788](/img/image-20250324225003788.png) + +现在我们已经添加了这些内联样式,可以将它们复制并粘贴到我们的代码编辑器中。 + +### 在 Firefox 浏览器中使用 `changes` 功能 + +Firefox 有一个名为"Changes"的有用功能,显示我们在 DevTools 中所做的更改。它不像版本控制显示两个更改之间的差异。以下是如何使用它: + +1. 检查要编辑的元素。 +2. 编辑样式。 +3. 转到"Changes"选项卡,你将看到你所做的编辑。 + +![image-20250324225031469](/img/image-20250324225031469.png) + +## 调试 Source-Map 文件 + +当使用 Sass 等预处理器时,渲染的 CSS 文件可能包含链接的 source-map 文件的指令。当你在 DevTools 中检查元素的 CSS 时,你会注意到 CSS 在扩展名为 `.scss` 的文件中。这可能令人困惑。 + +要从 DevTools 中删除该 `.scss` 文件,你必须关闭 source-map 功能;或者你可以在浏览器中打开"Sources"面板并选择 CSS 文件。然后,你会找到类似这样的内容: + +```css +/*# sourceMappingURL=index.css.map */ +``` + +这是一个指令,使浏览器加载 Sass 文件。删除它将完全隐藏 source-map 文件。 + +## 调试由 CSS 引起的可访问性问题 + +尽管大多数可访问性问题是由误用的 HTML 引起的,但 CSS 在可访问性中也起作用。在本节中,你将学习在调试 CSS 时要记住的一些事情。 + +### 为文本提供足够的颜色对比度 + +太淡而无法阅读的颜色对用户来说是个问题。根据 Web 内容可访问性指南 (WCAG) 2.0,前景色和背景色在 AA 级别应具有 4.5:1 的对比度,在 AAA 级别应具有 7:1 的对比度。 + +为了实现这一点,使用经过充分测试的颜色。有很好的工具可以让我们的工作更轻松。要检查文本颜色是否可访问,检查元素,然后单击小颜色方块。你会看到对比度数字。 + +![image-20250324225104444](/img/image-20250324225104444.png) + +### 在使用 display: none 隐藏之前三思 + +错误使用 `display: none` 会阻碍可访问性。它很容易破坏体验。假设你有搜索输入和按钮,设计要求隐藏 `label` 元素。 + +```html + + + +``` + +![image-20250324225132479](/img/image-20250324225132479.png) + +`label` 应该在视觉上隐藏,但不能使用 `display: none`。为什么? + +1. 你无法使用 `for` 属性将 `label` 绑定到 `input`。 +2. 屏幕阅读器无法读取输入的标签。如果你幸运的话,它可能会读取占位符,如果已添加的话。 + +正确的方法是为标签添加 `visually-hidden` 类。这只会在视觉上隐藏它,因此不会成为可访问性问题。 + +这个代码片段来自[可访问性项目](https://www.a11yproject.com/posts/how-to-hide-content/): + +```css +.visually-hidden { + position: absolute !important; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + white-space: nowrap; /* added line */ +} +``` + +### 使用可访问性树 + +DevTools 中的可访问性面板是一个美丽的面板。它为我们提供了关于元素如何暴露给屏幕阅读器的线索。例如,如果我们选择 `input` 字段并检查可访问性树,它将显示标签(如果可用)和占位符。 + +修复与此相关的小问题可能产生巨大影响,你不需要成为可访问性专家就能做到。这是一个现实的例子: + +```html + + +``` + +我们有一个电子邮件 `input`,没有与之关联的 `label`。如果我们检查 `input` 并转到可访问性面板,我们会看到类似这样的内容: + +![image-20250324225207380](/img/image-20250324225207380.png) + +注意它说"textbox: Email address",它读取 `input` 占位符内的内容。没有它,字段将为空,这将是一个问题。在处理 Web 表单时,确保使用可访问性树进行调试。 + +当然,这不仅仅是关于表单。有些元素不应该暴露给屏幕阅读器用户——例如,带有伴随箭头的菜单项。 + +![image-20250324225225837](/img/image-20250324225225837.png) + +箭头是 span 内的 HTML 符号。检查时,它将文本显示为"Services ▼",这是不正确的。屏幕阅读器会读作:"Services down pointing black pointer"。非常令人困惑。强烈建议尽早调试此类问题。解决方案是为 span 元素使用 `aria-hidden=true`。 + +### 修复不可点击的元素 + +与按钮和链接的交互至关重要。当元素预期可点击但不可点击时,这是一个问题。误用 CSS 属性可以阻止元素交互。考虑这个例子: + +```css +.element { + pointer-events: none; +} +``` + +CSS `pointer-events` 阻止,例如,按钮上的事件发生。在这种情况下,当用户悬停在它上面时: + +- 光标不会改变, +- 点击它什么也不做。 + +一个简单的 CSS 属性可以阻止按钮被点击。误用属性可能破坏体验,导致用户流失。 + +## 调试 CSS 性能 + +某些 CSS 属性在用于动画时如果使用不当可能会导致性能问题。任何浏览器都可以廉价动画的属性是: + +- 变换(`translate`、`scale`、`rotate`); +- 不透明度。 + +使用其他属性进行动画是有风险的,可能会损害性能。让我们回顾一下浏览器如何计算其样式。以下是浏览器采取的步骤: + +1. **重新计算样式**:计算应用于每个元素的样式。 +2. **布局**:分配每个元素的宽度、高度和位置。 +3. **绘制**:将所有元素绘制到图层。 +4. **合成图层**:将图层绘制到屏幕。 + +最轻的步骤是合成。要获得良好的性能,只使用 `transform` 和 `opacity` 属性。下图比较了用于动画的 `left` 和 `transform: translateX`。 + +![image-20250324225253410](/img/image-20250324225253410.png) + +注意 `left` 时间线有多忙。浏览器在动画发生时不断重新计算样式。同时,`transform: translateX` 非常不同;浏览器的工作很轻。 + +要检查网页的性能,打开 DevTools,然后选择"Performance"选项卡。从那里,你可以开始分析和进行测试。配置文件就像在页面上运行一段时间(通常是几秒钟)的测试。完成后,你可以看到时间线,其中包含浏览器如何计算样式的所有详细信息。 + +我们对 CSS 的关注是重新计算和合成。避免使用重型 CSS 属性进行动画。 + +## 多个浏览器配置文件 + +你可能使用不同的浏览器,每个浏览器都存储你浏览的历史记录和私人信息。在你每天使用的浏览器中调试和测试网站可能没有意义。你需要一些新鲜的东西,没有历史记录或缓存,这样你就可以在没有任何不必要问题的情况下进行测试,比如 CSS 缓存,并避免可能导致错误的扩展。 + +因此,建议使用专用的浏览器配置文件进行测试。以下是在 Chrome 中创建一个的方法: + +1. 在右上角,单击用户头像。 +2. 单击"Add +"。 +3. 命名配置文件(例如,"Dev"),然后单击"Add"。 + +在 Firefox 中,它有点不同: + +1. 在浏览器的 URL 字段中打开 `about:profiles`。 +2. 单击"Create a new profile"。 +3. 选择名称并单击"Done"。 +4. 在同一页面上,向下滚动找到你创建的配置文件,然后单击"Launch profile in a new browser"。 + +完成!你已经创建了一个专门用于测试和调试的配置文件。 + +## 渲染和模拟 + +在 Chrome 中,我们可以模拟不同的渲染和模拟媒体查询,以帮助我们调试 CSS 查询 `@media print`。我们还可以使用 `prefer-color-scheme` 媒体查询调试网站的浅色和深色模式版本。 + +要访问渲染和模拟设置,请按照以下步骤操作: + +1. 打开 DevTools,然后单击垂直点菜单。 +2. 选择"More tools",然后选择"Rendering"。 +3. 向下滚动,你会找到模拟选项。 + +### CSS 打印样式 + +![image-20250324225325779](/img/image-20250324225325779.png) + +我们可以使用媒体查询来编辑 CSS 样式并定制要打印的页面。要调试和模拟网页打印时的外观,我们可以打印页面并将其保存为 PDF 文件,或者使用 Chrome 中的模拟功能。 + +```css +@media print { + /* All of your print styles go here. */ + .header, + .footer { + display: none; + } +} +``` + +网站的页眉和页脚可能不需要打印,因此可以使用打印媒体查询隐藏它们。 + +### CSS 媒体 `prefer-color-scheme` + +![image-20250324225350017](/img/image-20250324225350017.png) + +随着 iOS 正式支持它们,深色模式用户界面越来越受欢迎,并且在网络上也得到支持。在 CSS 中,我们可以使用以下内容来检测用户是否喜欢深色或浅色模式: + +```css +.element { + /* Light-mode styles (the default) */ +} +@media (prefer-color-scheme: dark) { + .element { + /* Dark-mode styles */ + } +} +``` + +在 macOS 上,你可以通过更改系统偏好设置在网站的深色和浅色模式之间切换。 + +![image-20250324225404691](/img/image-20250324225404691.png) + +虽然这有效,但当你在进行大量更改时可能不实用。幸运的是,可以在渲染设置中测试。我们可以模拟媒体查询 `prefer-color-scheme: dark` 或 `prefer-color-scheme: light`。 + +### CSS 媒体 `prefers-reduced-motion` + +![image-20250324225419045](/img/image-20250324225419045.png) + +你不能假设所有用户都能接受在你的网站上播放动画。有些人更喜欢不在页面上看到动画,因为它可能影响可访问性。媒体查询检查用户是否已请求系统最小化非必要运动。 + +```css +.element { + animation: pulse 1s linear infinite both; +} +@media (prefers-reduced-motion: reduce) { + .element { + animation: none; + } +} +``` + +更好的是,你可以为喜欢减少运动的用户提供更简单的动画。 + +```css +@media (prefers-reduced-motion: reduce) { + .element { + animation-name: simple; + } +} +``` + +有了这个,我们就完成了对浏览器 DevTools 的回顾。让我们回顾一下我们可以用来测试和调试 CSS 的其他方法。 + +## 虚拟机 + +在你作为 Web 开发人员的工作过程中,你需要在除了你通常使用的浏览器和操作系统之外的浏览器和操作系统上进行测试。例如,你可能使用 macOS 但想要在 Windows 的 Chrome 上测试。在这种情况下,最便宜的解决方案是使用虚拟机。我推荐使用 VirtualBox,因为它是免费的,易于使用,并且在 macOS 和 Windows 上都可以工作。 + +此外,Microsoft 提供免费的 Windows 副本,用于在 Edge 和 Internet Explorer 11 浏览器中测试。 + +## 在线服务 + +类似于虚拟机,一些在线服务使你能够在数百种类型的设备上进行测试。但是,它们不是免费的,并且依赖于快速的互联网连接。 + +## 移动设备 + +在真实移动设备上测试无法与使用浏览器 DevTools 测试相比。原因是无法在 DevTools 中模拟真实设备。触摸屏本身在测试网站中起着巨大作用。 + +我建议,如果可行的话,购买两部 Android 手机作为测试设备。每台设备投资不超过 150 美元。另一个解决方案是使用你家人的手机。我总是借用我妈妈的手机在 Android 上仔细检查。 + +**提示**:如果你的 Web 项目在 `localhost` 上运行,你可以在连接到同一 Wi-Fi 网络的任何移动设备上打开其链接。例如,如果项目在 `localhost:3000` 上运行,以下是 macOS 用户如何获取完整 IP 地址的方法: + +1. 转到"System Preferences",然后转到"Network"。 +2. 在"Connected"部分,记下 IP 地址(我的是 `192.168.1.252`)。 +3. 在你的移动设备上,输入带有端口号的地址(我的是 `192.168.1.252:3000`)。 + +然后,你可以在移动设备上访问项目。从那里,你可以更新和编辑 CSS 来测试它。 + +## 移动浏览器 + +移动设备上的浏览器与我们在桌面上使用的浏览器不同。它们更简单、更轻量。在 iOS 上,默认浏览器是 Safari。在 Android 上,这取决于手机制造商。例如,三星手机有一个名为 Samsung Internet 的预装浏览器。 + +## 检查你的移动浏览器 + +通过 USB 或 USB-C 将手机连接到计算机,你可以检查代码。对于 iOS,我们可以连接 iPhone,然后在计算机上使用 Safari 检查它。这使得检查和测试更快。对于 Android 设备,过程更复杂。请参考 Chrome DevTools 博客的[这个很棒的资源](https://developers.google.com/web/tools/chrome-devtools/remote-debugging)。 + +## 移动模拟器 + +macOS 开发人员可以访问 iOS 模拟器,在那里他们可以在多个 iOS 设备尺寸(iPhone、iPad)上进行测试。此外,可以为你测试的每个设备打开 DevTools。 + +## 浏览器支持 + +在开始新的前端项目时,决定你想要支持的浏览器。例如,你会支持 Internet Explorer 11 吗?或者旧版本的 Firefox?提前回答这些问题,以便为即将到来的事情做好准备。 + +在开发过程中,你可能会意外使用在你想要支持的浏览器中不受支持的 CSS 功能。因为你是根据渐进增强进行设计的,你需要检查 CSS 功能是否受支持,如果是,那么你会将该功能作为增强应用。 + +诸如 [doiuse](https://github.com/anandthakker/doiuse) 之类的工具可以通过 npm 安装在你的项目中。告诉它要支持的最低浏览器。下面的示例命令将在命令行中运行: + +```bash +doiuse --browsers "ie >= 9, > 1%, last 2 versions" main.css +``` + +输出将列出 CSS 功能以及警告——例如,属性 X 仅在 Internet Explorer 11 及以上版本中受支持。 + +## Can I Use + +Can I Use 是一个非常有用的工具,用于搜索特定的 CSS 功能。它会告诉你功能支持的历史。有时,属性的支持表会在修复问题时为你节省数小时的试错时间。 + +## 供应商前缀 + +浏览器供应商为仍未最终确定的实验性 Web 功能添加前缀。理论上,开发人员不应该在生产网站上使用这些属性,直到它们得到 100% 支持;然后,他们可以使用无前缀版本。但是,许多开发人员没有足够的耐心等待数年才能完全支持属性。 + +根据 MDN: + +> 浏览器供应商正在努力停止对实验性功能使用供应商前缀。Web 开发人员一直在生产网站上使用它们,尽管它们具有实验性质。这使得浏览器供应商更难确保兼容性和处理新功能;对于较小的浏览器来说,这也是有害的,它们最终被迫添加其他浏览器的前缀以加载流行的网站。 + +这意味着你不会看到未来 CSS 功能的任何供应商前缀。这很好;它将使新功能的发布速度更快。 + +MDN 补充说: + +> 最近,趋势是在用户控制的标志或偏好设置后面添加实验性功能,并创建可以更快达到稳定状态的较小规范。 + +MDN 列出了所有主要浏览器的前缀: + +- `-webkit-`:Chrome;Safari;Opera 的最新版本;几乎所有 iOS 浏览器,包括 iOS 的 Firefox;基本上,任何基于 WebKit 的浏览器 +- `-moz-`:Firefox +- `-o-`:Opera 的旧预 WebKit 版本 +- `-ms-`:Internet Explorer 和 Microsoft Edge + +以下是供应商前缀的示例用法: + +```css +-webkit-transition: all 4s ease; +-moz-transition: all 4s ease; +-ms-transition: all 4s ease; +-o-transition: all 4s ease; +transition: all 4s ease; +``` + +在开发网站时手动添加这些前缀是不实用的。名为 Autoprefixer 的工具会自动为你完成。指定要支持的浏览器,它会完成其余工作。 + +## 总结 + +在本章中,我们涵盖了调试环境(如 DevTools)、虚拟机、移动设备和在线服务。掌握 DevTools 的每个细节将大大减少你在解决和调试 CSS 上花费的时间。 + +接下来,我们将探索一些常见的 CSS 问题并学习如何解决它们。准备好了吗? diff --git a/content/zh/inconsistency_implementation.mdx b/content/zh/inconsistency_implementation.mdx index f200618..f30cade 100644 --- a/content/zh/inconsistency_implementation.mdx +++ b/content/zh/inconsistency_implementation.mdx @@ -1,5 +1,299 @@ -# 浏览器不一致和实现错误 +# 浏览器不一致和实现缺陷 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +我们都知道网络浏览器存在不一致性,这很正常。作为网络开发者,我们必须在项目的第一天就修复这些问题,这样我们就可以从一个干净和可靠的代码基础开始。在本章中,我将介绍最常见的 CSS 重置方案,以及如何制作简化测试用例和进行回归测试。 - +## 使用 CSS 重置文件 + +CSS 重置文件是网络开发的重要组成部分。最常见的两个是 Eric Meyer 的 Reset CSS 和 Nicolas Gallagher 的 Normalize.css。 + +通过使用 CSS 重置文件,你将节省大量时间来修复和调试已经修复的问题。以下是来自 Normalize.css 的示例: + +```css +/** + * 1. Correct the line height in all browsers. +*/ +html { + line-height: 1.55; /* 1 */ +} +``` + +在所有浏览器中拥有一致的 `line-height` 很重要。它将节省你弄清楚为什么 `line-height` 在浏览器中不一致工作的时间。 + +另一个例子是使诸如 `b` 和 `strong` 之类的元素具有粗体字重。这在所有浏览器中不一致工作,所以添加它将防止意外行为。 + +```css +/** + * Add the correct font-weight in Chrome, Edge, and Safari. +*/ +b, +strong { + font-weight: bold; +} +``` + +这些只是几个例子。记住包含 CSS 重置文件。如果你不想,那么至少制作你自己的重置文件。有些人会争论重置文件并不总是需要的,因为它会增加 CSS 文件的总大小。我同意。但你可以轻松创建自己的小文件并完成它。 + +## 使用 Normalize.css + +与其他一些重置相比,Normalize.css 只修复浏览器一致性问题,而不重置所有内容。它将保持网络浏览器之间的常见 CSS 样式。例如,诸如 `h1` 和 `h2` 之类的标题元素的边距将被保留。 + +这是来自 Normalize.css 的一个片段,重置 body 元素的边距: + +```css +body { + margin: 0; +} +``` + +根据你正在工作的项目的性质,你可以决定最好使用什么。 + +## 浏览器实现缺陷 + +网络浏览器是由人类制作的,人类会犯错误。发现浏览器不按预期支持特定功能或与其他浏览器不同地实现功能是完全正常的。在本节中,我们将介绍在浏览器实现中发现缺陷的步骤。 + +首先,什么是浏览器实现错误?它是由浏览器本身引起的错误。错误可能在一个或多个浏览器中,是由 CSS 规范的不当实现引起的。 + +### 验证缺陷 + +真的有缺陷,还是你弄错了?开始调试某些东西,只是后来意识到它不是缺陷,而是故意的功能,这是浪费时间的。 + +一些错误是在所有设备上的所有浏览器中出现的——这些很容易找到。其他错误出现在特定浏览器或设备中,如 Nexus 5 Android 手机——找到这些更困难,因为你需要实际的 Nexus 5 设备或在线模拟器,这通常不是免费的。 + +验证缺陷确实是缺陷将根据其复杂性而变化。一旦你确定它是一个缺陷,那么你将进入下一步。 + +### 决定正确的行为 + +一旦你验证了它是一个缺陷,你需要决定功能应该如何看起来和行为。例如,你可以决定特定元素的高度应该在 150 到 450 像素之间,如果它超过了,那么它将被认为是一个缺陷。 + +### 隔离缺陷 + +一旦你验证了缺陷并决定了正确的行为,尝试重现它。让我们学习如何通过测试用例简化来重现缺陷。 + +## 测试用例简化 + +网络开发者中最被低估的技能之一是创建简化的测试用例。当我们在构建的网页中遇到问题时,我们需要识别原因。问题可能跨浏览器或仅在移动浏览器上。通过使用页面的整个 HTML 和 CSS 来调试问题不是理想的。我们需要在测试用例中隔离问题,这样我们的时间更多地花在修复问题上,而不是识别问题上。 + +让我们学习如何制作简化的测试用例。 + +1. **禁用 JavaScript** + +如果你禁用 JavaScript 并且问题仍然存在,那么你会知道问题与 JavaScript 无关。这有助于快速排除与 JavaScript 相关的问题。此外,你可以在私人模式标签中打开项目,或停用所有浏览器扩展。如果问题消失,那么可能是因为浏览器扩展。 + +2. **识别问题** + +问题与 alignment 有关吗?还是 horizontal flow 有关?无论是什么,我们需要识别它。非常精确地表达问题有助于:"我将调试英雄部分的水平滚动。" + +3. **隔离 HTML** + +打开浏览器的 DevTools 并梳理页面的 `head` 元素,消除任何不需要的样式和脚本文件。如果某些东西看起来不相关,删除它并继续。一旦 `head` 被清理,移动到 `body` 元素并删除所有与问题无关的 HTML 元素。 + +4. **隔离 CSS** + +现在我们确信 HTML 只包含我们想要调试的代码位,让我们隔离该 HTML 所需的 CSS。删除任何装饰性样式,如 `color` 和 `background-color`。保持 CSS 尽可能最小,只留下导致问题的 CSS 规则。 + +5. **注释代码** + +如果你怀疑特定的 HTML 元素或 CSS 规则导致问题,添加注释解释,这样你在调试时不会忘记。当你需要从其他人那里获得帮助或作为提醒,如果你在一段时间后回到问题时,注释非常有用。 + +6. **使用简化的文件** + +一旦我们完成隔离问题,让我们将剩余的 HTML 和 CSS 复制到新的本地文件,然后我们就准备好了。我们现在有了问题的简化测试用例。更好的是,我们可以将 HTML 和 CSS 添加到 [CodePen](https://codepen.io/),这将使我们自己测试或从其他人那里获得帮助变得容易。 + +### 简化测试用例的示例 + +为了使事情更清楚,让我们通过一个 CSS 缺陷的真实示例,看看如何将其转换为简化的测试用例。 + +在下图中,我们有一个具有水平滚动问题的网页。我们已经努力尝试,无法弄清楚如何解决问题。所以,让我们尽可能地隔离问题。 + +![image-20250325214220882](/img/image-20250325214220882.png) + +首先,我们将尝试以下操作: + +1. 从页面的 `head` 元素中删除对演示不重要的任何额外样式和脚本。 + +2. 检查 `body` 元素,逐个删除部分。如果删除部分导致问题消失,那么保留它。 + +3. 一旦我们决定了应该保留的 HTML,下一步是 CSS。我们只需要使演示工作所需的 CSS,如 `width`、`height` 和 `display`。如果装饰性属性如 `background`、`border`、`color` 和 `box-shadow` 不影响问题,可以删除它们。 + +按照上述步骤,我们应该有**最少**的 HTML 和 CSS 来使测试用例工作。重复上述步骤,确保代码是干净的。这是我们隔离后简化测试用例的外观: + +![image-20250325214259696](/img/image-20250325214259696.png) + +看起来问题在 grid 部分。现在,问题是,是否可能进一步简化这个?这是我们可以做的: + +- 删除网格中的第二行。 +- 清理卡片的圆角和阴影。 +- 从每张卡片中删除标题。 + +![image-20250325214316550](/img/image-20250325214316550.png) + +一旦清理完毕,我们可以添加一些注释来帮助测试。 + +```css +/* Not sure if 100vw is causing the horizontal scrolling. */ +.section { + width: 100vw; + padding: 1rem; +} +``` + +这个注释对我们或任何其他尝试修复错误的人都有帮助。这是我们可以做的最多的来简化测试用例。下一步是提取 HTML 和 CSS,并将它们上传到我们的网站或我们想要的任何地方。 + +## 使其失败 + +在他的调试书中,Dave Agans 将"使其失败"确定为调试任何编码问题的主要步骤之一。我们能够在 CSS 中遵循这个建议的程度将取决于我们正在处理的错误类型。一些错误很明显——例如,在所有视口大小中出现的错误。其他错误类型更复杂,在这种情况下使它们失败更困难。 + +正如 Agans 在他的书中提到的,使其失败的原因是这样我们可以: + +- 看到错误, +- 专注于原因, +- 告诉我们是否已经修复了它。 + +他的一句话说明了我们经常遇到的误解: + +> 吐司只有在你把面包放在烤面包机里时才会烧焦;因此问题出在面包上。 + +有时,我们误解了 CSS 问题的原因。吐司只有在我们把它放在烤面包机里很长时间时才会烧焦。如果它烧焦了,那是我们的错,不是烤面包机的错。这同样适用于 CSS 开发。如果我们使用布局模块来做它不是为之设计的事情,那么错误是我们的。 + +你可能想知道,"如果我已经尝试了我知道的一切,仍然无法重现问题怎么办?"好吧,提醒自己每个失败都有原因。没有找到它的秘密配方;它隐藏在随机性的某个地方。 + +## 备份你的工作 + +在测试之前,将你的工作保存在新的 Git 分支中。如果你不使用版本控制,将你的工作复制到备份中,并从那里开始测试。通过做其中之一,迭代和更改事物将更安全,丢失工作的机会将非常低。 + +## 记录一切 + +对于复杂的 CSS 错误,我喜欢写下以下内容: + +1. 我做了什么, +2. 我采取的步骤顺序, +3. 采取步骤的结果发生了什么。 + +记录这些步骤对你和你的团队都有帮助。 + +以下是记录重现 CSS 错误步骤的示例。 + +1. 在 iOS 12 上的 Safari 中打开网站。 +2. 点击移动菜单切换。 +3. 点击关闭按钮。它不工作。 +4. 再次点击关闭按钮。它工作。 + +在执行上述步骤时,页面应该是空白的,除了存在问题的标题。 + +## 测试和迭代 + +一旦我们有了简化的测试用例,我们可以开始在相关浏览器或设备中测试错误。我们会继续迭代和编辑,直到我们注意到差异。 + +在迭代时,**一次更改一件事**非常重要。不要随机更改 CSS 并希望它会工作。如果它工作,那么你不会知道你是如何做到的,猜测工作将开始。更改一件事,测试,重复。 + +这种方法的有用性在于,当错误被修复时,我们可以**比较**我们所做的更改以使其工作。如果我们一次更改一百万件事来使其工作,这不会发生。 + +## 研究问题 + +如果你已经努力尝试修复问题但无法做到,那么互联网是你的朋友。在线搜索问题或模式,看看其他人是否面临同样的问题。你不是第一个的机会很高。 + +## 向浏览器供应商报告 + +如果你相信你发现的错误是独特的,没有人遇到过,那么是时候向浏览器供应商报告了。每个浏览器供应商都有一个公共论坛,包含用户提交的所有错误。如果你已经记录了重现问题的步骤,如前面建议的,那么你只需要发布步骤和你的简化测试用例文件。此外,如果有帮助,提交截图或视频。 + +这是在浏览器实现中提交错误的地方: + +- Firefox: [bugzilla.mozilla.org](https://bugzilla.mozilla.org) +- Safari: [bugs.webkit.org](https://bugs.webkit.org) +- Chrome: [bugs.chromium.org](https://bugs.chromium.org) + +## 永远不要丢弃调试演示 + +在制作简化测试用例时,你可能会创建它的多个副本,每个都有轻微的更改。不要删除它们。存档它们,因为它们在将来可能有帮助。你可以用它们做几件事: + +- 写一篇关于错误和你的测试用例的博客文章,解释你如何修复它。 +- 将它们作为你自己的日志保留,当你面临类似错误时。 +- 与想要学习你如何修复它的同事或团队成员分享它们。 + +## 回归测试 + +如 [Wikipedia 上解释的](https://en.wikipedia.org/wiki/Regression_testing): + +> 回归测试是重新运行功能和非功能测试,以确保先前开发和测试的软件在更改后仍然执行。如果不是,那将被称为回归。 + +当你修复错误时,你可能会意外地破坏另一件事而不知道。这被称为回归。测试回归可能很耗时,因为错误可能在特定环境、视口大小或滚动位置中发生。使用工具,我们可以通过定义我们的设计组件来进行回归测试。 + +在本节中,我们将学习如何使用 [BackstopJS](https://github.com/garris/BackstopJS) 进行回归测试。根据[官方 GitHub 存储库](https://github.com/garris/BackstopJS): + +> BackstopJS 通过比较随时间的 DOM 截图来自动化你的响应式网络 UI 的视觉回归测试。 + +BackstopJS 使用无头 Chromium 浏览器,与 Google Chrome 使用的相同。它的工作原理如下: + +1. 为我们想要测试的页面分配 URL 路径。 +2. 添加我们想要观察的选择器。 +3. 为它们生成参考截图。 +4. 运行 `backstop test` 来测试我们的更改与参考截图。 + +让我们学习如何使用 BackstopJS。首先,全局安装它: + +```bash +$ npm install -g backstopjs +``` + +在我们项目的目录内,我们需要初始化一个 BackstopJS 项目。 + +```bash +$ backstop init +``` + +### BackstopJS 配置文件 + +当我们初始化项目时,将在项目的根目录中创建一个 `backstop.json` 文件。你会在那里找到可以配置的所有内容。对于我们的简单课程,需要以下内容: + +- `id` +- `viewport` +- `scenarios` + +在 `scenarios` 数组中,有一个 `selectors` 数组,我们可以在其中添加所有要观察的 CSS 选择器。我添加了以下内容: + +```json +"viewports": [ + { + "label": "phone", + "width": 320, + "height": 480 + }, + { + "label": "tablet", + "width": 1024, + "height": 768 + } +], +"scenarios": [ + { + "url": "http://localhost:8080/", + "selectors": [ + ".c-header--full" + ] + } +] +``` + +在这个配置中,我们添加了两个要观察的元素,页面的 URL 是 `index.html`。下一步是生成**参考**截图。 + +```bash +$ backstop reference +``` + +通过为我们分配的元素生成参考,我们可以在 CSS 中进行更改。如果与参考有什么不同,BackstopJS 将抛出错误,并提供详细的 UI。 + +要使用该工具,让我们增加标题中的垂直内边距,然后重新运行测试。 + +```bash +$ backstop test +``` + +![image-20250325214826708](/img/image-20250325214826708.png) + +测试失败,因为参考截图与测试的截图不同。注意第三个截图如何有粉红色——这是差异的突出显示。 + +在真实项目中,我们可能会修复问题,在过程中无意中创建回归。我们无法在大型项目中手动测试所有内容,所以拥有这样的工具使我们的生活更轻松,使我们更有生产力。 + +## 总结 + +在本章中,我们学习了 CSS 重置、简化测试用例和回归测试。我们几乎完成了!在下一章中,我们将探索一些调试 CSS 的一般技巧和窍门。 diff --git a/content/zh/index.mdx b/content/zh/index.mdx index 560464b..eae4474 100644 --- a/content/zh/index.mdx +++ b/content/zh/index.mdx @@ -1,17 +1,19 @@ # 前言 -作为一名在 CSS 标准制定前就开始参与并推广该技术的资深从业者,我见证了其近 25 年的发展历程。 +作为一名在 CSS 标准制定之前就开始使用并积极推广该技术的资深从业者,我见证了其近 25 年的发展历程。 -CSS 的引入为 Web 开发范式带来了根本性变革。尽管其规范的完善和普及经历了漫长的迭代过程,但它最终成功取代了传统的表格布局、字体元素等早期 Web 开发模式,为前端技术发展奠定了基础。 +CSS 的引入为 Web 开发带来了深刻的变革。虽然其规范的完善和普及经历了漫长的迭代过程,但它最终成功取代了传统的表格布局、字体元素等早期 Web 开发模式,为前端技术的发展奠定了坚实基础。 -CSS 的技术演进已远超出了规范制定者和早期实践者的预期,为开发者提供了前所未有的布局能力和样式控制能力。然而,这种强大的功能特性也伴随着相应的技术复杂性。 +CSS 的技术演进已经远远超出了其创始者和早期使用者的想象,为开发者提供了令人难以置信的强大能力。然而,这种强大功能和复杂性也带来了相应的代价。 -在深入研究 CSS 开发实践时,不禁让我联想到歌德的《魔法师的学徒》(后经迪士尼《幻想曲》改编)所传达的寓意 —— 强大的能力若缺乏系统性的掌握和规范化的应用,往往会带来意想不到的技术负债。 +在深入研究 CSS 开发实践时,我不禁联想到《魔法师的学徒》这个故事(原为德国浪漫主义诗人歌德的诗作,后因迪士尼《幻想曲》中米老鼠的演绎而广为人知)。魔法师的学徒获得了师傅的法力,但由于无法正确运用,最终造成了混乱。 -这一现象在 CSS 开发中尤为明显:层叠(Cascade)、特异性(Specificity)和继承(Inheritance)等核心机制虽然赋予了样式系统强大的表达能力,但同时也是导致常见工程问题的技术根源。 +这听起来很像我们在使用 CSS 时的经历! -正因如此,业界急需一部系统性解决 CSS 调试问题的技术著作。Ahmad 的这本专著恰逢其时,它深入剖析了这一关键议题。 +层叠(Cascade)、特异性(Specificity)和继承(Inheritance)都是 CSS 的强大特性,但同时也是导致我们经常遇到问题的根源。 -强烈推荐此书给每一位Web开发者,相信它将对行业产生深远影响。 +正因如此,我很惊讶竟然花了这么长时间才有人真正着手解决 CSS 调试这一重大挑战。这也是为什么我对 Ahmad 的这本新书感到兴奋,它详细地探讨了这一重要议题。 + +我强烈推荐这本书给每一位 Web 开发者,这确实是一本姗姗来迟的佳作! **John Allsopp — Web Directions** diff --git a/content/zh/introduction_to_css_bugs.mdx b/content/zh/introduction_to_css_bugs.mdx index 6f257dc..920913b 100644 --- a/content/zh/introduction_to_css_bugs.mdx +++ b/content/zh/introduction_to_css_bugs.mdx @@ -1,5 +1,277 @@ # CSS Bug 简介 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +## 什么是 CSS Bug? - +当某些东西与你的预期不同时,那就是一个 bug。例如,一个图标可能没有与其兄弟元素对齐,或者一张图片可能看起来很奇怪,因为它被拉伸了(其宽度和高度彼此不成比例)。在某些情况下,你认为的 bug 实际上可能是预期的行为。它可能是一个功能请求,或者有人故意这样做的。因此,值得检查并详细询问相关人员关于这个 bug 的情况。 + +### 浏览器是不同的 + +Web 浏览器是不同的,并非所有浏览器都支持 CSS 中的所有功能。在你的工作过程中,你可能会遇到在浏览器 X 中看起来像 bug 的东西,而在浏览器 Y 中,它工作得很完美。这并不意味着浏览器 X 渲染错误。有时,问题的出现是因为一个浏览器厂商根据规范实现了某个功能,而在另一个浏览器中工作是因为该厂商没有错误地实现它。 + +# 如何修复 CSS Bug + +让我们来了解当你发现 CSS bug 或团队中有人指出你工作过的页面上有问题时的基本步骤。 + +### 检查 CSS + +首先,检查正在使用的 CSS。你是否使用了一些只在现代浏览器中支持的前沿属性?或者是一些旧的、在显示 bug 的浏览器中应该能工作的东西? + +它在你构建时使用的浏览器中工作吗?或者也许是其他人构建了网站,你不知道它是否工作?那么,你需要检查问题是否在你的浏览器中可以重现。 + +### 检查浏览器支持 + +访问 [Can I Use](https://caniuse.com/) 并检查 CSS 属性的浏览器支持情况。如果你看到该属性在出现 bug 的浏览器中不受支持,或者该属性只有在使用厂商前缀时才受支持,那么这可能就是你的答案。确保添加了厂商前缀(如果有的话),并且你正在测试的浏览器支持该 CSS 属性。 + +### 使用浏览器的开发者工具 + +一旦你确定 CSS 属性可以在显示 bug 的浏览器中正常工作,那么就是时候深入研究浏览器的开发者工具(DevTools)了。在检查元素之前,你需要确定这是什么类型的问题。例如: + +- 这是一个视觉问题,比如图标未对齐吗? +- 它是发生在特定部分还是跨页面发生?(问题可能与 CSS 布局有关。) + +在下一节中,我们将深入了解我们发现的 CSS 问题类型的详细信息,以及如何使用浏览器的 DevTools 来调试它们。 + +## CSS Bug 类型 + +按类型对 bug 进行分类是有帮助的。例如,问题是与设计相关还是与语法错误相关?在本章中,我们将逐一介绍每种类型,并提供基本示例。 + +### 视觉设计 Bug 类型 + +当你在 HTML 和 CSS 中实现设计时,设计和代码之间任何明显的不一致都可以被认为是 bug。例如,你是否注意到图标与其文本标签未对齐,或者页面容器比设计中提议的更宽或更窄?所有这些都可以被认为是开发者无意中造成的视觉设计问题。 + +设计师可能不了解 CSS,在这种情况下,他们可能会截取问题的屏幕截图,并将其发送回开发者并附上注释。如果开发者有设计背景,那么他们可能能够轻易注意到设计师报告的那些不一致之处。 + +考虑以下图片: + +![image-20250325001053823](/img/image-20250325001053823.png) + +在上面显示的导航设计中,第一个是原始设计,第二个是代码实现。开发者努力实现它,但由于几个原因,它仍然与原始设计相差甚远: + +- 高度更短。 +- 字体大小更小。 +- 边框半径更小。 +- 边框颜色不同。 +- 阴影太浅。 + +我们已经发现了实现与设计不相似的五种方式。在更大的规模上,许多组件和部分需要精心制作,以使实现看起来与设计相似。并非所有开发者都注意到这些设计细节。 + +此外,视觉设计问题包括任何在不是实际 bug 的情况下对用户造成障碍的东西。一些例子是不可访问的颜色、令人困惑的内容组织、未对齐的按钮、使布局看起来奇怪的文本,以及网站页面之间的不一致行为。所有这些都会导致视觉设计问题,进而导致可访问性问题。 + +### 技术 Bug 类型 + +并非所有问题都只是通过查看网页就能注意到。有时你处理的是语法错误或 CSS 属性的不正确值。让我们探索技术问题的原因。 + +#### 调用错误的文件路径 + +许多开发者花费数小时试图弄清楚为什么某些 CSS 根本不工作,结果发现原因是 CSS 文件的路径不正确。这可能发生是因为你使用了 CSS 预处理器,如 Sass 或 LESS,它会渲染一个 `.css` 文件。有时,渲染文件的名称与源文件的名称不同。始终确保链接的 CSS 文件是正确的,特别是如果你有多个 CSS 文件。 + +#### 属性名称拼写错误 + +当你在 CSS 属性名称中打错字时,浏览器不会直接告诉你。CSS 在出现错误时不会抛出错误。你需要通过使用浏览器的 DevTools 来弄清楚。如果你检查元素,浏览器会显示无效属性,并带有警告三角形和名称上的删除线。 + +我记得为一篇文章制作一个简单的演示时,我绞尽脑汁试图弄清楚为什么某些东西不工作?结果发现我在声明 opacity 属性时有一个拼写错误。 + +```css +.element { + opacity: 0.5; +} +``` + +我没有注意到这个微不足道的错误的原因是我太分心了,没有静下心来思考 bug 的原因。大多数代码编辑器会在属性名称拼写错误时警告你。以下是来自 Visual Studio Code 的示例: + +![image-20250324222007023](/img/image-20250324222007023.png) + +#### 为属性使用无效值 + +与上一个问题类似,当你给 CSS 属性一个无效值时会发生这种情况。该值可能是拼写错误或不适用于给定属性的值。考虑以下示例: + +```css +.element { + opacity: 50; +} +``` + +`opacity` 属性接受从 0 到 1 的值。这里的作者想要 50% 的不透明度,但将其表示为百分比,同时忘记了百分号。浏览器会忽略这个 `opacity` 属性。 + +#### 使用依赖于另一个属性的属性 + +并非所有属性都能独立工作。一个属性可能依赖于应用于元素本身或父元素或子元素的某个规则。考虑这个: + +```css +.element { + z-index: 1; +} +``` + +`z-index` 属性不会工作,因为它需要一个除 `static` 之外的 `position` 值。浏览器不会将此标记为无效,你需要猜测为什么它不工作。 + +让我们考虑何时必须将规则应用于父元素或子元素: + +```css +.child { + position: absolute; +} +``` + +我们希望子元素相对于其父元素绝对定位。但是,父元素没有 `position: relative`。这将导致子元素根据最近的相对定位的父元素或 body 元素进行定位。 + +#### 用一个属性覆盖另一个属性 + +有时没有拼写错误或错误,但你用一个属性覆盖了另一个属性。这就是 CSS 的工作方式,但一些开发者可能认为发生了 bug。例如,CSS 的最小和最大尺寸属性可能令人困惑。 + +```css +.element { + width: 100px; + min-width: 50%; + max-width: 100%; +} +``` + +这里,元素的宽度将是其父元素的 `50%`。如果你没有仔细阅读 CSS 规范,你可能认为这是一个问题,但它不是。 + +#### 重复属性 + +有时你会声明一个属性,由于某种原因它对元素没有影响。你不断尝试和测试,但没有结果。最终,你意识到属性是重复的,你正在编辑它的第一个声明,而它被第二个声明覆盖了。 + +```css +.element { + display: block; + width: 50%; + opacity: 1; + border: 1px solid #ccc; + opacity: 0; +} +``` + +这里 `opacity` 属性被定义了两次。这是一个错误,可能由于各种原因发生: + +- 也许你复制了一些样式来快速测试它们,然后忘记删除重复的。 +- 这可能只是意味着你累了,需要休息一下。 + +无论原因是什么,这都是一个 bug。 + +#### 类名拼写错误 + +你的 CSS 可能 100% 正确和有效,但类名中的一个拼写错误可能导致样式不被应用到元素上。尽管这很简单,但当我们每天工作八小时时,我们倾向于专注于大问题,可能会忽略这样的小错误。 + +#### 忽略层叠 + +CSS 代表层叠样式表。正如名称所示,网站的样式是层叠的,它们的顺序很重要。如果你为一个元素定义了一个 CSS 规则,然后在 CSS 文件的末尾重新定义它,后者将覆盖前者。 + +```css +.element { + color: #000; +} +/* 500 行之后… */ +.element { + color: #222; +} +``` + +这是一个可能发生的非常简单的例子。你可能面临比这更棘手的问题。考虑一个应该在移动设备和桌面设备上切换颜色的元素: + +```css +@media (min-width: 500px) { + .element { + background: #ccc; + } +} + +.element { + background: #000; +} +``` + +`.element` 的背景颜色将是 `#000`,因为它在媒体查询规则之后(因此覆盖了它)。 + +#### 忘记清除缓存 + +CSS 缓存发生在服务器上,而不是在本地机器上。一个常见问题是推送更新,当你刷新网页时,CSS 更新没有出现。在这种情况下,CSS 文件可能被缓存了,你需要清除浏览器的缓存或在每次推送后重命名文件。 + +这个问题有多种解决方案,最简单的是添加查询字符串: + +```html + +``` + +当你进行更改时,你也会更改版本: + +```html + +``` + +然后,浏览器会下载最新的 CSS 文件。有关更多信息,CSS-Tricks 有[一篇很好的文章](https://css-tricks.com/strategies-for-cache-busting-css/)。 + +#### 忽略性能 + +为工作使用错误的属性很容易损害性能。例如,当将元素从左到右动画时,`left` 属性是性能杀手,因为它强制浏览器在每个像素移动时重新绘制布局。 + +```css +.element:hover { + left: 100px; +} +``` + +更好的解决方案是使用 CSS `transform` 属性。它不会影响性能,一切都会顺利运行。属性的简单选择可以显著提高性能! + +```css +.element:hover { + transform: translateX(100px); +} +``` + +#### 忽略特异性 + +如果 CSS 规则没有按预期工作,原因可能是它的特异性比另一个更高。 + +```css +.title { + color: #222; +} + +.card .title { + color: #000; +} +``` + +`.card .title` 的特异性比 `.title` 更高。结果,前者会被覆盖。为了解决这个问题,我们可以向元素添加一个变体类,并将新颜色应用于该类。 + +```css +.card-title { + color: #000; +} +``` + +另一种可能性是使用 `!important`。一般来说避免使用这个,因为它使大规模维护 CSS 变得更加困难。明智地使用它,只在需要时使用。 + +## 调试过程 + +正如我们所见,CSS 问题有很多类别。有些是视觉的,有些是非视觉的。现在我们已经完成了常见类型的列举,下一步是弄清楚如何使用我们可用的各种工具和技术进行调试。 + +## 从非技术人员那里获取浏览器信息 + +假设用户报告了你网站上的问题。作为前端开发者,你检查了自己的浏览器,一切都正常。所以,问题只出现在用户的浏览器中。在这种情况下,向非技术人员询问更多详细信息的最佳方式是什么?以下是你通常会采取的步骤: + +1. 询问浏览器的名称。 +2. 询问浏览器版本,并解释用户如何获取它(例如,点击设置图标,然后点击 `关于`,并复制底部的数字)。 +3. 询问完整页面的屏幕截图。如果用户不知道如何做到这一点,向他们推荐一个易于使用的浏览器扩展。 + +获取浏览器信息的一个很好的工具是 Andy Bell 的 [mybrowser.fyi](https://mybrowser.fyi/)。很棒的部分是用户可以分享他们浏览器信息的自动生成链接。以下图片显示了视觉结果: + +![image-20250324222310444](/img/image-20250324222310444.png) + +一旦你有了浏览器的名称和版本并获得了问题的视觉效果,你就可以开始调试。如果你没有需要调试的浏览器,那么你可以在你的机器上安装它或使用在线服务,如 BrowserStack。 + +### 调试技术 + +当涉及到测试网页以调试 CSS 时,我们可以使用很多技术和工具,最常见的是: + +- 浏览器的 DevTools; +- 移动设备; +- 移动模拟器(如 iOS 模拟器); +- 虚拟机(如 VirtualBox); +- 在线服务(如 BrowserStack 和 CrossBrowserTesting)。 + +## 总结 + +在本章中,我们定义了什么是 bug,介绍了不同类型的 CSS bug,并总结了调试过程。在下一章中,我们将深入研究浏览器的 DevTools,并学习如何在修复 CSS 问题时利用它们。 diff --git a/content/zh/overview.mdx b/content/zh/overview.mdx index e08c6ab..a648cd0 100644 --- a/content/zh/overview.mdx +++ b/content/zh/overview.mdx @@ -1,30 +1,39 @@ # 引言与概述 -说实话,我们并没有一套直接又清晰的方法来处理 CSS 问题,因此 CSS 调试并不是一件轻松的事。 +说实话,CSS 调试并不是一件轻松的事,因为我们并没有一套直接又清晰的方法来处理 CSS 问题。 -与 Java、C 以及 PHP 等传统编程语言相比,这些语言已经建立了成熟且经过广泛验证的调试工具和技术体系;然而,CSS 领域的调试手段发展相对滞后。一个关键区别在于,使用 Java 等静态类型语言时,开发者可以在编译阶段获得明确的错误信息反馈,而 CSS 作为样式表语言,更常见的情况是遭遇“静默失效”,即没有显式的错误提示,这无疑增加了修复问题的难度。 +与传统编程语言(如 Java、C 和 PHP)相比,这些语言的调试技术已经发展多年并趋于成熟。但 CSS 的情况却不同。调试 CSS 与调试编程语言不同,因为你在编译时不会收到错误提醒。相反,你会遇到静默错误,这对解决问题毫无帮助。 -在调试 CSS 错误之前,你首先需要找到它。有时,你可能会从同事那里接到需要修复的 bug 报告。由于没有直接的方法可以遵循,找到一个 CSS bug 可能很困难。即便对经验丰富的 CSS 开发者来说,调试和发现 CSS 问题也可能是一件棘手的事情。 +在调试 CSS 错误之前,你首先需要发现它。有时,你可能会收到同事的报告,说有一个 bug 需要修复。由于没有直接的方法可以遵循,找到 CSS bug 可能很困难。即使对经验丰富的 CSS 开发者来说,调试和发现 CSS 问题也可能是一件棘手且令人困惑的事情。 + +本章将讨论: + +- CSS 调试的发展历史 +- 当今的变化 +- CSS 调试的含义 +- 调试思维 +- 为什么调试需要时间 +- 本书主题概览 ## CSS 调试的发展历史 -鉴于本书专注于调试和查找 CSS 问题,因此了解 CSS 调试工具的发展历史对于掌握现代调试技术具有重要意义。 +由于本书专注于调试和查找 CSS 问题,你应该了解一些 CSS 调试工具多年来是如何发展的。 ### Style Master -你可能会惊讶地发现,CSS 调试工具的第一个版本竟然是在 1998 年发布的 —— 距今已有 22 年之久(本书写作于 2020 年)!它的创造者是 John Allsopp 和 Maxine Sherrin,他们将其命名为 Style Master。正如他们描述的那样: +你可能会惊讶地发现,第一个 CSS 调试工具是在 1998 年发布的 —— 距今已有 22 年之久(本书写作于 2020 年)!它的创造者是 John Allsopp 和 Maxine Sherrin,他们将其命名为 Style Master。正如他们描述的那样: -> Style Master 是领先的跨平台 CSS 开发工具。它不仅仅是一个文本编辑器,Style Master 支持你的工作流程,包括:基于 HTML 创建样式表;实时编辑 PHP、ASP.NET、Ruby 等动态生成的网站的 CSS;通过 FTP 编辑 CSS;以及更多功能。 +> Style Master 是领先的跨平台 CSS 开发工具。它不仅仅是一个文本编辑器,Style Master 支持你的工作流程,包括:基于 HTML 创建样式表;实时编辑 PHP、ASP.NET、Ruby 等动态生成网站的 CSS;通过 FTP 编辑 CSS;以及更多功能。 ![style master](/img/1-1.png) -Style Master 的目标是优化 CSS 相关的开发体验,以提升工作效率、增强生产力并改善开发体验。正如你所看到的,CSS 调试多年来一直是开发人员关注的话题。 +Style Master 的目标是让 CSS 工作更加高效、更有生产力且更愉快。正如你所看到的,CSS 调试多年来一直是开发者关注的话题。 ### Firebug 浏览器扩展 2006 年,Joe Hewitt 发布了 [Firebug](https://getfirebug.com/) 的第一个版本。 -> Firebug 是一个已经停止维护的免费开源 Mozilla Firefox 浏览器扩展,它提供了对网站的 CSS、HTML、DOM、XHR 和 JavaScript 的实时调试、编辑和监控功能。 —— [维基百科]() +> Firebug 是一个已停止维护的免费开源 Mozilla Firefox 浏览器扩展,它提供了对网站 CSS、HTML、DOM、XHR 和 JavaScript 的实时调试、编辑和监控功能。 —— [维基百科]() ![firebug](/img/1-2.png) @@ -36,101 +45,101 @@ Firebug 的主要功能与今天的开发者工具(DevTools)非常相似, 如果没有这些优秀工具(Style Master 和 Firebug)创造者的努力,我们今天可能就没有现在使用的开发者工具。 -## 现代调试环境的变化 +## 当今的变化 -相比于 20 年前,现代的调试环境已经发生了巨大的变化。如今,每个浏览器都内置了开发者工具,使得开发者可以轻松地检查和编辑网页的 HTML、CSS 和 JavaScript。 +如今的场景已经发生了巨大变化。现在每个浏览器都内置了开发者工具,让开发者可以轻松检查和编辑网页的 HTML、CSS 和 JavaScript。在本书中,我们主要关注 CSS。 -值得注意的是,当 Style Master 和 Firebug 发布时,网站结构非常简单,我们只要考虑一种屏幕尺寸的测试。如今,一个网站可以被智能手表、移动电话、平板电脑、笔记本电脑和台式机等数百种设备访问。调试这些不同类型的设备绝非易事。你可能会在移动设备上修复了一个问题,但在无意中造成了桌面设备上的另一个问题。 +值得一提的是,当 Style Master 和 Firebug 发布时,网站结构非常简单,我们只需要测试一种屏幕尺寸。如今,一个网站可以被数百种设备访问,包括智能手表、手机、平板电脑、笔记本电脑和台式机。为所有这些类型的设备进行调试并非易事。你可能修复了移动端的问题,却无意中破坏了桌面端。 -这不仅仅是屏幕尺寸的问题。在过去 10 年中,Web 项目的规模也变得更加复杂。例如,像 Facebook 或 Twitter 这样的大型前端项目,其开发者需要一种系统化的方式来测试和调试。所有这些对 Web 开发工作的影响都清楚地表明,调试必须从项目第一天开始就被重视,并且开发者必须将它作为核心技能来学习。 +这不仅仅是屏幕尺寸的问题。在过去 10 年中,Web 项目的规模也变得更大。例如,像 Facebook 或 Twitter 这样的大型前端项目的开发者需要一种系统化的方式来测试和调试。所有这些对 Web 开发工作的变化都清楚地表明,调试必须从项目第一天开始就被重视,开发者必须将其作为核心技能来学习。 -## CSS 调试的定义 +## CSS 调试的含义 > 调试是查找和修复计算机程序、软件或系统中缺陷(阻碍正常运行的问题或缺陷)的过程。 —— [维基百科](https://en.wikipedia.org/wiki/Debugging#Debugging_process) -这个定义同样适用于 CSS 调试。查找和修复 CSS Bug 是一项重要的技能。无论你习惯使用哪种编程语言,CSS 调试的步骤几乎都是相同的。本章后续部分将介绍一个实用的调试策略框架。 +我们可以使用相同的定义。查找和修复 CSS bug 是一项重要技能。无论你习惯使用哪种编程语言,CSS 的调试步骤几乎都是相同的。本章后面部分,我们将介绍一个清晰的调试策略,你可以立即使用。 -本书提到的 "CSS bug" 特指由开发者导致的错误,而不是浏览器本身的错误。不过我们也会同时讨论这两种类型的错误。 +当我在本书中提到 "CSS bug" 时,我指的是由开发者造成的 bug,而不是浏览器本身的 bug。但我们也会讨论这两种类型。 -## 为什么应该学习 CSS 调试 +## 为什么应该教授调试 -随着浏览器开发者工具的快速迭代更新,紧跟最新的 CSS 调试技术和方法变得日益困难。此外,目前市场上缺乏专为初学者设计的、条理清晰且深入浅出的教学资源来指导有效的 CSS 调试实践。 +浏览器开发者工具的快速发展使得很难跟上所有 CSS 调试的技术和方法。更不用说缺乏一个组织良好的、便于初学者跟随的资源。 ## 调试思维 根据 Devon H. O'Dell 在 "[The Debugging Mindset](https://queue.acm.org/detail.cfm?id=3068754)" 中的研究: -> 软件开发人员花费 35-50% 的时间进行验证和调试软件。调试、测试和验证的成本估计占软件开发项目总预算的 50-75%,每年超过 1000 亿美元。 +> 软件开发人员花费 35-50% 的时间验证和调试软件。调试、测试和验证的成本估计占软件开发项目总预算的 50-75%,每年超过 1000 亿美元。 -快速修复问题的压力可能导致开发者在缺乏明确策略的情况下急于求成,反而增加时间成本与技术风险。 +如果你需要快速修复 bug,你可能会感到有点压力,这可能导致你在没有明确策略的情况下急于求成。这很容易导致混乱,在无关紧要的事情上浪费时间。 -从技术实现层面分析,不同的编程语言都存在逻辑性与非逻辑性错误。以 JavaScript 语言为例,当运行时出现异常,开发者能够通过浏览器控制台获取详细的错误堆栈追踪与诊断信息,这为问题定位提供了明确的技术线索。 +任何编程语言都有逻辑性和非逻辑性错误。以 JavaScript 为例。当你的 JavaScript 出现错误时,你可以通过检查浏览器控制台看到。至少你会得到错误发生的证据和原因。 -相较而言,CSS 调试具有独特的复杂性特征。层叠样式表语言不具备显式的错误报告机制,即便最基础的样式异常也可能演变为难以解决的技术障碍——尤其是在缺乏系统调试策略与结构化思维框架的情况下。这些样式异常可能是开发者对 CSS 属性规范的理解不够深入,或者是浏览器引擎对 CSS 规范的实现存在差异所导致。 +CSS 完全不同。当你犯错时,你不会收到任何类型的提醒。仅这一点就使得最简单的 CSS bug 很难修复,除非你思路清晰并遵循明智的策略。CSS bug 可能是由开发者造成的,比如 CSS 属性使用不当,也可能是由于浏览器之间的不一致性造成的。 -通常,你需要承担双重职责:既是开发者,又是测试者。这就要求技术团队必须具备从问题发现到根本原因分析的全流程解决能力,并建立涵盖问题诊断、根因定位及解决方案验证的系统化调试框架。 +此外,你可能需要负责测试网站并发现 bug。所以,我们不仅要处理修复 bug,还要发现它们。 ### 识别 CSS Bugs -在客户或团队成员发现网站问题之前,你可以主动尝试故意破坏设计,以发现潜在的 CSS 问题。在本书的第五章中,你将学习一些故意破坏 CSS 布局的方法。 +在客户或团队成员发现网站问题之前,你可以主动进行一些测试,尝试故意**破坏**设计。在第五章中,你将学习一些故意破坏 CSS 布局的方法。 -### 解释 CSS Bugs +### 向他人解释 Bug -你是否曾经花费数小时试图解决一个 CSS 问题,结果在向同事解释时,灵感迸发,突然就想到了解决方案?这就是向他人解释问题的魔力。当你陷入困境时,往往是因为你没有花足够的时间深入思考问题。 +你是否曾经花费数小时试图解决 CSS 问题,结果在向朋友或同事解释时,突然灵光一闪想到了解决方案?这就是向朋友解释 bug 的效果。你陷入困境是因为你没有花足够的时间深入思考问题。 -当你发现自己陷入思维的死胡同时,不妨稍作休息,然后再回来解决问题。修复技术问题需要高度集中。如果你在精神疲惫的状态下工作,很可能会在无意中引入新的问题。适当的休息可以有效避免这种情况。 +当你发现自己陷入这种循环时,休息一下,稍后再回来。修复问题需要高度集中。如果你在精神疲惫的状态下工作,你可能会无意中破坏其他东西。通过休息来避免这种情况。 -## 调试耗时的原因 +## 为什么调试需要时间 -在 Matt Lacey 的博客 "[You've Only Added Two Lines — Why Did That Take Two Days!](https://www.mrlacey.com/2020/07/youve-only-added-two-lines-why-did-that.html)" 中,Matt Lacey 提出了一些关于为什么调试耗时的观点。让我们逐一分析这些因素: +在 Matt Lacey 的优秀博客文章 "[You've Only Added Two Lines — Why Did That Take Two Days!](https://www.mrlacey.com/2020/07/youve-only-added-two-lines-why-did-that.html)" 中,Matt Lacey 提出了一些关于为什么调试耗时的可靠理由。让我们逐一分析。 ### 问题不清晰 -不要指望客户或团队成员能详细描述问题。模糊的描述是可以接受的。在向客户或团队成员询问更多细节之前,尝试尽可能全面地理解问题。一旦你有了充分的理解,接下来需要做的是在本地环境中复现问题。一旦你能够复现问题,你就可以开始调试了。 +不要期望某人能详细报告问题。模糊的描述是可以的。在要求更多细节之前,尝试尽可能全面地理解问题。一旦你做到了这一点,你需要在你的机器上复现 bug,然后你就有了一个起点。 ### 症状比原因更容易处理 -在处理问题时,关键是要找出根本原因,而不仅仅是表面症状。正如 Lacey 所说: +在处理问题时,重要的是调查问题的原因,而不仅仅是症状。正如 Lacey 所说: -> 如果某些代码抛出错误,你可以简单地用 try..catch 语句包裹它,并抑制错误。没有错误,没有问题。对吗?抱歉,对我来说,让问题隐形和修复它不是一回事。 +> 如果某些代码抛出错误,你可以简单地用 try..catch 语句包裹它并抑制错误。没有错误,没有问题。对吗?抱歉,对我来说,让问题隐形和修复它不是一回事。 -使用 "快速修复" 来隐藏问题可能会引入意想不到的副作用。你应把握机会从根本上解决问题,而不是制造更多问题! +用"快速修复"让 bug 隐形可能会引入一些意想不到的副作用。你有机会修复问题,而不是制造更多问题! -### 专注于单一路径 +### 专注于问题的单一路径 -有些问题可以通过多种方式复现,而不仅仅是报告的方式。找到这些方法不仅有助于彻底解决问题,还能深入理解 CSS 的特性和工作原理,以及修复项目中潜在的相似问题。这对于在问题影响用户体验之前主动修复问题具有重要意义。 +某些问题可以通过多种方式复现,而不仅仅是报告的方式。找到这些方法不仅有助于彻底解决问题,还能深入了解 CSS 的编写方式,以及代码库中是否还有其他地方可能出现相同问题。这对于在 bug 影响用户之前修复它们非常有帮助。 ### 忽略副作用 -修复问题是一回事,避免修复问题引入的副作用是另一回事。因此,最好用最少的 CSS 修复问题,并对其可能引入的副作用有充分的理解。 +修复问题是一回事;避免修复问题带来的副作用是另一回事。这就是为什么最好用最少的 CSS 修复问题,并对可能的副作用有充分的理解。 ## 编写易于调试的代码 -糟糕的代码组织方式会大大增加调试难度。对于大型 Web 项目,CSS 应该按组件和功能模块拆分成多个文件,然后通过 CSS 预处理器(如 Sass、LESS 或 PostCSS)进行编译。 +组织不当的代码会使调试变得更加困难。对于大型 Web 项目,CSS 应该按组件和功能模块拆分成多个文件,然后通过 CSS 预处理器(如 Sass、LESS 或 PostCSS)进行编译。 -如果决定将所有 CSS 集中写入单个文件,调试难度会大大增加。这会导致在文件中进行无效率的滚动查找,这不仅增加认知负担,也往往导致更多 bug 的产生。 +如果你决定将所有 CSS 写入单个文件,不要期望调试会很容易。你最终会在大文件中无休止地上下滚动。这种方法令人困惑且不理想。单文件 CSS 中往往会出现更多 bug。 -在下一章中,我们将深入探讨不同类型的 CSS 问题,详细分析浏览器中的调试技术和更多相关内容。 +在下一章中,我们将深入探讨不同类型的 CSS 问题,详细了解浏览器中的调试,以及更多内容。 ## 本书面向的读者 -本书适合希望提升 CSS bug 查找与修复技能的设计师、前端及后端开发者。你应该具备中级水平的 HTML 和 CSS 知识基础。 +本书适合希望提升 CSS bug 查找与修复技能的设计师、前端和后端开发者。你应该具备中级水平的 HTML 和 CSS 知识。 -在某些章节中,你需要按照指引安装 npm 包。请不用担心,这不会要求你具备深入的 Node.js 经验。你可以轻松地跟随操作指引完成相关步骤。 +在某些章节中,你需要按照步骤安装 npm 包。不用担心,这不需要深入的 Node.js 经验。你可以轻松地跟随操作。 ## 为什么我写这本书? -市场上缺乏系统化学习 CSS bug 查找与修复的资源是我撰写本书的主要动机。在开始研究这一主题时,我发现这一领域研究相对匮乏。技术社区需要一本以简明直接的方式全面讨论调试相关细节的专业指南。 +缺乏学习如何查找和修复 CSS bug 的资源是我写这本书的主要原因。当我开始研究这个主题时,我发现这个主题一直被忽视。应该有一本指南以简单直接的方式讨论所有与调试相关的细节。 ## 本书章节概览 以下是本书各章节的概览: - CSS bug 简介 -- 调试环境与工具:浏览器开发者工具、虚拟机、移动浏览器 -- 导致 bug 的常见 CSS 属性 +- 调试环境和工具:浏览器开发者工具、虚拟机、移动浏览器 +- 经常导致 bug 的 CSS 属性 - 故意破坏布局 -- 浏览器差异与实现缺陷 -- 通用技巧与窍门 +- 浏览器不一致性和实现 bug +- 通用技巧和窍门 -现在,让我们开始深入 CSS 调试的世界吧! +现在我们已经完成了本书的介绍,让我们开始调试 CSS 吧! diff --git a/content/zh/tips_tricks.mdx b/content/zh/tips_tricks.mdx index 2939d1f..20be306 100644 --- a/content/zh/tips_tricks.mdx +++ b/content/zh/tips_tricks.mdx @@ -1,5 +1,366 @@ # 通用技巧和诀窍 -import SupportTranslateTip from '@/components/SupportTranslateTip'; +## 调试多语言网站 - +当涉及调试多语言网站时,我们需要了解如何测试它以及事物如何工作。在谈论多语言网站时,我们将专注于从左到右(LTR)和从右到左(RTL)布局,分别使用英语和阿拉伯语的示例。 + +### LTR 和 RTL 的常见错误 + +### 间距问题 + +在调试 LTR 和 RTL 时,大多数问题将与间距相关。每种语言的水平方向将被翻转,间距问题通常归结为内边距或外边距。假设我们有以下内容: + +```css +.element { + margin-left: 10px; +} +``` + +对于 RTL,它将是这样的: + +```css +.element { + margin-right: 10px; +} +``` + +我们会对内边距和定位属性(`top`、`right`、`bottom`、`left`)做等效处理。 + +此外,我们可以使用 CSS 逻辑属性来避免为 RTL 编写更多 CSS。以下是上述示例的外观: + +```css +.element { + margin-inline-start: 10px; +} +``` + +属性 `margin-inline-start` 是逻辑的。这意味着,对于 LTR 它将是 `margin-left`,对于 RTL 它将是 `margin-right`。如果你有兴趣了解更多关于 RTL 样式的信息,我推荐你阅读我写的这个[指南](https://rtlstyling.com/)。 + +### 对齐问题 + +当文本在 LTR 中向右对齐时,它应该在 RTL 中翻转。 + +```css +.element { + text-align: right; +} +``` + +对于 RTL,它将是这样的: + +```css +.element { + text-align: left; +} +``` + +### 调试 RTL + +根据你正在构建的网站的工作方式,将给定页面的 CSS 从 LTR 切换到 RTL 可能很容易。如果 CSS 合并到一个文件中,切换将像在 `html` 元素上设置 `dir` 属性一样简单。 + +```html + +``` + +我们可以首先在 DevTools 中设置属性,然后检查我们想要修复的问题。 + +如果 LTR 和 RTL 的 CSS 不在一个文件中,那么它很可能在两个文件中,如 `main-ltr.css` 和 `main-rtl.css`。仅切换 `dir` 属性是不够的;我们还需要编辑 `head` 元素中样式表的 `src`。 + +### 快速添加 RTL 内容的方法 + +假设我们已经为 LTR 和 RTL 布局构建了 CSS,唯一缺少的是测试 RTL 内容的排版。在使用 LTR 内容以 RTL 模式查看设计时,你可以使用 Google 的页面内翻译来快速翻译所有内容。这将帮助你创建带有内容的 RTL 设计,并使其适合文本方向。 + +如果你感兴趣,我写了一个关于此的详尽指南,标题为 [RTL Styling 101](https://rtlstyling.com/)。 + +## 使用 `@supports` + +如果你不知道它,`@supports` 用于检测用户浏览器是否支持给定的 CSS 功能。 + +```css +@supports (display: flex) { + /* If flexbox is supported, apply this. */ + .element { + display: flex; + } +} +``` + +测试它的一个有趣方法是切换其功能。有浏览器扩展可以做到这一点,但我们可以通过添加随机字母来手动完成。当添加随机字母时,它将破坏规则;因此,CSS 不会工作。 + +```css +@supports (display: flexB) { + /* ... */ +} +``` + +我在 `display: flex` 后添加了字母"B"。浏览器不会识别它,你将获得默认行为,就像 `@supports` 被禁用一样。很酷,对吧? + +但是,在具有大量 `@supports` 规则的大型项目中,手动执行是不实际的。幸运的是,Ire Aderinokun 为此目的创建了一个[浏览器扩展](https://github.com/ireade/feature-queries-manager),它适用于 Chrome 和 Firefox。 + +![image-20250325215541817](/img/image-20250325215541817.png) + +该扩展将在你的浏览器 DevTools 中添加一个新标签。在左侧,你会看到嵌套在 `@supports` 查询中的 CSS 功能的可切换列表,在右侧将是使用特定功能的每个 `@supports` 查询的列表。上面显示的 CSS 是与网格相关的内容。切换左侧的复选框将禁用和启用 CSS 网格。这是测试和破坏布局的好方法。让我们更深入地了解破坏布局的方法。 + +## 浏览器扩展 + +### Grid Ruler + +测试两个 UI 元素是否正确对齐的好方法是使用标尺和参考线。这可以在 Sketch、Adobe XD、Photoshop 和 Illustrator 等设计应用程序中轻松完成。在浏览器中,没有扩展是不可能的。 + +一个很棒的扩展,[Grid Ruler](https://chromewebstore.google.com/detail/grid-ruler/joadogiaiabhmggdifljlpkclnpfncmj),仅在 Google Chrome 中可用。它使你能够水平或垂直拖放参考线。这对于验证两个元素是否正确对齐非常有用。 + +![image-20250325215612101](/img/image-20250325215612101.png) + +在这个模型中,网格线告诉我们用户头像和按钮是对齐的。 + +### OLI Grid CSS + +[OLI Grid CSS](https://addons.mozilla.org/en-US/firefox/addon/oli-grid-css/) 插件适用于 Firefox 和 Chrome。它的好处是它在页面中绘制列,就像在 Sketch 和 Adobe XD 中一样。这有助于查看你正在工作的布局是否与列对齐。 + +![image-20250325215631205](/img/image-20250325215631205.png) + +我尝试用 Bootstrap 构建的页面测试插件,它按预期工作。注意你需要首先弄清楚页面的 `.container` 元素的宽度。 + +### Web Developer Extension + +![image-20250325215652486](/img/image-20250325215652486.png) + +一个非常有用的扩展,提供了很多功能。以下是一些关键功能: + +- 禁用所有样式 +- 禁用浏览器默认样式 +- 禁用内联样式 +- 禁用打印样式 + +这只是 CSS 标签中的几个功能! + +### Pesticide Extension + +![image-20250325215708928](/img/image-20250325215708928.png) + +我之前解释过使用轮廓 CSS 属性作为调试设计问题的方法。这个博客做同样的事情,但只需一次点击。它为页面上的每个元素添加随机彩色轮廓,并能够突出显示特定元素。 + +## 在浏览器中模拟 + +有时你想通过移动一些元素来快速在浏览器中模拟设计想法。这对于向开发者、客户或设计师展示设计概念很有用。能够快速进行此类编辑对生产力很重要。 + +利用浏览器的内置工具,我们可以做到这一点。在本节中,我们将专注于在浏览器中快速模拟设计的概念和示例。 + +### 好老的 CSS 定位 + +使用 CSS 定位,我们可以通过在 DevTools 中向一些元素添加 `position` 并将它们放置在我们想要的位置来编辑它们。这是在测试错误时模拟设计想法的快速方法。 + +![image-20250325215756080](/img/image-20250325215756080.png) + +这里我们有一个带有类别的卡片。经过一些思考,设计师告诉你,开发者,团队已经决定想要类别的不同位置。你建议类别可以移动到左上角。这可以在你们都在视频通话时完成。就像添加以下内容一样简单: + +```css +.card { + position: relative; +} +.category { + position: absolute; + left: 0; + top: 16px; +} +``` + +这种不到一分钟的编辑可以让决策更快地发生。 + +### 隐藏设计元素 + +正如我之前解释的,能够快速隐藏设计元素,比如在 Chrome 中使用 `H` 键,是一个有用的技巧。这样做,我们可以隐藏一些设计元素并用其他元素替换它们,例如,如果我们想要截取设计概念的屏幕截图。 + +![image-20250325215815868](/img/image-20250325215815868.png) + +这里我们有一个部分标题,其中包含一个阻止作者头像和姓名对齐的错误。设计团队要求暂时删除它。你可以快速从 HTML 中删除它,用 `display: none` 隐藏它,或在 Chrome 中使用 `H` 键。 + +### CSS Flexbox + +使用 Flexbox,我们可以快速使布局水平或垂直。Flexbox 属性如 `align-items` 和 `justify-content` 很强大,可以完成你想要展示的大多数设计想法。 + +![image-20250325215837320](/img/image-20250325215837320.png) + +这个部分标题有一行项目。问题是项目之间的间距不一致。我们能做什么?最快的解决方案是添加 `display: flex` 和 `justify-content: space-between`。设计立即改变,所有这些都在 DevTools 中发生!你现在可以继续截取此更改的屏幕截图并与同事讨论。 + +### CSS Grid Layout + +这是 CSS 中最强大的布局模块。假设我们有一个特色新闻部分,设计师想要以可呈现的方式布局项目——比如说,作为等高列。 + +![image-20250325215857726](/img/image-20250325215857726.png) + +我们简单地使用 CSS 网格来设置列,然后我们与设计师确认这是他们想要的。 + +```css +.wrapper { + display: grid; + grid-template-columns: 2fr 1fr 1fr; +} +``` + +这对设计师来说还不够吗?你可以继续编辑并向他们展示你的更改。此外,你可以尝试不同的布局概念并将每个概念绑定到 CSS 类,在".cls"面板中切换每个类。 + +### CSS 视口单位 + +我们可以使用视口单位使部分占用视口的完整水平或垂直空间。我们也可以使用它们来调整字体大小。所有这些用例给我们灵活性,使我们的设计更动态。 + +假设我们有一个英雄部分,需要占用屏幕完整高度的 90%。我们想要与设计师验证要求,所以我们非常快速地模拟它: + +```css +.hero { + height: 90vh; +} +``` + +我们给英雄部分一个 `90vh` 的高度,这将使它占用屏幕垂直空间的 90%。我们在不到一分钟内完成了这个编辑! + +### CSS 列 + +如果我们想要比 CSS 网格更快的方法,我们可以使用列。例如,我们可以将页脚中的链接分为两个相等的列。我们可以用一行代码进行此编辑,并立即回到设计师那里。 + +```css +.footer-section { + columns: 2; +} +``` + +CSS 列的另一个好处是我们可以用键盘的上下箭头更改列数。 + +![image-20250325215935175](/img/image-20250325215935175.png) + +### CSS 滤镜 + +假设设计师想要为网站实验暗模式,但他们还没有为此做任何模型。使用 CSS 滤镜,我们可以快速制作暗模式。 + +```css +html { + filter: invert(90%) hue-rotate(25deg); +} +``` + +为了完善它,我们可以恢复不应该被反转的元素(如图像和视频): + +```css +html img, +html video, +html iframe { + filter: invert(100%) hue-rotate(-25deg); +} +``` + +完成后,我们可以截取全页屏幕截图并向团队展示——所有这些都在不到两分钟内完成!这不是很酷吗?发送模型后,团队可以开始思考和决定。此外,你节省了设计师的时间!为小网页制作暗模式至少需要他们 10 分钟。 + +### 去饱和设计 + +使用 CSS 滤镜去饱和页面(即将其转换为黑白)是一个有用的技巧,原因如下: + +- 如果你正在测试的网站颜色很重,你的眼睛可能会疲劳。去饱和页面将帮助你专注于修复手头的错误。 +- 它对测试和探索很有用。当页面饱和时,你可以轻松发现任何不适合设计的颜色。 +- 可访问性测试变得更容易。使页面灰度将让你知道哪些颜色易于阅读,哪些不是。 + +要使用 CSS 去饱和网页,打开浏览器的 DevTools,选择 `html` 或 `body` 元素,并添加以下内容: + +```css +html { + filter: grayscale(1); +} +``` + +就是这样。你现在有一个黑白网站! + +### 线框样式 + +在模拟设计时,我们并不总是有时间选择好的颜色和字体。在这种情况下,我们可以使用一点 CSS 将整个网页转换为线框样式。这将让你专注于快速模拟想法并尽快获得反馈。 + +以下是如何做到的: + +```css +* { + color: #000; + background: #ccc !important; + outline: solid 1px; +} + +img, +video, +iframe { + background: #ccc; + opacity: 0; +} +``` + +## 触摸屏的悬停 + +在触摸设备(手机、平板电脑等)上调试时,你可能会注意到一些元素在滚动时改变颜色或样式。这是因为 `:hover` 样式在滚动时触发。这是一个问题。解决方案是使用 `hover` 媒体查询。根据 [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover): + +> hover CSS 媒体功能可用于测试用户的主要输入机制是否可以悬停在元素上。 + +![image-20250325220020774](/img/image-20250325220020774.png) + +```css +@media (hover: hover) { + .element:hover { + color: #222; + } +} +``` + +这样,我们防止 `:hover` 样式为移动和平板用户触发。在撰写本文时,此功能在所有主要浏览器中都受支持。好处是当你在 Chrome 中激活设备模式时,它将被视为触摸屏,所以你可以在那里测试 `hover` 媒体查询。 + +## 使用 CSS 显示潜在错误 + +没有直接的方法在 CSS 中显示潜在错误。但是,一些聪明的人设计了解决方法,使我们能够调试 HTML 和 CSS 的不正确使用。让我们探索其中一些。 + +### 在上下文之外使用 CSS 类 + +假设你与团队构建了设计系统,你想要检查与设计系统中组件不正确使用相关的错误。在他名为倒三角 CSS(ITCSS)的方法论中,Harry Roberts 使用以下类来创建关于 CSS 类不正确使用的警告。 + +```html +
+
+
+
+
+``` + +`.o-layout` 类用于充当布局包装器的元素。`.o-layout__item` 类应该只应用于具有 `.o-layout` 类的父元素内的元素。以下用法将是不正确的: + +```html +
+
+
+``` + +具有 `.o-layout__item` 类的元素不应该像这样独立存在。我们可以很容易地调试这个: + +```css +.o-layout__item { + /* Show a warning outline by default. */ + outline: solid 5px yellow; +} +.o-layout .o-layout__item { + /* Remove the outline when item is in .o-layout. */ + outline: none; +} +``` + +此外,我们可以检测 `.o-layout__item` 是否是 `.o-layout` 的直接子元素。 + +```css +.o-layout > :not(.o-layout__item) { + outline: solid 5px yellow; +} +``` + +### 向元素添加 `width` 或 `height` 属性 + +一般来说,除了 `img` 之外,不建议为任何 HTML 元素使用 `width` 和 `height` 属性。 + +```css +:not(img):not(object):not(embed):not(svg):not(canvas)[width], +:not(img):not(object):not(embed):not(svg):not(canvas)[height] { + outline: solid 5px red; +} +``` + +进一步,你可以使用 Gaël Poupard 的浏览器扩展,[a11y.css](https://ffoodd.github.io/a11y.css/index.html),它显示不同的建议、警告和错误。