Switch the design and save it in the browser's cache

Exploring the possibility of having two themes, "dark" and "light," that toggle when a checkbox is clicked.

To implement the theme change, I used the following JavaScript code:

document.documentElement.setAttribute('data-theme', 'dark');

While this code successfully changes the theme, I aim to persist this change using local storage. This way, the theme remains the same even after reloading the page.

Take a look at the code snippet below:

checkBox.addEventListener('change', function () {
    if(this.checked) {

        document.documentElement.setAttribute('data-theme', 'dark');
        localStorage.setItem( 'data-theme', 'dark');   
    }
    else {
        document.documentElement.setAttribute('data-theme', 'light');
        localStorage.setItem('data-theme', 'light');
    }
})

Are there any mistakes in my implementation, or is there something I am overlooking?

Answer №1

Perhaps you could attempt it in this manner:

let checkbox = document.getElementById('cb');

let theme = window.localStorage.getItem('data-theme');
if(theme) document.documentElement.setAttribute('data-theme', theme);
checkbox.checked = theme == 'dark' ? true : false;

checkbox.addEventListener('change', function () {
  if(this.checked){
    document.documentElement.setAttribute('data-theme', 'dark');
    window.localStorage.setItem('data-theme', 'dark');
  } else {
    document.documentElement.setAttribute('data-theme', 'light');
    window.localStorage.setItem('data-theme', 'light');
  }
});
<input id="cb" type="checkbox" />
<label for="cb">Checkbox</label>

Answer №2

Hey team, here's the plan:

First, make sure to include this .js file in your project:

export class ThemeManager {
    'use-strict';
    /**
     * ThemeManager class object constructor
     * @param {string} themeToggle - element that changes website theme on click
     * @param {string} theme - light for initial theme light, dark for dark theme
     */
    constructor(themeToggle, theme = 'light') {
        // get theme toggle element
        if (!themeToggle) {
            console.error(`A valid DOM element must be passed as the themeToggle. You passed ${themeToggle}`);
            return;
        }
        this.themeToggle = themeToggle;
        this.themeToggle.addEventListener('click', () => this.switchTheme());

        // set initial theme and apply
        this.theme = theme;
        if (localStorage.getItem('data-theme')) {
            if (localStorage.getItem('data-theme') === (theme === 'light' ? 'dark' : 'light')) {
                this.theme = (theme === 'light' ? 'dark' : 'light');
            }
        }
        else if (window.matchMedia(`(prefers-color-scheme: ${(theme === 'light' ? 'dark' : 'light')})`).matches) {
            this.theme = (theme === 'light' ? 'dark' : 'light');
        }
        this._applyTheme();

        // add listener to change web theme on os theme change
        window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => {
            this.theme = (e.matches ? 'light' : 'dark');
            this._applyTheme();
        });

    }

    /**
     * Private _applyTheme sets documentElement and localStorage 'data-theme' attribute
     * changing page style based on 'data-theme'
     */
    _applyTheme = () => {
        this.themeToggle.innerHTML = (this.theme === 'light' ? 'Dark' : 'Light');
        document.documentElement.setAttribute('data-theme', this.theme);
        localStorage.setItem('data-theme', this.theme);
    }

    /**
     * switchTheme toggles website theme on themeToggle 'click'
     */
    switchTheme = () => {
        this.theme = (this.theme === 'light' ? 'dark' : 'light');
        this._applyTheme();
    }
}

Next, add this css to your pages .css file:

/* Dark/Light Mode Support Modifications
-------------------------------------------------- */
a, a:hover {
    color: var(--link-white-color);
}

.dropdown-menu {
    background: var(--bg-color);
}

.dropdown-item {
    color: var(--link-white-color);
}

hr {
    background-color: var(--link-white-color);
    height: 1px;
    border: 0;
}

/* Dark/Light Mode Support
-------------------------------------------------- */
/*Light*/
:root {
    --font-color: #000;
    --link-color: #1C75B9;
    --link-white-color: #000;
    --bg-color: rgb(243,243,243);
}
/*Dark*/
[data-theme="dark"] {
    --font-color: #c1bfbd;
    --link-color: #0a86da;
    --link-white-color: #c1bfbd;
    --bg-color: #333;
}

Finally, in your html file, simply add this:

<html>
    <head>
        <title>your_title</title>
        <link rel="stylesheet" href="path/to/your/css">
    </head>
    <body>
        <button id="themeToggle"></button>
        <script type="module" src="path/to/ThemeManager.js"></script>
        <script type="module">
            import {ThemeManager} from 'path/to/ThemeManager.js';
            new ThemeManager(document.getElementById('themeToggle'));
        </script>
    </body>
</html>

Answer №3

In order to implement these solutions, follow these steps:

  • Retrieve the previously set value from localStorage
  • Locate the checkbox element
  • Set the checkbox based on the previous localStorage value, defaulting to false if not set

Finally, implement the set and unset logic:

var storedTheme = window.localStorage.getItem('dark-theme'); // Retrieve the previously set value
var themeCheckBox = document.getElementById('themeCheck'); // Locate the checkbox element
themeCheckBox.checked = (storedTheme == "true")? true : false; // Set the checkbox based on previous value, defaulting to false

