【问题标题】:Dark Mode not working during loading time黑暗模式在加载期间不起作用
【发布时间】:2020-10-02 07:16:35
【问题描述】:

我正在尝试制作一个暗模式切换按钮,它可以在点击时在暗模式和亮模式之间切换,用户首选项也使用localStorage 存储。用户应手动按下按钮以切换到其他模式。如果用户选择的是深色模式,那么每个页面都将处于深色模式,并且在刷新时不会转为浅色模式。到目前为止一切看起来都很好,但真正的问题在于加载时间。页面的加载时间接近 1 秒,在此期间,即使用户选择深色模式,页面也似乎处于浅色模式。我不希望这种情况发生。如果用户的选择是黑暗的,我希望在黑暗模式下加载时间部分。 这是我当前的代码:

<script>
const body = document.querySelector('body');
function toggleDark() {
  if (body.classList.contains('dark')) {
    body.classList.remove('dark');
    localStorage.setItem("theme", "light");
  } else {
    body.classList.add('dark');
    localStorage.setItem("theme", "dark");
  }
}

if (localStorage.getItem("theme") === "dark") {
  body.classList.add('dark');
}
</script>
<style>
body {background-color: #ffffff}
body.dark {background-color: #000000; color: #ffffff} 
</style>
<button class="dark-mode" id="btn-id" onclick="toggleDark()"></button>

【问题讨论】:

  • 我的建议是不要仅仅依赖客户端。您应该能够在服务器端设置类。如果只有客户端能够添加类,那么您在加载期间总是有时间(当仍在加载/解释 Javascript 时)该类不存在。使用 cookie,以便您也可以在服务器端检查它们。
  • 将您的&lt;script&gt; 添加为&lt;body&gt; 内的第一行以帮助减少FOUC
  • 还要考虑prefers-color-scheme css 属性。对于黑暗模式的全面介绍,我建议阅读A Complete Guide to Dark Mode on the Web
  • 您可以在“html”标签中添加/删除类(您可以使用document.documentElement.classList 来执行此操作)...这样,您将类设置为dark 的代码可以在head 元素,在 body 存在之前 - 你需要在 CSS 中将 body.dark 更改为 html.dark body
  • @JaromandaX 我的解决方案怎么样?太复杂了?它都是基于客户端和本地存储的

标签: javascript html jquery css


【解决方案1】:

另一种方法是在&lt;head&gt; 元素中加载脚本,并在html 元素上切换类

为此,您使用document.documentElement.classList,因为那是HTML 元素

然后将您的 CSS 更改为

html.dark body {}

等等 .. HTML 上的类选择器

html body {background-color: #ffffff}
html.dark body {background-color: #000000; color: #ffffff}
<script>
  const body = document.querySelector('body');

  function toggleDark() {
    if (document.documentElement.classList.contains('dark')) {
      document.documentElement.classList.remove('dark');
      //localStorage.setItem("theme", "light");
    } else {
      document.documentElement.classList.add('dark');
      //localStorage.setItem("theme", "dark");
    }
  }

  //if (localStorage.getItem("theme") === "dark") {
    document.documentElement.classList.add('dark');
  //}
</script>
<button class="dark-mode" id="btn-id" onclick="toggleDark()">DARK</button>

由于限制,localStorage 在堆栈溢出时不可用 - 取消注释这些行以使其正常工作

或者 - 见https://jsfiddle.net/e9zg2p4c/

【讨论】:

  • 不错。十分优雅。没有考虑 documentElement
【解决方案2】:

将其存储到后端数据库。然后在提供 HTML 内容时为您的元素放置适当的类/样式。这将消除加载时间之间的闪烁:

<!DOCTYPE html>
<html>
    <head>
        <style>
            /* Use Less/Sass for better management */
            .theme.light {
                background-color: #ffffff;
            }
            .theme.dark {
                background-color: #000000; color: #ffffff;
            }
        </style>
    </head>
    <body class="theme <?= $user->themeName; ?>">

    </body>
</html>

【讨论】:

  • 如果您能够在后端存储用户选择的主题,为什么不简单地拥有两个(或更多)css 文件——例如,dark.css 和 light.css——然后加载link 标签中的正确一个?
  • 是的,但也可以在前端完成 - 可以根据需要添加链接和/或可以存在具有默认方案的现有链接,如果需要,可以更改 href 值。
  • @ATD "更改链接" 需要一些处理,所以它要么是 JS,要么是后端。拥有两个文件也是个坏主意,因为在使用 Less 时,您只需更改特定类的变量值,而不是拥有必须串联维护的两个不同文件。
  • @Justinas 这就是我更喜欢使用变量方法的原因。但是,更改链接相对简单,可以在tbody 之前完成,只需要几行代码和style 标记上的ID,以便您可以定位href 属性。
  • @ATD 从服务器加载代码时,您将如何执行代码?
【解决方案3】:

切换和使用默认主题有点棘手

注意 localStorage 调用在 SO 中不起作用

working example

在下面的代码中替换

const theme = "dark";localStorage.getItem("theme") || "light"

并取消注释// localStorage.setItem("theme", body.classList.contains("dark") ? "light" : "dark");

在你的服务器上

.dark { background-color: black; color: white; }
<!DOCTYPE html>
<html>

<head>
  <style>
    .theme {
      background-color: white;
      color: black;
    }
  </style>
  <script>
    const theme = "dark"; // localStorage.getItem("theme") || "theme"
    if (theme === "dark") {
      const st = document.createElement("style");
      st.id="darkStyle";
      st.innerText = `body.theme { background-color: black; color: white; }`;
      document.querySelector("head").appendChild(st);
    }

    window.addEventListener("load", function() {
      document.getElementById("toggleTheme").addEventListener("click", function() {
        const body = document.querySelector("body");      
        const darkStyle = document.getElementById("darkStyle");
        if (darkStyle) {
          darkStyle.remove(); // remove stylesheet now we know what the user wants
          body.classList.remove("theme");
        }  
        const theme = body.classList.contains("theme"); 
        body.classList.toggle('theme',!theme);
        body.classList.toggle('dark',theme);
        // localStorage.setItem("theme", theme ? "light" : "dark"); // uncomment on your server
      });
    })
  </script>



</head>

<body class="theme">
  Here is the body

  <button id="toggleTheme" type="button">Toggle theme</button>
</body>

</html>

【讨论】:

    【解决方案4】:

    鉴于浅色和深色之间唯一真正的区别是颜色,为什么不简单地为您要使用的每种颜色创建 css 变量并使用 javascript 来更改变量的值。这样,一旦您在适当的位置使用变量定义了类,更改变量值就会自动更改类。 “深色”和“浅色”的选择可以以任何可用的方式存储 - localStorage、cookie 或后端等 - 您只需在页面加载时为 css 变量设置适当的颜色。无需为每个类单独定义,作为开发人员,它允许您快速测试配色方案,而无需逐个手动更改每个类。

    function changeTheme(t) {
      if (t == "dark") {
        document.documentElement.style.setProperty("--backgroundcolour", "black");
        document.documentElement.style.setProperty("--fontcolour", "white");
      } else {
        document.documentElement.style.setProperty("--backgroundcolour", "white");
        document.documentElement.style.setProperty("--fontcolour", "black");
      }
    
    }
    :root {
      --backgroundcolour:black;
      --fontcolour:white;
    }
    
    body {
      background-color:var(--backgroundcolour);
      color:var(--fontcolour);
    }
    
    span {
      background-color:var(--backgroundcolour);
      color:var(--fontcolour);
    }
    
    div {
      background-color:var(--backgroundcolour);
      color:var(--fontcolour);
    }
    
    table {
      background-color:var(--backgroundcolour);
      color:var(--fontcolour);
    }
    <button onclick="changeTheme('dark');">Use dark theme</button><button onclick="changeTheme('light');">Use light theme</button>
    <hr>
    <span>Text in a span</span>
    <hr>
    <div>Text in a div</div>
    <hr>
    <table>
      <tbody>
        <tr><td>Text in a table</td></tr>
      
      </tbody>
    
    </table>

    【讨论】:

    • 这应该在文档 HEAD 部分的 STYLE 标记中。只要在那之后有代码,就可以在加载 BODY 标记之前更改变量。
    • 是的,所以,这就是帮助 OP 的部分:p - 哦,等等,你做到了 - 抱歉,我真的误读了你的代码 - cmets 已删除
    • 需要注意的一点(我也在考虑使用 CSS vars 的解决方案) - 这在 IE 中不起作用(并不是说这是一件坏事,Internet Explorer 的最终死亡将来自许多小切口)
    • 我们从不使用 IE,并且在相当长的一段时间内都建议我们的用户也不要使用。 IE 中有很多怪癖,以至于在所有浏览器中获得任何一致性都成了一场噩梦。当我们不得不考虑 IE 时,用户的选择被存储在后端,页面实际上是用正确的主题呈现的。我们甚至允许他们选择自己的颜色并将其存储起来。但是我们经常不得不添加一个链接样式表来为所有元素类型设置默认值,这样我们至少可以在主 css 中有相同的起点。
    【解决方案5】:

    如果你想使用复选框,这个解决方案适合你。

    如果您希望值保持不变,请使用localStorage。如果您希望在关闭选项卡或浏览器时您的值消失的暗模式,请使用sessionStorage

    const check = document.getElementById('chk');
    
    check.addEventListener('change', () => {
      document.body.classList.toggle('dark');
      localStorage.darkMode=!localStorage.darkMode;
    });
    
    window.onload=function() {
      if(localStorage.darkMode) document.body.classList.toggle('dark');
    }
    #modeSwitcher{
       margin: 5% 50%;
    }
    
    #modeSwitcher .checkbox {
       opacity: 0;
       position: absolute;
    }
    
    #modeSwitcher .checkbox:checked + .label .ball{
       transform: translateX(35px);
    }
    
    #modeSwitcher .checkbox:checked + .label .ball::after{
       content: '';
       position: absolute;
       background-color: #0A0E27;
       width: 13px;
       height: 13px;
       border-radius: 50%;
       bottom: 50%;
       left: -5%;
       transform: translateY(50%);
    }
    
    #modeSwitcher .label {
        background-color: #0A0E27;
        border-radius: 50px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 5px;
        margin: 0;
        position: relative;
        height: 16px;
        width: 50px;
        transform: scale(1.5);
    }
    
    #modeSwitcher .label .fa-moon{
        color:#0A0E27 ;
    }
    
    #modeSwitcher .label .ball {
        background-color: #FDC503;
        border-radius: 50%;
        position: absolute;
        top: 3px;
        left: 3px;
        height: 20px;
        width: 20px;
        transform: translateX(0px);
        transition: transform 0.2s linear;
    }
    
    body{
         background-color: #fff;
    }
    
    body.dark{
         background-color: black;
    }
    <div id="modeSwitcher">
       <input type="checkbox" class="checkbox" id="chk" />
       <label class="label" for="chk">
         <i class="fas fa-moon"></i>
         <div class="ball"></div>
       </label>
    </div>

    【讨论】:

    • window.onload 如果您想避免 1 秒的浅色模式,切换主题为时已晚......问题是如何尽快启用深色主题 - OP 已经有一个工作开关,只是还不够快
    • 将该脚本放在&lt;head&gt;
    • 当然,除了,等等,document.body 还不存在 - 但如果你把它放在 window.onload 中,它是在头部还是在身体中都没关系......它是甚至晚于 OP 的代码也为时已晚
    猜你喜欢
    • 2020-12-11
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-06
    相关资源
    最近更新 更多