0

I have a toggle button that switches between light / dark mode.

When toggled off, the "mode" local storage key is set to light, when on, "mode" is set to "dark-mode". Or at least that is what is supposed to happen. What ACTUALLY happens is that "mode" is set to light, and never changes to "dark-mode" (I'm monitoring this in the browser dev console).

When the button is toggled, the page does switch between dark and light mode, the problem is that when the page is refreshed, it defaults to light mode due to the issue described above.

My code:

$(document).ready(function() {
  const checkbox = document.getElementById("checkbox")
  checkbox.addEventListener("change", () => {
    document.body.classList.toggle("dark-mode")
    if (document.body.classList == "dark-mode") {
      localStorage.setItem('mode', 'dark-mode');
    } else {
      localStorage.setItem('mode', 'light');
    }
  });

  let mode;
  mode = localStorage.getItem('mode');

  if (mode === 'light') {
    lightMode();
  } else {
    darkMode();
  }

  function darkMode() {
    document.body.classList.add("dark-mode");
    localStorage.setItem('mode', 'dark-mode');
    mode = localStorage.getItem('mode');
  }

  function lightMode() {
    document.body.classList.remove("dark-mode");
    localStorage.setItem('mode', 'light');
    mode = localStorage.getItem('mode');
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<input type="checkbox" class="checkbox" id="checkbox">
<label for="checkbox" class="checkbox-label">
    <i class="fas fa-moon"  title="Dark Mode"></i>
    <i class="fas fa-sun" title="Light Mode"></i>
    <span class="ball"  title="Toggle Light / Dark Mode"></span>
</label>

Thanks in advance for your help!

6
  • 4
    Is there a single class set on your <body> element? Otherwise the check document.body.classList == "dark-mode" will never be true, you probably want document.body.classList.contains("dark-mode") instead
    – Kaiido
    Commented May 27 at 0:47
  • 2
    @Phil they don't use a strict equality so yes... it can be the same. (I made the same misreading as you at first)
    – Kaiido
    Commented May 27 at 0:50
  • 1
    @Kaiido ah, it has a toString() override
    – Phil
    Commented May 27 at 0:51
  • Yes, <body> does have two classes assigned initially. I've tried document.body.classList.contains("dark-mode") already, but doesn't change anything unfortunately. Commented May 27 at 0:56
  • 1
    @AndrewRout Is there any code initializing the input on page load from local storage data?
    – LogiStack
    Commented May 27 at 0:59

2 Answers 2

1

Prefer something like this way:

HTML

<label>
  <input type="checkbox" id="chkBxLightDarkMode">
  <i class="fas fa-moon"  title="Dark Mode"></i>
  <i class="fas fa-sun"   title="Light Mode"></i>
</label>

CSS

body           { background-color: #add8e6; } /* lightblue */
body.dark-mode { background-color: #091d3b; }

#chkBxLightDarkMode         { appearance: none; }
#chkBxLightDarkMode ~ i.fas { font-size : 32px; }
#chkBxLightDarkMode:checked       ~ i.fa-sun,
#chkBxLightDarkMode:not(:checked) ~ i.fa-moon { display : none; }

#chkBxLightDarkMode ~ i.fa-moon { color: yellow; }
#chkBxLightDarkMode ~ i.fa-sun  { color: gold;   }

JS

const chkBxLightDark = document.querySelector('#chkBxLightDarkMode')

chkBxLightDark.checked = (localStorage.getItem('mode') === 'dark-mode');
check4DarkMode();

chkBxLightDark.addEventListener('change', check4DarkMode );

function check4DarkMode()
  {
  document.body.classList.toggle('dark-mode', chkBxLightDark.checked )
    ? localStorage.setItem('mode', 'dark-mode')
    : localStorage.setItem('mode', 'light')  
  }
4
  • Why click and not change? Pretty sure you'll miss events from the <label> with this
    – Phil
    Commented May 27 at 1:40
  • @Phil I've always seen code with click as an event... I'm probably wrong, but it seems to me that the change has become obsolete?, otherwise the event input also remains. But anyway with the click, it works perfectly, do the test! Commented May 27 at 2:01
  • Note that OP has <label for="checkbox"> in their code which means that clicking the label can be used to change the checkbox state regardless of DOM hierarchy. And the change event has most certainly not become "obsolete"
    – Phil
    Commented May 27 at 2:03
  • That aside, good answer 👍. Simpler is better
    – Phil
    Commented May 27 at 2:05
0

This happens because the code is missing updating the checkbox initialization after the page load.

You can fix this by checkbox.setAttribute('checked', ''); following after #17. lightMode();

    if (mode === 'light') {
      lightMode();
      checkbox.setAttribute('checked', ''); // New code
    } else {
      darkMode();
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.