function myFunction() { // Triggered on checkbox value change
   if(themeCheckBox.checked) {
      document.documentElement.setAttribute('dark-theme', 'dark');
      window.localStorage.setItem('dark-theme', true);
   } else {
      document.documentElement.setAttribute('dark-theme', 'light');
      window.localStorage.setItem('dark-theme', false);
   }
}
<label for="themeCheck">
  <input type="checkbox" id="themeCheck" onchange="myFunction()" /> Dark Theme
</label>

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

Utilizing a flexbox container within a table container, implementing PRE tags for handling overflow

Currently, I am diligently working on an intricate DASHBOARD utilizing bootstrap-4. A peculiar issue arose with the implementation of <pre> within a Flexbox. However, upon closer inspection, it became evident that this was not a flexbox or <pre> ...

The drop-down menu is failing to display the correct values in the second drop-down

Having trouble with the update feature in this code. There are 2 textboxes and 2 dropdowns, but when selecting a course code, the corresponding values for the subject are not being posted. Can anyone assist? view:subject_detail_view <script type="te ...

What causes parameters to be initially passed as undefined when sending them from one component to another, only to later arrive with the actual data intact

When passing properties from a parent component to a child component, my code gets executed before the properties arrive. This results in an error stating "Cannot read properties of undefined (reading 'map')". Why does this occur? https://i.ssta ...

Floating division element above responsive divisions

element, I am in the process of developing a straightforward online card interface. In this interface, there will be a user profile picture displayed above some details about that user. However, to achieve this layout, the profile picture must appear hove ...

Unused function in Vue error compilation

I am facing an issue with the compiler indicating that the function 'show' is defined but never used. The function show is being used in HTML like this: <b-card> <div id="draw-image-test"> <canvas id="can ...

Efficiently organizing reducers into separate files in ReactJS and merging them together

My App is a simple counter app where buttons are images with their own counters. https://i.stack.imgur.com/qkjoi.png In my App.js file, I imported the reducer for the counters using the following code: import reducer from './reducers/reducerCounter&a ...

CSS navigation issue

When using the third example provided on this page: http://simple-navigation-demo.andischacke.com/ I encountered an issue where the main page would display an empty div on the left instead of the content filling the entire area. However, when navigating ...

Using NodeJS and EJS to Display MySQL Query Results

I am currently working on a project where I need to display data retrieved from a MySQL query in an HTML table. However, when I attempt to do so, I encounter the following output: [object Object],[object Object],[object Object],[object Object],[object Obj ...

Utilizing props to define the background-color of the '&:hover' state

I'm adapting Material UI's Chip component to accept custom values for the colors prop, rather than being limited to "primary" and "secondary". Additionally, I want the chip to exhibit a hover effect when clicked, transitioning to a different colo ...

Creating customized JQuery UI tabs to perfectly accommodate the available horizontal space

Can JQuery UI Tabs be modified to fill the horizontal space and stack to the left? In order to achieve this, could we consider using a markup like the one below: <table width="100%"> <tr> <td> ...content... </td> ...

Save the value of a promise in a variable for future use in state management within a React-Native application

let storage = AsyncStorage.getItem('@location_data').then(data => data) const MainApp = () => { let [currentLocation, setCurrentLocation] = useState(storage); The current situation is that the storage variable holds a promise. How can ...

Unable to find the Popper component in Material UI

Material-UI version "@material-ui/core": "^3.7.0" I have a requirement to display Popper on hover over an element, but unfortunately, the Popper is not visible. This section serves as a container for the Popper component. import PropTypes from &apos ...

Looking to modify the contents of a shopping cart by utilizing javascript/jQuery to add or remove items?

I'm dealing with a challenge on my assignment. I've been tasked with creating a Shopping Cart using Javascript, HTML5, and JQuery. It needs to collect all the items from the shop inside an Array. While I believe I have most of it figured out, I a ...

Unable to activate the on('click') event when the button is loaded via AJAX

I am facing an issue with the on('click') event. I have a button that is loaded dynamically via ajax and has a click event attached to it. However, when I try clicking it, nothing happens even though the expected output should be showing an alert ...

Get the page downloaded before displaying or animating the content

Is there a method to make a browser pause and wait for all the content of a page to finish downloading before displaying anything? My webpage has several CSS3 animations, but when it is first loaded, the animations appear choppy and distorted because the ...

"Exploring the creation of multidimensional arrays in Arduino - what steps should I

Is there a way to create a multidimensional array in Arduino? I want something like this. C++ var arr = { name: "John", age: "51", children: [ "Sara", "Daniel" ] }; or maybe like this. JSON ...

Guide to sending a POST request from within my application

I'm facing an issue with using $resource for making a post request in my app. Here is the code snippet from my Angular module: angular.module('myApp').factory('Product', ['$resource', function ($resource) { ...

Element failing to adhere to CSS grid layout

I am currently working with grid layout and aiming to position the following content at the bottom and center, but it is currently aligning to the left: .body { margin: 0; background-color: rgb(255, 237, 237); display: grid; grid-template-rows: ...

Any tips for filtering an array within an array of objects using the filter method?

I have an array of products and models that I am currently filtering based on 'id' and 'category'. var app = angular.module("myApp", []); app.controller("myCtrl", function($scope) { $scope.products = [{ 'id': 1, ...

Customizing font sizes for individual fonts within the same font family

I am designing a website in a language other than English. This means that the text on the webpage will be a mixture of English and other languages. In order to make sure the text displays correctly, I have set the font-family like this: p{ font-family: ...