Continual dark mode throughout page navigation with the ability to switch between light and dark modes

I've been experimenting with creating a persistent dark mode feature for web pages. My goal is to remember the user's preference as they navigate between pages. Additionally, I included a toggle button for switching between dark and light modes to enhance the user experience. Here is the code I came up with:

<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#002f30">
<style>

body {
  padding: 25px;
  color: black;
  font-size: 25px;
}

.btn {
    background: white;
    border: 1px solid #10101c;
    color: #10101c;
    border-radius:0.5rem;
}

a { 
    text-decoration: none;
    color: #006262;
    border-bottom: 1px dotted #192841;
    font-style: italic;
}

body.dark {
  background-color: #10101c;
  color: #778899;
}

body.dark .btn {
    background: #10101c;
    color: #708090;
    border: 1px solid #002e43;
    border-radius:0.5rem;
}
body.dark a { 
    text-decoration: none;
    color: #778899;
    border-bottom: 1px dotted #d5c4a1;
    font-style: italic;
}

</style>

</head>
<body>
<h1 style="margin-top: 0">page 1</h1>
<h2 style="margin-top: 0">Toggle Dark/Light Mode</h2>
<p>Click the button to switch between dark and light modes on this page.</p>
<p>Here is a link to a second page <a href="test2.html">click</a></p>
<button id="mode" class="btn" onclick="toggle()">Toggle dark mode</button>

<script>
function toggle() {
// This part handles toggling between dark and light modes
    let element = document.body;
    element.classList.toggle("dark");

    // This section manages the changing text inside the button 
    var toggle = document.getElementById("mode");
      if (toggle.innerHTML === "Toggle dark mode") {
       toggle.innerHTML = "Dark mode it is";
     } 
       else {
       toggle.innerHTML = "Toggle dark mode"; }

    // The following code was adapted from a tutorial on dark mode switches by Kevin Powell, aiming to maintain persistence but still troubleshooting...

    // check for saved 'darkMode' in localStorage
    let darkMode = localStorage.getItem('darkMode'); 

    const darkModeToggle = document.querySelector('#mode');

    const enableDarkMode = () => {
      // 1. Add the class to the body
      document.body.classList.add('dark');
      // 2. Update darkMode in localStorage
      localStorage.setItem('darkMode', 'enabled');
    }

    const disableDarkMode = () => {
      // 1. Remove the class from the body
      document.body.classList.remove('dark');
      // 2. Update darkMode in localStorage 
      localStorage.setItem('darkMode', null);
    }

    // If the user previously enabled darkMode
    // start off with it already active
    if (darkMode === 'enabled') {
      enableDarkMode();
    }

    // When the button is clicked
    darkModeToggle.addEventListener('click', () => {
      // get their darkMode setting
      darkMode = localStorage.getItem('darkMode'); 

      // if it's not currently enabled, enable it
      if (darkMode !== 'enabled') {
        enableDarkMode();
      // if it has been enabled, turn it off  
      } else {  
        disableDarkMode(); 
      }
});

    // This snippet updates the meta theme color when the dark or light mode changes
    var meta = document.querySelector("meta[name=theme-color]");

  if (meta.getAttribute("content") === "#002f30") {
    console.log(meta.getAttribute("content"));
    meta.setAttribute("content", "#10101c");
  } else {
    console.log(meta.getAttribute("content"));
    meta.setAttribute("content", "#002f30");
  }
}

</script>

</body>
</html>

When testing the code, everything seems to work fine except for the persistence aspect. If dark mode is activated and I move to another page within the same HTML setup, it reverts back to the default light mode. Am I implementing the code correctly?

P.S. I'm relatively new to Javascript

Answer №1

// For security reasons, using a local variable for storage instead of localStorage operations.

let storage = {darkMode: "disabled"}

function userPrefersDarkMode() {
  //return localStorage.getItem("darkMode") === "enabled";
  return storage.darkMode === "enabled";
}

