I am trying to enable a dark mode/light mode feature on a website, in which I am using the .setProperty
function to change the HEX value of a variable. However, whenever I toggle a switch, the site does not update. Why is this? Here is all the code pertaining to the issue:
let color = "#141414";
function changeMode() {
console.log("Switch was toggled. Switching site color scheme...")
let bg = document.querySelector(':root');
// Should switch the value of 'color' from grey to white, and vice versa
if (color === "#141414") { color = "#ffffff"; } else if (color === "#ffffff") { color = "#141414"; }
bg.style.setProperty("--main2", color);
}
:root {
--main1: #ffdb15;
--main2: #141414;
}
html, body {
background-color: var(--main2);
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
left: 20px;
top: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #5d00e2;
}
input:focus + .slider {
box-shadow: 0 0 1px #5d00e2;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
<label class = "switch" onclick = "changeMode()">
<input type = "checkbox" checked>
<span class = "slider round"> </span>
</label>
I am trying to enable a dark mode/light mode feature on a website, in which I am using the .setProperty
function to change the HEX value of a variable. However, whenever I toggle a switch, the site does not update. Why is this? Here is all the code pertaining to the issue:
let color = "#141414";
function changeMode() {
console.log("Switch was toggled. Switching site color scheme...")
let bg = document.querySelector(':root');
// Should switch the value of 'color' from grey to white, and vice versa
if (color === "#141414") { color = "#ffffff"; } else if (color === "#ffffff") { color = "#141414"; }
bg.style.setProperty("--main2", color);
}
:root {
--main1: #ffdb15;
--main2: #141414;
}
html, body {
background-color: var(--main2);
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
left: 20px;
top: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #5d00e2;
}
input:focus + .slider {
box-shadow: 0 0 1px #5d00e2;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
<label class = "switch" onclick = "changeMode()">
<input type = "checkbox" checked>
<span class = "slider round"> </span>
</label>
Is the problem in the color
variable, or in the program not updating the page's body itself when the value is switched?
Edit: Also, if you know of any way to improve my code, please let me know how. Specifically, is there any way to improve the method that switches the value of the color variable?
Share Improve this question asked May 20, 2021 at 1:14 BubblyBubbly 3013 silver badges12 bronze badges4 Answers
Reset to default 2Have you noticed your event is being fired twice? You put your event listener on a label with an input checkbox inside. Clicking the label makes it like clicking the checkbox. So what you can do is change the onclick to be put in the input.
let color = "#141414";
function changeMode() {
console.log("Switch was toggled. Switching site color scheme...")
let bg = document.querySelector(':root');
// Should switch the value of 'color' from grey to white, and vice versa
if (color === "#141414") {
color = "#ffffff";
} else if (color === "#ffffff") {
color = "#141414";
}
console.log(color);
bg.style.setProperty("--main2", color);
}
:root {
--main1: #ffdb15;
--main2: #141414;
}
html, body {
background-color: var(--main2);
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
left: 20px;
top: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #5d00e2;
}
input:focus + .slider {
box-shadow: 0 0 1px #5d00e2;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
<label class = "switch">
<input type = "checkbox" checked onclick="changeMode()">
<span class = "slider round"> </span>
</label>
If you look at the console while clicking the input you can see it's triggering two click events, effectively toggling it twice at once. You can add console.log(event)
in changeMode()
to see exactly what events are triggering (label click and the input click). The quick fix is to change it to trigger on the input change, rather than parent label click:
<label class = "switch">
<input onchange="changeMode()" type = "checkbox" checked>
<span class = "slider round"> </span>
</label>
Your question is actually asking How to update css variable with js?
Below is example
<script>
// Get the root element
var r = document.querySelector(':root');
// Create a function for getting a variable value
function myFunction_get() {
// Get the styles (properties and values) for the root
var rs = getComputedStyle(r);
// Alert the value of the --blue variable
alert("The value of --blue is: " + rs.getPropertyValue('--blue'));
}
// Create a function for setting a variable value
function myFunction_set() {
// Set the value of variable --blue to another value (in this case "lightblue")
r.style.setProperty('--blue', 'lightblue');
}
</script>
Add var rs = getComputedStyle(r);
and it will work fine
source: w3school
You should use getComputedStyle to get the document.documentElement
, then you can set the variable in a conditional and then set the style of the document.documentElement using setProperty.
There is a big difference between getComputedStyle(element).backgroundColor and element.style.backgroundColor. The first will actually give you the set CSS from the style sheet where as the latter will give you the inline style set in the elements HTML tag.
So you get the set style that is set using a variable in your CSS file using getComputedStyle(element) and then run that through your conditional and set the inline of the ROOT using element.style.backgroundColor where element is set to document.documentElement.
let bg = document.documentElement;
let input = document.getElementById('input')
function changeMode() {
let color = '';
currentStyle = getComputedStyle(document.documentElement).backgroundColor
currentStyle === "rgb(20, 20, 20)" ? color = "rgb(255, 219, 21)" : color = "rgb(20, 20, 20)"
bg.style.setProperty("--main2", color);
}
input.addEventListener('click', changeMode)
:root {
--main1: #ffdb15;
--main2: #141414;
}
html,
body {
background-color: var(--main2);
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
left: 20px;
top: 20px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked+.slider {
background-color: #5d00e2;
}
input:focus+.slider {
box-shadow: 0 0 1px #5d00e2;
}
input:checked+.slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
<label class="switch">
<input id="input" type="checkbox" checked>
<span class="slider round"> </span>
</label>