likes
comments
collection
share

前端主题切换方案实践

作者站长头像
站长
· 阅读数 14

前言

主题切换方案大致可以分为两种方案:

  • 固定主题
  • 用户自定义主题

固定主题即系统提供固定的几种主题颜色供用户选择,这种方案一般主题不会太多。

用户自定义主题即系统允许用户可以自定义主题颜色,如通过取色面板设定主题颜色。

一、加载指定主题css文件

这种方案一般适合固定几种主题的切换,根据不同主题开发对应主题的css文件。当切换主题时加载对应的css文件。

可以将页面的通用样式定义在一个文件,比如不涉及颜色的width、height、font-size等。然后将涉及到颜色相关的样式属性如color、background等写在对应主题文件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 公共样式文件 -->
  <link rel="stylesheet" href="./css/common.css">
  <!-- 默认主题 -->
  <link rel="stylesheet" href="./css/light.css" data-theme="light">
</head>
<body>
  <h3>主题方案:加载不同css文件</h3>
  <p>
    <button id="switch">切换主题</button>
  </p>
  <div class="wrp"></div>
</body>
</html>
/* dark */

.wrp{
  background-color: #666;
  color:blueviolet;
}
/* light */

.wrp{
  background-color: red;
  color: #Fff
}
  <script>
    window.onload = function(){
      //默认主题
      let theme = 'light';
      
      const switchBtn = document.getElementById('switch')
    
      const wrp = document.getElementsByClassName('wrp')[0]

      wrp.innerHTML = theme
      // 加载主题样式文件 
      function loadTheme(theme){
        const linkTag = document.createElement('link');
        linkTag.setAttribute('rel', 'stylesheet')
        linkTag.setAttribute('data-theme', theme)
        linkTag.setAttribute('href', `./css/${theme}.css`)
        document.getElementsByTagName('head')[0].appendChild(linkTag)
        wrp.innerHTML = theme
      }
      // 绑定事件
      switchBtn.addEventListener('click', function(){
        // 主题切换
        theme = theme === 'light' ? 'dark' : 'light';
        const link = document.getElementsByTagName('link')
        const arrLink = [...link]
        arrLink.forEach(item => {
          const nowTheme = item.getAttribute('data-theme')
          if(nowTheme && nowTheme !== theme){
            // 删除旧主题
            item.parentElement.removeChild(item)
            // 加载新主题
            loadTheme(theme)
          }
        })
      }, false)
    }
  </script>
特点:

此方案比较简单,但是在开发阶段会比较繁琐,需要维护不同主题颜色文件。由于需要加载对应主题样式文件,所以在切换主题时候可能会存在延迟。

二、css3 自定义属性

自定义属性是css3提供的新特性,自定义属性以两个减号(--)声明,由var()函数来取值。

/* 声明自定义属性 */
body{
  --default-width: 100px;
}

/* 访问自定义属性 */
.wrp{
  width: var(--default-width)
}
作用域:

自定义属性所在的容器下的节点均可访问。

方案介绍:
  • 在文档根元素伪类(:root)下定义主题相关的主题颜色值。
  • 通过html5原属自定义属性(data-)来声明不同主题。
  • 在用户选择对应主题时候修改根元素自定义属性值。
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./index.css">
  <script src="./index.js"></script>
</head>
<body>
  <h3>css自定义属性实现页面主题切换</h3>
  <p>dark/light: <input type="checkbox" id="checkbox" value="dark"></p>

  <div class="content">这是一段描述文字</div>

</body>
</html>
window.onload = function(){
  // 设置自定义属性值
  function switchRootTheme(theme){
    const htmlElement = document.getElementsByTagName('html')[0];
    const targetTheme = htmlElement.getAttribute('data-theme');
    if(theme !== targetTheme){
      htmlElement.setAttribute('data-theme', theme)
    }
  }
  
  // 切换固定主题
  const chekboxS = document.getElementById('checkbox');
  chekboxS.addEventListener('click', function(e){
    if(e.target.checked) {
      // dark
      switchRootTheme('dark')
    } else {
      // light
      switchRootTheme('light')
    }
  }, false)

}
html[data-theme='dark']:root {
  --primary-color: red;
}

html[data-theme='light']:root{
  --primary-color: #666;
}

.content{
  width: 80%;
  height: 220px;
  margin: 0 auto;
  text-align: center;
  background-color: var(--primary-color);
}

特点

  • 此方案不需要维护不同主题样式文件
  • 此方案不需要在切换主题时候请求加载对应样式文件而存在延迟
  • 存在一定的兼容性问题,ie11不兼容。

三、自定义主题颜色

此方案与前面两种方案不同,不再是仅仅支持固定的几种主题颜色,而是允许用户随意设定主题颜色。通过取色面板可以快速的切换主题颜色。

这种方案是在第二种方案的基础上升级而来。结合css自定义属性,通过js获取和设置自定义属性值从而实现自定义切换主题方案。

  • 获取:element.style.getPropertyValue(自定义属性名)或者getComputedStyle(element).getPropertyValue(自定义属性名)
  • 设置:element.style.setProperty(自定义属性名,值)

在方案二的基础上增加以下代码:

  <h3>动态主题</h3>
  <p>选择主题颜色:<input type="color" id="customColor"></p>
// 动态随机主题
  const customColor = document.getElementById('customColor')
  customColor.addEventListener('change', function(e){
    const selectValue = e.target.value;

    const htmlElement = document.getElementsByTagName('html')[0];
    // 获取自定义变量值
    console.log(getComputedStyle(htmlElement).getPropertyValue('--primary-color'))
    // 设置自定义变量值
    htmlElement.style.setProperty('--primary-color', selectValue)
  }, false)

总结

除了上面的三种方案之外,其实还有很多方案可以实现,如css优先级覆盖、less/scss变量、html{filter:grayscale(1)}。如有不对之处,欢迎交流学习。