function setThemePreference(value) {
  // localStorage.setItem("darkMode", value || "disabled");
  storage.darkMode = value || "disabled";
}

const enableDarkMode = () => {
  // 1. Add the class to the body
  document.body.classList.add("dark");
};

const disableDarkMode = () => {
  // 1. Remove the class from the body
  document.body.classList.remove("dark");
};


function setTheme() {
  // If the user has enabled darkMode in the past
  // start with it on
  if (userPrefersDarkMode()) {
    enableDarkMode();
  } else {
    disableDarkMode();
  }
  const appDiv = document.getElementById("app");
  appDiv.innerHTML = `<h1>Dark mode: ${userPrefersDarkMode()}</h1>`;
}

function bootstrap() {
     const darkModeToggleButton = document.querySelector("#mode");
darkModeToggleButton.addEventListener("click", () => {
  if (userPrefersDarkMode()) {
    setThemePreference("disabled");
    disableDarkMode();
  } else {
    setThemePreference("enabled");
    enableDarkMode();
  }
  const appDiv = document.getElementById("app");
  appDiv.innerHTML = `<h1>Dark mode: ${userPrefersDarkMode()}</h1>`;
});

setTheme();
}

 document.addEventListener("DOMContentLoaded", function(event) {
     // Your code to run since DOM is loaded and ready
     bootstrap()
  });
Live Demo

<!DOCTYPE html>
<html>
   <head>
      <title>This is document title</title>
      <style>
      body {
  padding: 25px;
  color: black;
  font-size: 25px;
}

.btn {
background: white;
border: 1px solid #10101c;
color: #10101c;
border-radius:0.5rem;
}

a { 
text-decoration: none;
color: #006262;
border-bottom: 1px dotted #192841;
font-style: italic;
}

body.dark {
  background-color: #10101c;
  color: #778899;
}

body.dark .btn {
background: #10101c;
color: #708090;
border: 1px solid #002e43;
border-radius:0.5rem;
}
body.dark a { 
text-decoration: none;
color: #778899;
border-bottom: 1px dotted #d5c4a1;
font-style: italic;
}
      </style>
   </head>
   <body>
      
<div id="app">hello</div>

<button id="mode" class="btn">Toggle dark mode</button>
   </body>
</html>

The check for stored preference is now wrapped in the toggle method. This ensures that the value from localstorage is read only when the toggle method is called on the second page.

Check out the StackBlitz demo

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the method to incorporate the current time into a date object and obtain its ISO string representation?

I'm using a ngbDatePicker feature to select a date, which then returns an object structured like this: {year:2020, month:12, day:03} My goal is to convert this date into an ISOString format with the current time. For example, if the current time is 1 ...

The use of "app.use("*") appears to be triggering the function repeatedly

