Maniac Piano
Maniac Piano

Reputation: 147

Dark Mode not working during loading time

I'm trying to make a dark mode toggle button which can toggle between dark and light mode on click, User preference is also stored using localStorage. The user should manually press the button to toggle to other mode. If the user's choice is dark mode, Every page will be in dark mode and it doesn't turn to light mode on refreshing. Everything looks fine upto now but the real issue comes with loading time. The load time of a page is nearly 1 second and in that time, Page appears to be in light mode even if user's choice is dark mode. I don't want that to happen. I want loading time section in dark mode if user's choice is dark. This is my current code:

<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>

Upvotes: 4

Views: 3191

Answers (5)

frankfurt
frankfurt

Reputation: 153

If you want to use checkbox, this solution for you.

if you want the value to remain unchanged, use localStorage. If you want a dark mode where you have values ​​disappear when you close a tab or browser, use 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>

Upvotes: 0

ATD
ATD

Reputation: 1364

Given that the only real difference between light and dark is the colours, why not simply create css variables for each colour you are going to use and use javascript to change the values of the variables. This way, once you have defined the classes using the variables in the appropriate places, changing the variable values changes the classes automatically. The choice of "dark" and "light" can be stored in whatever way is available to you - localStorage, cookies or backend etc - and you simply set the appropriate colours to the css variables when the page is being loaded. There's no need for separate definitions for each class and, as a developer, it allows you to quickly test the colour schemes without having to manually change every class one by one.

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>

Upvotes: 0

mplungjan
mplungjan

Reputation: 177950

A little more tricky to toggle AND have a default theme

Note the localStorage calls do not work here at SO

working example

In the code below replace

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

and uncomment // localStorage.setItem("theme", body.classList.contains("dark") ? "light" : "dark");

on your server

.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>

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

Another alternative is to load the script in the <head> element, and toggle the class on html element

To do so, you use document.documentElement.classList as that is the HTML element

Then change your CSS to

html.dark body {}

etc .. the class selector on 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>

Due to restrictions, localStorage is unavailable on stack overflow - uncomment those lines to see it work

Or - see https://jsfiddle.net/e9zg2p4c/

Upvotes: 2

Justinas
Justinas

Reputation: 43479

Store it to backend database. Then when serving HTML content put proper class/style for your elements. This will remove flickering between loading times:

<!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>

Upvotes: 0

Related Questions