Reputation: 23
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
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
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