app.use("*", topUsers) is being invoked multiple times. The function topUsers is called repeatedly. function topUsers(req, res, next){ console.log("req.url", req.url) findMostUsefullReviewsAggregate(6) .then(function(aggregationResult){ ...

Can passing parameters between nested map functions cause any issues?

While attempting to navigate to a page in reactjs and pass parameters using the useNavigate hook, I encounter an unexpected token error as soon as I include the navigation within the anchor tag. <a onClick={() ={ ...

Receiving the result as well as encountering undefined initially during AJAX request

I have a dropdown menu, and when a user selects an option, an AJAX call is made to retrieve and display data based on the selected value. If the dropdown value is 2, it triggers the AJAX request. However, I am encountering two issues: https://i.sstatic.n ...

Error TS2307: Module './tables.module.css' or its type declarations could not be located

Currently utilizing CSS modules within a create-react-app project and encountering an error within Google Chrome browser: https://i.stack.imgur.com/0ItNM.png However, the error appears to be related to eslint because I am able to close the warning modal i ...

"Ensure div remains at the bottom of the page even while

In order to implement a feature where the menu sticks to the top when scrolling down, you can use the following JS code. You can view a live example of this functionality on this Plunker by widening the preview window to see the columns side by side. wi ...

Difficulties encountered when initiating CRA using npm start

Hi everyone! I'm dealing with a frustrating issue; every time I try to run npm start I keep encountering the error message below: events.js:288 throw er; // Unhandled 'error' event ^ Error: spawn cmd ENOENT To resolve this probl ...

Communication between nodes using serial ports in Node.js fails to receive a response from the connected Arduino board

I've been attempting to establish communication between a computer and an Arduino board using node.js. Despite having a simple program, it just doesn't seem to work as expected. The Arduino program (which seems to be working fine) is as follows: ...

using a route object to update the path in Nuxt

I need to navigate to a specific URL: myApp.com/search-page?name%5Bquery%5D=value The code snippet below works perfectly when I'm on the homepage myApp.com: this.$router.push({ path: "search-page", query: { name: { query: `${this.value} ...

Looking to obtain the coordinates of a draggable element?

After dragging a div around the page and submitting a form, I would like to capture its location on the page so it can render in that same spot when the page reloads. My current question is how can I capture the coordinates or location of the div after it ...

Preventing the cascading effects of past hovers with AngularJS

I am working with AngularJS and have the following HTML code snippet. <div class="row" style="margin-left:60px; text-align:center;"> <div class="col-xs-1 " style="margin-left:25px;width:10px; " ng-repeat="image_thumb_id in image_thumbnail_ ...

Defining colors in TinyCSS and GTK themes using the `@define-color

I've been working on a project that involves parsing GTK+3 themes using the TinyCSS library. However, it seems that TinyCSS is not recognizing lines such as @define-color base_color #FAFAFA; These definitions are stored in parser.styleSheets[0].er ...

When the ajax response comes in, my javascript code seems to suddenly stop

After sending a POST request, my JavaScript suddenly stops working for some unknown reason. Here's the situation: I visit my webpage, fill out the form, and then click 'Click me' : Upon clicking OK in the alert popup, I see the expected ou ...

A declaration of "import '***.css';" was located within an ECMAScript module file in the monaco-editor npm library

It's perplexing to me why the developers of monaco-editor included these statements, as they are actually causing errors in my browser such as: Failed to load module script: Expected a JavaScript module script but the server responded with a MIME typ ...

Updating button appearance when clicked using Vue.js and Tailwind CSS

As part of my learning journey, I have expanded this code to enhance my frontend skills. While I am aware that the code can be significantly shortened, I am focused on learning and broadening my experience in frontend development. The code below functions ...

I'm attempting to slightly adjust the alignment of the text to the center, but it keeps causing the text to shift

I am facing an issue where I want to slightly center text, but whenever I add just 1px of padding-left, it moves to a new line. In my layout, I have 2 columns each occupying 50% width of the screen. One column contains only a photo while the other has a ba ...

Combining and grouping objects by their IDs in a JavaScript array

Information: [ { "id": "ewq123", "name": "Joshua", "order": "Pizza" }, { "id": "ewq123", "name": "Joshua", "order": ...

Issue with displaying content in AngularJS view set by a service

I'm currently facing a challenge in accessing the view with an expression that doesn't seem to be working correctly. Here is my Service: angular.module('myApp', []).service('myService', function($timeout){ this.sayHello = ...

Next/image is encountering an error due to an invalid Element type being generated

Trying to utilize the next/image feature to display an SVG image is causing me some trouble. Every time I attempt this, an error message pops up: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite ...

Can I utilize p5.Vector.fromAngle() in Vue JS?

Incorporating P5 into a Vue application can be achieved using the following code snippet: let script = p5 => { p5.setup = _ => { this.setup(p5) } p5.draw = _ => { this.draw(p5) } } this.ps = new P5(script) While functions like ba ...