trcharbo
trcharbo

Reputation: 23

How to make toggleClass changes persist as visitor moves between pages?

Potential coding-efficiency issues aside, I put together a jQuery function for a toggle button of mine. When clicking the toggle on and off, the site applies various CSS classes and styles for a dark mode. This works extremely well:

$(function() {
   $(".slider.round").click(function() {
     $("#canvas-wrapper").toggleClass("dark-canvas");
     $("#page-body-wrapper p").toggleClass("dark-body-text");
     $("h2, h3, .summary-title a, .summary-read-more-link, .summary-excerpt a, #block-yui_3_17_2_32_1456852631790_12688 a").toggleClass("dark-headings-links");
     $("#block-yui_3_17_2_2_1457035305182_118918 h2").toggleClass("dark-intro");
     $(".summary-item").toggleClass("dark-summary");
     $(".summary-excerpt strong, #sidebar-one h2").toggleClass("dark-excerpt-sidebar");
     $("#sidebar-one, .sqs-col-1").toggleClass("dark-sidebar");
     $(".summary-metadata-item a, .summary-metadata-item--date").toggleClass("dark-metadata");
   });
});

However, there's one big problem. Say a visitor toggles on dark mode, changing the appearance accordingly. When they reload the page or move between pages, this state resets itself every time. This is jarring when a user is navigating a dark interface, then is suddenly presented with a white-dominated design.

What could I add to this function to allow those toggleClass states to persist from page to page? Another user pointed me towards localStorage and sessionStorage. Unfortunately, I'm still quite a novice at jQuery and lack the knowledge to put something intricate together - much less make sense of related terminology.

Could anybody please lend a hand with this? It would be a huge help and step forward!

Thanks a million guys, you're rockstars :)

Upvotes: 1

Views: 550

Answers (2)

frodo2975
frodo2975

Reputation: 11725

The issue you're having is a symptom of a lack of separation between your data layer and your view layer. Instead of directly changing your html when the user clicks the toggle, you want to update your data layer, then rerender your view. Ideally, you'd be using some kind of javascript templating to render your html, like lodash's template function, or a framework like angular/react. However, I've kept the example just using jquery. The flow would look something like this:

Initial page load: Data -> View

Toggle: Update Data -> Rerender View

So you keep track of your dark mode in a variable, then update your html when it changes:

var localStorageKey = 'appState';
var appState;

function loadState() {
    const json = localStorage[localStorageKey];

    if (json) appState = JSON.parse(json);
    else appState = {};
}

function saveState() {
    localStorage[localStorageKey] = JSON.stringify(appState);
}

function render() {
    $('body').toggleClass('mode--dark', state.isDarkMode);
}

loadState();
render();

$('.darkModeToggle').click(function() {
    appState.isDarkMode = !appState.isDarkMode;
    saveState();
    render();
});

Upvotes: 1

Jiri
Jiri

Reputation: 115

I recommend that you simplify your code right in the CSS file. It's absolutely unnecessary for you to be adding a gazillion classes to each and every element using JavaScript.

Instead, you can work with one class, let's call it "mode--dark", and we'll be adding it to the body.

In your CSS, this class can look like this:

.mode--dark #canvas-wrapper {
 // your styles
}

.mode--dark #page-body-wrapper p {
 // your styles
}


//--------------------> right after body script start (this should go right after <body> tag
function getMode() {
    // let's check whether key "mode" exists in our localStorage and if so return true, false otherwise
    return localStorage.getItem('mode') == 'true' ? true : false;
}

var body = document.querySelector('body'),
      modeClass = 'mode--dark',
      switchClass = 'js-switch'; // add any class name to your input for switching between themes and retype it here


if (getMode()) {
    body.classList.add(modeClass);
}

document.addEventListener('change', function (e) {
    var target = e.target;

    if (target.className.indexOf(switchClass) > -1) {
        if(!getMode()) {
            body.classList.add(modeClass);
            // set dark mode to true
            localStorage.setItem('mode', true);
        } else {
            body.classList.remove(modeClass);
            // set dark mode to false
            localStorage.setItem('mode', false);
        }
    }
});

//--------------------> right after body script end

//--------------------> this can be put anywhere start
document.addEventListener('DOMContentLoaded', function (e) {
    var switchInput = document.querySelector('.' + switchClass);

    if(getMode()) {
        switchInput.checked = true;
    }
});
//--------------------> this can be put anywhere end

Upvotes: 1

Related Questions