处理常见的 HTML 和 CSS 问题

场景设定完毕,我们现在将具体探讨在 HTML 和 CSS 代码中常见的跨浏览器问题,以及可以使用哪些工具来防止问题发生或修复已出现的问题。这包括代码 Lint、处理 CSS 前缀、使用浏览器开发工具追踪问题、使用 Polyfill 为浏览器添加支持、解决响应式设计问题等等。

预备知识 熟悉核心的 HTMLCSSJavaScript 语言;了解 跨浏览器测试的高级原则
目标 能够诊断常见的 HTML 和 CSS 跨浏览器问题,并使用适当的工具和技术来修复它们。

HTML 和 CSS 的麻烦

HTML 和 CSS 的部分麻烦在于这两种语言都相当简单,开发者通常不会认真对待它们,例如确保代码结构良好、高效,并语义化地描述页面上功能的目的。在最糟糕的情况下,JavaScript 被用来生成整个网页内容和样式,这使得你的页面无法访问,并且性能较差(生成 DOM 元素成本很高)。在其他情况下,新兴功能在浏览器之间支持不一致,这可能导致某些功能和样式对某些用户不起作用。响应式设计问题也很常见——一个在桌面浏览器中看起来不错的网站,在移动设备上可能会提供糟糕的体验,因为内容太小无法阅读,或者网站由于昂贵的动画而速度缓慢。

让我们继续看看如何减少由 HTML/CSS 导致的跨浏览器错误。

首先:解决一般问题

我们在本系列的第一篇文章中提到,一个好的开始策略是在桌面/移动设备上的几个现代浏览器中进行测试,以确保你的代码正常工作,然后再专注于跨浏览器问题。

在我们的 调试 HTML调试 CSS 文章中,我们提供了一些关于调试 HTML/CSS 的非常基本的指导——如果你不熟悉这些基础知识,你 definitely 应该在继续阅读之前学习这些文章。

基本上,就是检查你的 HTML 和 CSS 代码是否格式良好,不包含任何语法错误。

注意:CSS 和 HTML 的一个常见问题是不同的 CSS 规则开始相互冲突。当使用第三方代码时,这尤其成问题。例如,你可能会使用一个 CSS 框架,并发现它使用的某个类名与你已经用于不同目的的类名发生冲突。或者你可能会发现由某种第三方 API(例如生成广告横幅)生成的 HTML 包含你已经用于不同目的的类名或 ID。为确保这种情况不会发生,你需要首先研究你正在使用的工具,并围绕它们设计你的代码。对 CSS 进行“命名空间”也值得,例如,如果你有一个小部件,请确保它有一个独特的类,然后用这个类开始选择小部件内部元素的选择器,这样冲突的可能性就会降低。例如 .audio-player ul a

验证

对于 HTML,验证涉及确保所有标签都正确关闭和嵌套,你正在使用 doctype,并且你正在为正确的目的使用标签。一个好的策略是定期验证你的代码。可以做到这一点的一项服务是 W3C Markup Validation Service,它允许你指向你的代码,并返回错误列表。

The HTML validator homepage

CSS 也有类似的情况——你需要检查属性名称是否拼写正确,属性值是否拼写正确并且对其使用的属性有效,是否没有遗漏任何花括号等等。W3C 也有一个 CSS Validator 可供此目的使用。

Linters

另一个不错的选择是所谓的 Linter 应用程序,它不仅指出错误,还可以标记 CSS 中不良实践的警告以及其他一些点。Linter 通常可以自定义,使其在错误/警告报告方面更严格或更宽松。

有许多在线 Linter 应用程序,例如用于 HTML、CSS 和 JavaScript 的 Dirty Markup。这些应用程序允许你将代码粘贴到窗口中,它会用叉号标记任何错误,然后可以悬停叉号以获取告知你问题是什么的错误消息。Dirty Markup 还允许你使用清除按钮修复你的标记。

Dirty Markup application displaying the message "Unexpected character in unquoted attribute" over the following incorrect HTML markup: <div id=combinators">

然而,多次将代码复制粘贴到网页上以检查其有效性并不方便。你真正想要的是一个能够以最小的麻烦融入你的标准工作流程的 Linter。

许多代码编辑器都有 Linter 插件。例如,请参见

浏览器开发者工具

大多数浏览器内置的开发者工具也具有用于查找错误的有用工具,主要用于 CSS。

