如何用 CSS 和 3 行简单的 JavaScript 代码实现暗黑模式

2025-05-24

如何用 CSS 和 3 行简单的 JavaScript 代码实现暗黑模式

不可否认,大多数现代网站和网络应用的必备功能是能够将网站主题从亮色模式切换到暗色模式。这项功能已逐渐成为现代网络的潮流,了解如何实现它无疑会在未来的项目中或在现有项目中派上用场。编程中的一切都有千差万别的方法,在网站上实现暗色模式也不例外……方法有很多,但在本文中,我们将探讨其中一种。

先决条件

了解 CSS 自定义属性(CSS 变量)

与所有编程语言一样,变量只是为内存位置赋予的名称,这些位置可以轻松存储值,这使得变量可以在程序中重复使用,而无需在代码中的多个位置重复/硬编码实际值。您可能知道,这使得通过在定义/声明变量的地方更改其值,可以轻松实现全局更改,这反过来又会反映在该变量的每个实例中(变量的值会在使用变量的任何地方发生变化)。

幸运的是,CSS 规范允许使用称为自定义 CSS 属性的变量。这些自定义属性被定义为名称/值对的组合,可以与元素关联。定义变量名称的方法是使用双连字符/两个破折号开头,后跟自定义属性的名称,最后为其赋值,例如:

example {
  --primary-color: #222;
}
Enter fullscreen mode Exit fullscreen mode

要在规则中使用此自定义原色属性,您必须使用传递var()给它的自定义属性的名称来调用该函数。例如

example {
  background-color: var(--primary-color)
}
Enter fullscreen mode Exit fullscreen mode

正如您所猜测的,这与设置相同,background-color: #222因为变量 --primary-color 是其所持有的实际值的占位符。

范围

根据这些变量的定义位置,它们可以具有全局作用域,这意味着它们可以在样式表的任何位置访问和使用,也可以具有局部作用域,将其使用限制在特定规则内。要赋予变量全局作用域,必须将其存储在:root样式表中的伪类选择器中。此:root选择器以 HTML 标记中的根元素为目标,即<html>文档中的元素。您可以简单地将:root选择器视为元素的表示,<html>但具有更高的特异性(优先级)。

:root {
  --primary-color: #222;
}
Enter fullscreen mode Exit fullscreen mode

通过在根选择器伪类中声明此变量,它被赋予全局范围,并且可以在样式表中的任何位置使用。例如:

h1 {
  color: var(--primary-color)
}

div {
  background-color: var(--primary-color)
}
Enter fullscreen mode Exit fullscreen mode

要赋予变量局部作用域,必须在规则集中定义该变量,并且该变量只能在该规则集(局部作用域)内访问。例如:

element {
  --primary-color: #fff;
  color: var(--primary-color);
  border: 2px solid var(--primary-color)
}
Enter fullscreen mode Exit fullscreen mode

使用局部变量覆盖全局变量

这些变量最有趣的特性是,当一个已经在:root全局作用域中定义的变量在规则集(局部作用域)中用新值重新声明时,新值将覆盖全局作用域的值,但仅限于该规则集内。例如:

:root {
  --primary-color: #222;
}
h1 {
  --primary-color: #4169e1;
  color: var(--primary-color);
}
h2 {
  color: var(--primary-color)
}
Enter fullscreen mode Exit fullscreen mode

输出👇

本地范围覆盖全局.png

实现暗黑模式功能

凭借我们目前对 CSS 变量的了解,实现暗黑模式功能非常简单。在本教程中,我们将使用之前在另一篇文章中制作的 CSS 切换开关,如果您对此切换开关的制作方法感兴趣,可以快速跳转到该文章。

HTML 标记

对于 HTML 标记,我们只需放置这个切换开关和一个包含一堆内容的 div

  <body>
    <!-- Toggle Switch -->
    <div class="switch">
      <input type="checkbox" id="switch" />
      <label for="switch">
        <i class="fas fa-sun"></i>
        <i class="fas fa-moon"></i>
        <span class="ball"></span>
      </label>
    </div>

    <!-- Content of Our Webpage -->
    <div class="content">
      <h1>Heading</h1>
      <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Expedita, non?
      </p>
    </div>
  </body>
Enter fullscreen mode Exit fullscreen mode

输出👇
无样式.png

为了简单起见,以上就是我们实现此功能所需的全部 HTML。我们的想法是,内容部分代表我们网页的全部内容,而切换按钮是我们点击后触发网页更改的按钮。

CSS 标记

