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

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

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

主题切换

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

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

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

相关推荐
周亚鑫几秒前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
落魄小二10 分钟前
el-table 表格索引不展示问题
javascript·vue.js·elementui
y52364810 分钟前
Javascript监控元素样式变化
开发语言·javascript·ecmascript
Justinc.17 分钟前
CSS3新增边框属性(五)
前端·css·css3
fruge24 分钟前
纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码
javascript·css·css3
neter.asia33 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
嚣张农民40 分钟前
JavaScript中Promise分别有哪些函数?
前端·javascript·面试
光影少年1 小时前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
Rattenking1 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
熊的猫2 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js