注意:HTML 错误在开发者工具中不容易显示,因为浏览器会尝试自动更正格式错误的标记;W3C 验证器是查找 HTML 错误的最佳方法——参见上文 验证

例如,在 Firefox 中,CSS 检查器将显示未应用的 CSS 声明,并带有警告三角形。将鼠标悬停在警告三角形上将提供描述性错误消息。

The developer tools cross out invalid CSS and add a hoverable warning icon

其他浏览器开发者工具也有类似的功能。

常见的跨浏览器问题

现在让我们继续讨论一些最常见的跨浏览器 HTML 和 CSS 问题。我们将主要关注缺乏对现代功能的支持以及布局问题。

浏览器不支持现代功能

这是一个常见问题,尤其是在你需要支持旧浏览器或者你使用的功能在某些浏览器中实现但尚未在所有浏览器中实现时。通常,大多数核心 HTML 和 CSS 功能(例如基本的 HTML 元素、CSS 基本颜色和文本样式)在你想要支持的所有浏览器中都能正常工作;当你开始使用较新的 HTML、CSS 和 API 时,会发现更多问题。MDN 为每个文档化的功能显示浏览器兼容性数据;例如,请参阅 :has() 伪类的浏览器支持表

一旦你确定了你将使用的不被普遍支持的技术列表,最好研究一下它们在哪些浏览器中受支持,以及哪些相关技术有用。请参阅下面的 寻求帮助

HTML 回退行为

有些问题可以通过利用 HTML/CSS 的自然工作方式来解决。

未识别的 HTML 元素被浏览器视为匿名行内元素(实际上是没有语义值的行内元素,类似于 <span> 元素)。你仍然可以通过它们的名称引用它们,并用 CSS 为它们设置样式,例如——你只需要确保它们按你希望的方式运行。像为其他任何元素设置样式一样,包括在需要时将 display 属性设置为 inline 以外的值。

更复杂的元素,如 HTML <video><audio><picture><object><canvas>(以及其他功能)都有自然的机制来添加回退,以防链接的资源不受支持。你可以在开始和结束标签之间添加回退内容,不支持的浏览器将有效地忽略外部元素并运行嵌套内容。

例如

html
<video id="video" controls preload="metadata" poster="img/poster.jpg">
  <source
    src="video/tears-of-steel-battle-clip-medium.webm"
    type="video/webm" />
  <!-- Offer download -->
  <p>
    Your browser does not support WebM video; here is a link to
    <a href="video/tears-of-steel-battle-clip-medium.mp4"
      >view the video directly</a
    >
  </p>
</video>

此示例包含一个简单的链接,允许你在 HTML 视频播放器甚至无法工作时下载视频,这样至少用户仍然可以访问视频。

另一个例子是表单元素。当引入新的 <input> 类型用于在表单中输入特定信息(如时间、日期、颜色、数字等)时,如果浏览器不支持新功能,浏览器会使用 type="text" 的默认值。添加的输入类型非常有用,尤其是在移动平台上,提供一种无痛的数据输入方式对用户体验非常重要。平台根据输入类型提供不同的 UI 小部件,例如用于输入日期的日历小部件。如果浏览器不支持某种输入类型,用户仍然可以输入所需数据。

以下示例显示日期和时间输入

html
<form>
  <div>
    <label for="date">Enter a date:</label>
    <input id="date" type="date" />
  </div>
  <div>
    <label for="time">Enter a time:</label>
    <input id="time" type="time" />
  </div>
</form>

此代码的输出如下

你可以按 播放 按钮在 MDN Playground 中打开示例并编辑源代码。

如果你查看示例,你将在尝试输入数据时看到 UI 功能正在运行。在具有动态键盘的设备上,将显示特定类型的键盘。在不支持的浏览器中,输入将仅默认为普通文本输入,这意味着用户仍然可以输入正确的信息。

CSS 回退行为

CSS 在回退方面可以说比 HTML 更好。如果浏览器遇到它不理解的声明或规则,它只会完全跳过它,而不会应用它或抛出错误。如果这样的错误溜进生产代码,这可能会让你和你的用户感到沮丧,但至少这意味着整个站点不会因为一个错误而崩溃,而且如果巧妙地使用,你可以利用它。

让我们看一个例子——一个用 CSS 样式化的简单盒子,它有一些由各种 CSS 功能提供的样式

你可以按 播放 按钮在 MDN Playground 中打开示例并使用源代码。