这就是我们的 CSS 自定义属性(变量)发挥关键作用的地方。其理念是,我们不再使用硬编码值来设置网站的配色方案(网站上的各种颜色),而是将网站初始模式(浅色模式)的配色方案作为变量存储在:root样式表的(全局范围)中,然后在通常使用普通硬编码值的地方使用这些变量。那么,我们就这样做吧:

:root {
  --bg-color: #fff;
  --primary-text-color: #222;
  --secondary-text-color: #444;
}
Enter fullscreen mode Exit fullscreen mode

我们在全局根元素中创建了三个自定义变量,现在我们将使用这些变量来设置网页样式。首先,我们使用 flex 将所有内容居中,然后为网页添加背景色,并使用这些变量为内容添加两种不同的颜色。

body {
  display: flex;
  height: 100vh;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background: var(--bg-color);
}
.content {
  color: var(--primary-text-color);
}
.content p {
  color: var(--secondary-text-color);
}
Enter fullscreen mode Exit fullscreen mode

输出👇

样式.png

现在我们将创建一个黑暗主题类规则集,并使用针对黑暗模式前景定制的新值重新定义我们的全局变量。

.dark-mode {
  --bg-color: #3e4c59;
  --primary-text-color: #fff;
  --secondary-text-color: #ddd;
}
Enter fullscreen mode Exit fullscreen mode

每当切换开关打开时,暗模式类中的新值将用于覆盖全局变量的值,这是通过使用 JavaScript 将这个新创建的.dark-mode类添加到我们的<body>元素来完成的......这意味着我们网页主体范围内的任何元素都将应用暗模式。

JavaScript 的 3 行神奇代码 :)

const switchTheme = document.querySelector("#switch");

switchTheme.addEventListener("click", function () {
  document.body.classList.toggle("dark-mode");
});
Enter fullscreen mode Exit fullscreen mode

对于那些刚接触或不熟悉 JavaScript 的人来说,下面是上述代码的具体功能:

  • const switchTheme = document.querySelector("#switch"):我们只是要求 JavaScript 查询具有 switch ID 的 DOM(在网页中查找元素),然后将该元素存储在常量变量中。
  • switchTheme.addEventListener("click", function () {}:现在使用变量名访问该切换元素,我们要求 JavaScript 监听点击事件,当发生此点击时,它只会运行一个保存我们代码的函数。
  • document.body.classList.toggle("dark-mode"):正如你所猜测的,我们选中<body>网页中的元素,并检查它是否有“dark-mode”类。如果没有,则添加该类。如果已经有了,则移除该类。

输出👇

最终输出.gif

注意到引擎盖下发生了什么吗?

最终输出 dev tool.gif
当点击切换按钮时,我们的dark-mode类会被添加到文档主体中,这将导致整个网页中使用的所有自定义 CSS 属性值都被 dark-mode 类中的新值覆盖。再次点击它会删除这个类,从而使我们最初的全局变量再次生效。

现在让我们来看看 Hashnode.com 对其黑暗模式的实现: 注意,它基本上是相同的,但他们只是将其添加到元素中,仍然......你明白了这个想法以及如何在未来或预先存在的功能中实现此功能。
Hashnode的黑暗模式
<html>

结论

恭喜您读完了这篇文章。🎉 顺便说一句,我想指出我之前遇到的一个特殊实现,在本教程中,讲师希望实现一个选项,让用户可以在网站上选择 3 种颜色模式(亮、暗和蓝色模式),所以他将网站的初始样式复制到几个样式表中,并手动编辑每个样式表以匹配所选主题,然后使用 JavaScript 在用户点击时动态地将样式表链接交换为用户选择的链接。

它按预期运行,但回想起来,我发现实现上有些奇怪,点击选项后,新主题生效前明显有 1-2 秒的延迟,这让我很疑惑。现在我明白了,这个延迟是由于浏览器下载并应用了新的样式表,所以才出现了轻微的延迟。你可以猜到,这在性能和用户体验上都不是很好,但你可以自由尝试。

支持

如果您觉得这篇文章对您有帮助(我敢打赌您一定觉得有用😉),或者有疑问?或者发现错误/拼写错误……请在评论区留下您的反馈。最后,如果有人正在努力实现暗黑模式,请分享此资源并关注我以获取更多信息。

如果你感觉慷慨(我希望你是 🙂)或者想要鼓励我,你可以给我一杯(或一千杯)咖啡,让我开心一笑。:)

文章来源:https://dev.to/daveyhert/how-to-implement-a-dark-mode-with-css-and-3-simple-lines-of-javascript-576
PREV
每个开发人员都应该关注的 12 个网站
NEXT
使用 JavaScript 的 Intersection Observer API 在滚动时显示内容