Reputation: 147
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
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
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
Reputation: 177950
A little more tricky to toggle AND have a default theme
Note the localStorage calls do not work here at SO
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
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
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