该按钮应用了许多声明,但我们最感兴趣的是以下这些

css
button {
  /* … */

  background-color: red;
  background-color: rgb(255 0 0 / 90%);
  box-shadow:
    inset 3px 3px 3px rgb(255 255 255 / 40%),
    inset -3px -3px 3px rgb(0 0 0 / 40%);
}

button:hover,
button:focus {
  background-color: rgb(255 0 0 / 50%);
}

button:active {
  box-shadow:
    inset 3px 3px 3px rgb(0 0 0 / 40%),
    inset -3px -3px 3px rgb(255 255 255 / 40%);
}

这里我们提供了一个 RGB background-color,它在悬停时会改变不透明度,以提示用户该按钮是交互式的,以及一些半透明的内嵌 box-shadow 阴影,为按钮增加一点纹理和深度。虽然现在已完全支持,但 RGB 颜色和盒子阴影并非一直存在;它们从 IE9 开始出现。不支持 RGB 颜色的浏览器会忽略该声明,这意味着在旧浏览器中背景根本不会显示,因此文本将无法阅读,这很糟糕!

Hard to see pill button with white text on an almost white background

为了解决这个问题,我们添加了第二个 background-color 声明,它只指定了 red 颜色关键字——这在非常旧的浏览器中很早就得到了支持,如果现代炫酷功能不起作用,它就充当回退。发生的情况是,访问此页面的浏览器首先应用第一个 background-color 值;当它到达第二个 background-color 声明时,如果它支持 RGB 颜色,它将用此值覆盖初始值。如果不支持,它将只忽略整个声明并继续。

注意:对于其他 CSS 功能,如 媒体查询@font-face@supports 块,情况也是如此——如果它们不受支持,浏览器会直接忽略它们。

选择器支持

当然,如果你没有使用正确的 选择器 来选择你想要样式化的元素,那么任何 CSS 功能都不会应用!

在一个逗号分隔的选择器列表中,如果你只是错误地编写了一个选择器,它可能无法匹配任何元素。但是,如果一个选择器无效,则整个选择器列表都会被忽略,以及整个样式块。因此,只在一个宽容选择器列表中包含一个 :-moz- 前缀的伪类或伪元素,例如 :where(::-moz-thumb)。不要在 :is():where() 宽容选择器列表之外的逗号分隔选择器组中包含 :-moz- 前缀的伪类或伪元素,因为除 Firefox 之外的所有浏览器都将忽略整个块。请注意,:is():where() 都可以作为参数传递给其他选择器列表,包括 :has():not()

我们发现,使用浏览器开发工具检查你尝试样式化的元素,然后查看 DOM 检查器通常提供的 DOM 树面包屑路径,以查看你的选择器是否与其匹配,这很有帮助。

例如,在 Firefox 开发者工具中,你在 DOM 检查器底部会得到这种输出

The breadcrumb of elements is html > body > form > div.form > input#date

如果例如你正在尝试使用此选择器,你将能够看到它不会按预期选择输入元素

css
form > #date {
  /* … */
}

date 表单输入不是 <form> 的直接子元素;你最好使用通用后代选择器而不是子选择器)。

处理 CSS 前缀

另一组问题来自 CSS 前缀——这些前缀最初是一种机制,允许浏览器供应商在技术处于实验阶段时实现自己的 CSS(或 JavaScript)功能版本,这样他们就可以在不与其他浏览器实现或最终无前缀实现冲突的情况下进行试验和完善。

例如,Firefox 使用 -moz-,Chrome/Edge/Opera/Safari 使用 -webkit-。在旧代码中你可能会遇到并可以安全移除的其他前缀包括 -ms-(Internet Explorer 和早期版本 Edge 使用)和 -o(Opera 早期版本使用)。

带前缀的功能从未被打算用于生产网站——它们可能会在不事先警告的情况下更改或移除,可能会在需要它们的老旧浏览器版本中导致性能问题,并且一直是跨浏览器问题的根源。这尤其是一个问题,例如,当开发人员决定只使用属性的 -webkit- 版本时,这意味着网站在其他浏览器中将无法工作。这种情况实际上发生得如此之多,以至于其他浏览器供应商也实现了几个 CSS 属性的 -webkit- 前缀版本。虽然浏览器仍然支持一些带前缀的属性名称、属性值和伪类,但现在实验性功能被放在标志后面,以便 Web 开发人员可以在开发过程中测试它们。

