网页主题自动适配:网页跟随系统自动切换主题

主题切换是网站设计中一个非常有趣的功能,它允许用户在多种预先设计的样式之间轻松切换,以改变网站的视觉表现。最常见的就是白天和黑夜主题的切换,用户可以根据自己的喜好进行设置。

除了让用户手动去切换主题外,如果能够让用户第一次访问时,网站就设置为用户系统的主题样式,这无疑是一种更好的体验。

主题切换

CSS主题切换有多种方式实现,这里就简单描述下,不是本文重点

方式1:通过自定义标签属性来实现主题切换

css 复制代码
/* 默认主题样式 */
body {
  background-color: white;
  color: black;
}
​
/* 深色主题样式 */
body[data-theme="dark"] {
  background-color: black;
  color: white;
}
css 复制代码
:root {
  --body-background: rgb(248, 244, 212);
  --text-color: #000;
}
html[data-theme="dark"] {
  --body-background: rgb(58, 70, 90);
  --text-color: #eee;
}

方式2:运行时动态地修改CSS变量的值,从而实现主题切换的效果

css 复制代码
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --background-color: white;
  --text-color: black;
}
​
body {
  background-color: var(--background-color);
  color: var(--text-color);
}
javascript 复制代码
document.documentElement.style.setProperty('--background-color', 'black');
document.documentElement.style.setProperty('--text-color', 'white');

方式3:使用类名切换,通过对顶层节点设置不同的类名,然后定义不同类名的CSS样式,切换主题时修改类名即可

css 复制代码
.theme-light {
  background-color: white;
  color: black;
}
​
.theme-dark {
  background-color: black;
  color: white;
}
javascript 复制代码
function switchTheme() {
  const bodyElement = document.body;
  if (bodyElement.classList.contains('theme-light')) {
    bodyElement.classList.remove('theme-light');
    bodyElement.classList.add('theme-dark');
  } else {
    bodyElement.classList.remove('theme-dark');
    bodyElement.classList.add('theme-light');
  }
}


方式4:link标签动态引入

javascript 复制代码
function switchTheme(theme) {
  const linkElement = document.createElement('link');
  linkElement.rel = 'stylesheet';
​
  if (theme === 'light') {
    linkElement.href = 'theme-light.css'; // 切换为浅色主题
  } else {
    linkElement.href = 'theme-dark.css'; // 切换为深色主题
  }
​
  document.head.appendChild(linkElement);
}

扫码加入前端交流群,期待你的加入!

CSS媒体查询

CSS媒体查询是实现响应式网页设计的重要工具,它允许根据设备的特定特性来应用不同的样式规则。

使用 @media 规则可以实现这一功能,通过这个规则可以定义一个或多个条件,当这些条件满足时,相应的样式代码块将会被应用到文档上。例如,在屏幕宽度小于或等于768像素时,背景颜色可以设置为浅蓝色

css 复制代码
/* 基础样式 */
body {
  background-color: lightblue;
  color: white;
  font-size: 16px;
  padding: 20px;
}
​
/* 当屏幕宽度小于或等于768像素时,应用以下样式 */
@media (max-width: 768px) {
  body {
    background-color: red;
    color: black;
    font-size: 14px;
    padding: 10px;
  }
}

CSS媒体查询还可以检测用户是否有将系统的主题色设置为两色或者暗色

  • light:表示用户已告知系统选择使用浅色主题界面
  • dark:表示用户已告知系统选择使用暗色主题界面
css 复制代码
.day {
  background: #eee;
  color: black;
}
.night {
  background: #333;
  color: white;
}
​
@media (prefers-color-scheme: dark) {
  .day.dark-scheme {
    background: #333;
    color: white;
  }
  .night.dark-scheme {
    background: black;
    color: #ddd;
  }
}
​
@media (prefers-color-scheme: light) {
  .day.light-scheme {
    background: white;
    color: #555;
  }
  .night.light-scheme {
    background: #eee;
    color: black;
  }
}

这种方式的确可以实现主题跟随系统的变换而变换,但是存在以下缺点:

  • 增加工作量:开发者需要编写更多的CSS代码,这可能会导致工作效率降低

  • 加载时间延长:随着CSS代码量的增加,页面的加载时间可能会变长,尤其是对于那些包含大量媒体查询的复杂样式表

  • 用户无法自定义:样式固定,用户无法自定义设置主题样式

JS媒体查询

JS同样拥有媒体查询的方法 matchMedia()。传入一个被用于媒体查询解析的字符串,返回一个用来媒体查询新的 MediaQueryList 对象,其中的 matchs 属性值就是匹配结果。

javascript 复制代码
// 如果视口的宽度小于或等于600像素,则输出为true
const mql = window.matchMedia('(max-width: 600px)');
console.log(mql.matchs)

同样的也可以用来查询系统是否使用了暗色主题

javascript 复制代码
const osThemeIsDark = matchMedia("(prefers-color-scheme: dark)").matches;

接下来就采用上面方式1的主题切换方案,结合JS媒体查询来实现跟随系统主题切换的功能。

css 复制代码
:root {
  --body-background: rgb(248, 244, 212);
  --text-color: #000;
}
html[data-theme="dark"] {
  --body-background: rgb(58, 70, 90);
  --text-color: #eee;
}
body {
  padding-top: 200px;
  height: 100vh;
  width: 100vw;
  margin: 0;
  background: var(--body-background);
  text-align: center;
  color: var(--text-color);
}
css 复制代码
<body>
  <div>
  <select>
    <option value="light">白天模式</option>
    <option value="dark">黑夜模式</option>
    <option value="os">跟随系统</option>
  </select>
  </div>
  <h1>公众号:前端筱园</h1>
  <p>www.dengzhanyong.com</p>
</body>
javascript 复制代码
const select = document.querySelector('select');
const html = document.querySelector("html");
// 获取用户设置的主题,默认是跟随系统
const localTheme = localStorage.getItem('theme') || 'os';
settingTheme(localTheme);
// 设置select选中的值
select.value = localTheme;
​
select.onchange = (e) => {
const theme = e.target.value;
    settingTheme(theme);
    localStorage.setItem('theme', theme)
}
​
function settingTheme(theme) {
    // 如果是跟随系统,就获取系统的主题
if (theme === 'os') {
const osThemeIsDark = matchMedia("(prefers-color-scheme: dark)").matches;
        html.dataset.theme = osThemeIsDark ? 'dark' : 'light';
    } else {
        html.dataset.theme = theme;
    }
}

监听媒体变化

现在还有一个问题,如果页面已经打开的情况下,再去修改系统主题,是否能检测到系统主题的变化,使得网页在不刷新的情况下自动切换。

答案是可以的,可以通过监听 MediaQueryListchange 事件,当 matches 的值发生变化时会触发。

javascript 复制代码
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", event => {
    html.dataset.theme = event.matches ? 'dark' : 'light';
})

利用媒体查询还可以检测很多内容,比如:浏览器可视区域尺寸、设备尺寸、设备目前处于横向还是纵向、检测设备宽高比、设备颜色位数等

写在最后

欢迎访问我的个人网站:www.dengzhanyong.com

前端筱园交流群 】已经正式开通啦!在这里,你可以与一群志同道合的小伙伴们交流、分享、学习、共同成长,扫码加入,期待你的加入!

关注我的公众号【前端筱园】,不错过每一篇推送

相关推荐
joan_8510 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
还是大剑师兰特33 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
燃先生._.9 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖10 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
black^sugar11 小时前
纯前端实现更新检测
开发语言·前端·javascript
2401_8576009512 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_8576009512 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js