如果使用前缀,请确保它确实需要;该属性是少数仍需要前缀的功能之一。你可以在 MDN 参考页面和像 caniuse.com 这样的网站上查找哪些浏览器需要前缀。如果你不确定,你也可以通过直接在浏览器中进行一些测试来找出答案。在带前缀的样式声明之后包含标准无前缀版本;如果不支持,它将被忽略,如果支持,则会使用它。

css
.masked {
  -webkit-mask-image: url("MDN.svg");
  mask-image: url("MDN.svg");
  -webkit-mask-size: 50%;
  mask-size: 50%;
}

尝试这个简单的例子

  1. 使用此页面或另一个带有突出标题或其他块级元素的站点。

  2. 在相关元素上右键/Cmd + 点击,然后选择“检查”/“检查元素”(或你的浏览器中的相应选项)——这应该会在你的浏览器中打开开发者工具,并在 DOM 检查器中突出显示该元素。

  3. 寻找一个可用于选择该元素的功能。例如,在撰写本文时,MDN 上的这个页面有一个 ID 为 mdn-docs-logo 的标志。

  4. 将此元素的引用存储在一个变量中,例如

    js
    const test = document.getElementById("mdn-docs-logo");
    
  5. 现在尝试为该元素上你感兴趣的 CSS 属性设置一个新值;你可以使用元素的 style 属性来做到这一点,例如尝试将这些内容输入到 JavaScript 控制台中

    js
    test.style.transform = "rotate(90deg)";
    

当你在第二个点后开始输入属性名称表示时(请注意,在 JavaScript 中,CSS 属性名称以 小驼峰命名法 编写,而不是 中划线命名法),JavaScript 控制台应该开始自动完成浏览器中存在的属性名称并匹配你目前已输入的内容。这对于查找该浏览器中实现了哪些属性非常有用。

如果你确实需要包含现代功能,请使用 @supports 测试功能支持,它允许你实现本机功能检测测试,并将带前缀或新功能嵌套在 @supports 块中。

响应式设计问题

响应式设计是创建能够适应不同设备外形(例如,不同的屏幕宽度、方向(纵向或横向)或分辨率)的网页布局的实践。例如,桌面布局在移动设备上查看时会显得很糟糕,因此你需要使用 媒体查询 提供合适的移动布局,并确保使用 视口 正确应用。你可以在 我们的响应式设计教程 中找到此类实践的详细说明。

分辨率也是一个大问题——例如,移动设备比台式电脑更不可能需要大而重的图像,而且更有可能拥有较慢的互联网连接,甚至可能昂贵的数据套餐,这使得浪费带宽成为一个更大的问题。此外,不同的设备可以有不同的分辨率范围,这意味着较小的图像可能会出现像素化。有许多技术可以解决这些问题,从 媒体查询 到更复杂的 响应式图像技术,包括 <picture> 元素和 <img> 元素的 srcsetsizes 属性。

寻求帮助

你会遇到许多其他 HTML 和 CSS 问题,因此了解如何在线查找答案变得非常宝贵。

最好的支持信息来源包括 Mozilla 开发者网络(你现在就在这里!)、stackoverflow.comcaniuse.com

要使用 Mozilla 开发者网络 (MDN),大多数人会在搜索引擎中搜索他们想要查找信息的技术,加上“mdn”一词,例如“mdn HTML 视频”。MDN 包含几种有用的内容类型

caniuse.com 提供支持信息,以及一些有用的外部资源链接。例如,请参见 https://caniuse.cn/#search=video(你只需在文本框中输入你正在搜索的功能)。

stackoverflow.com (SO) 是一个论坛网站,你可以在其中提问,并让其他开发人员分享他们的解决方案,查找以前的帖子,并帮助其他开发人员。建议你在发布新问题之前,先查看是否已有你问题的答案。例如,我们在 SO 上搜索“禁用 HTML 对话框上的自动对焦”,很快就找到了 禁用 showModal 自动对焦使用 HTML 属性

除此之外,尝试在你喜欢的搜索引擎中搜索你问题的答案。如果遇到特定的错误消息,搜索它们通常很有用——其他开发人员很可能也遇到过和你相同的问题。

总结

现在你应该熟悉在 Web 开发中会遇到的主要类型的跨浏览器 HTML 和 CSS 问题,以及如何着手解决它们。