Remove backgroundImage on componentDidMount - javascript

I currently have a <Login/> page, and a <Dashboard/>.
The login page has a background of #222, and when you login the Dashboard has a background of whitesmoke
The way I am doing this is having this on the body css:
body {
background-color: #222222;
}
and this in the Dashboard.js:
componentWillMount() {
document.body.style.backgroundColor = "whitesmoke";
}
componentWillUnmount() {
document.body.style.backgroundColor = null;
}
Up to now, this was working. But I now have an Image as my background on the Login page, as seen here:
body {
background-color: #222222;
background: url('../../public/img/bg.png');
background-repeat: repeat;
}
but my Dashboard inherits the background image, even when I put something like this:
componentWillMount() {
document.body.style.backgroundImage = null;
document.body.style.backgroundColor = "whitesmoke";
}
componentWillUnmount() {
document.body.style.backgroundColor = null;
}
How do I get around this?
Thanks

Why not use classes instead?
componentWillMount() {
$('body').addClass('has-background');
}
componentWillUnmount() {
$('body').removeClass('has-background');
}
Also, you may want to abstract those addClass / removeClass and use emits.

Related

How to toggle True or False in sessionStorage

I am trying to add dark-mode to the website. When someone clicks the dark mode button it will add additional CSS classes and remove them when the button is clicked again.
I know I can do it easily with toggleClass but I don't want to disable this automatically when the page refreshes or some other page is opened on the website.
I have been playing with sessionStorage but unable to succeed so far I have come up with this code here:
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
$('.darkmode-button').click(function() {
if (sessionStorage.getItem('darkmode', 'true')) {
$('.header-wrap').removeClass('dark-header');
sessionStorage.setItem('darkmode', 'false');
}
if (sessionStorage.getItem('darkmode', 'false')) {
$('.header-wrap').addClass('dark-header');
sessionStorage.setItem('darkmode', 'true');
}
});
function changeText() {
var x = document.getElementById("dmbutton");
if (x.innerHTML === "Dark Mode") {
x.innerHTML = "Light Mode";
} else {
x.innerHTML = "Dark Mode";
}
}
.header-wrap {
color: black;
}
.dark-header {
color: white;
background-color: black;
}
Can someone please share a working example of how it can be achieved?
I already created a question before but it was marked duplicate with this answer. I read it all but still could not figure it out.
To do what you require simply set a single class on a parent element, the body would work well in this case, to indicate when dark mode has been turned on. You can then use this class in all the relevant selectors in your CSS to update the UI.
Regarding the session storage logic, set a boolean flag when the dark mode is updated when the button is clicked and set the class on the body based on the session storage flag when the page loads.
Putting it all together would look something like this:
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
let body = document.body;
let dmButton = document.querySelector('#dmbutton');
dmButton.addEventListener('click', e => {
body.classList.toggle('dark');
sessionStorage.setItem('darkmode', body.classList.contains('dark'));
e.target.textContent = e.target.textContent.trim() === 'Dark Mode' ? 'Light Mode' : 'Dark Mode';
});
let darkModeEnabled = JSON.parse(sessionStorage.getItem('darkmode')); // boolean type coercion
if (darkModeEnabled) {
body.classList.add('dark');
dmButton.textContent = 'Light Mode';
}
.header-wrap {
color: black;
}
body.dark {
background-color: #666;
}
body.dark .header-wrap {
color: white;
background-color: black;
}
Here's a working example in a jsFiddle, as SO snippets are sandboxed and disallow local/session storage access.
I don't know the logic of this code but it works for me which I found from this solution thanks to Stackoverflow
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
var $dark = $('.header-wrap')
if (localStorage.getItem('darkmode') === 'true') {
$dark.addClass('dark-header');
}
$('.darkmode-button').click(function() {
$dark.toggleClass('dark-header');
localStorage.setItem('darkmode', $dark.hasClass('dark-header'));
});
.header-wrap {
color: black;
}
.dark-header {
color: white;
background-color: black;

4 Javascript commands execute simultaneously when 1 button is clicked

I have four html elements on a page that are each meant to execute a different setTheme JS command when clicked. However, when I click one, nothing happens. I'm pretty sure they all execute at the same time, and because they're all basically the same code, they cancel each other out and it reverts back to the default theme, which means I see nothing. However, I could be wrong. How do I fix this?
A note about this code: I couldn't figure this out at first, so I looked online but couldn't find anything, so I modified some code meant for the toggleTheme function, not the setTheme function. If that's the problem, I don't know how to solve it but it might help you.
This is the code for one of them (the others are similar, but with, for example, 'theme-light-purple' replaced by 'theme-light-blue' or 'theme-dark-blue'. 'theme-dark-purple' is the default.):
<button id="switch-light-purple" onclick="setTheme()">Light Purple</button>
<script>
// function to set a given theme/color-scheme
function setTheme(themeName) {
localStorage.setItem('theme', themeName);
document.documentElement.className = themeName;
}
// function to set theme
function setTheme() {
setTheme('theme-light-purple');
}
// Immediately invoked function to set the theme on initial load
(function () {
if (localStorage.getItem('theme') === 'theme-light-purple') {
setTheme('theme-light-purple');
} else {
setTheme('theme-dark-purple');
}
})();
</script>
<button id="switch-light-purple" onclick="setTheme('theme-light-purple-dark')">Light Purple</button>
<script>
// function to set a given theme/color-scheme
function setTheme(themeName) {
localStorage.setItem('theme', themeName);
document.documentElement.className = themeName;
}
// Immediately invoked function to set the theme on initial load
(function () {
if (localStorage.getItem('theme') === 'theme-light-purple') {
setTheme('theme-light-purple');
} else {
setTheme('theme-dark-purple');
}
})();
</script>
You don't need to write 2 functions.
This is how you should have your code
function setTheme(themeName = 'theme-light-purple') {
localStorage.setItem('theme', themeName);
document.documentElement.className = themeName;
}
Here, you are providing a default value to your first parameter in case you want to call the setTheme function without any parameters like this setTheme()
So, if you call setTheme() it automatically means you are calling setTheme('theme-light-purple')
Here's the official documentation for default parameters if you want to go through.
If you want setTheme() to behave differently whether it receives or not an argument (and if I understood what you want to achieve), you need to replace:
// function to set a given theme/color-scheme
function setTheme(themeName) {
localStorage.setItem('theme', themeName);
document.documentElement.className = themeName;
}
// function to set theme
function setTheme() {
setTheme('theme-light-purple');
}
with:
// function to set a given theme/color-scheme or a default one if none is provided
function setTheme(themeName) {
const chosenTheme = themeName ? themeName : 'theme-light-purple';
localStorage.setItem('theme', chosenTheme);
document.documentElement.className = chosenTheme;
}
[EDIT] Following the comment, I am adding a partial demo; unfortunately the code snippet cannot access window.localStorage therefore the storing to and retrieving from localStorage of the theme cannot be demonstrated here:
/* theme default */
html.theme-default body {
color: #333;
background-color: #efefef;
}
html.theme-default button {
border: 2px solid #333;
border-radius: 3px;
color: #333;
}
html.theme-default p {
border: 1px dashed #333;
color: #333;
}
/* theme red */
html.theme-red body {
color: #300;
background-color: #ffefef;
}
html.theme-red button {
border: 2px solid #c00;
border-radius: 3px;
color: #300;
}
html.theme-red p {
border: 1px dashed #c00;
color: #300;
}
/* theme green */
html.theme-green body {
color: #030;
background-color: #efffef;
}
html.theme-green button {
border: 2px solid #0c0;
border-radius: 3px;
color: #030;
}
html.theme-green p {
border: 1px dashed #0c0;
color: #030;
}
/* theme blue */
html.theme-blue body {
color: #003;
background-color: #efefff;
}
html.theme-blue button {
border: 2px solid #00c;
border-radius: 3px;
color: #003;
}
html.theme-blue p {
border: 1px dashed #00c;
color: #003;
}
<body>
<button id="switch-default" onclick="setTheme()">default (grey)</button>
<button id="switch-red" onclick="setTheme('theme-red')">red</button>
<button id="switch-green" onclick="setTheme('theme-green')">green</button>
<button id="switch-blue" onclick="setTheme('theme-blue')">blue</button>
<p>Lorem ipsum dolor sit consecutor amet</p>
<script>
function setTheme(themeName) {
const chosenTheme = themeName ? themeName : 'theme-default';
// window.localStorage.setItem('theme', chosenTheme); // this line is commented as the code snippet has no access to localStorage
document.documentElement.className = chosenTheme;
}
// Immediately invoked function to set the theme on initial load
(function() {
/* the following lines are commented as the code snippet has no access to localStorage
const storedTheme = window.localStorage.getItem('theme') || null;
if (storedTheme) {
setTheme(storedTheme);
} else {
setTheme();
}
*/
setTheme() // this line should be deleted in the actual code
})();
</script>
</body>

Vuejs - Use variable for background-image in style template

I have got a question regarding using a variable within the background-image of the style template. Is this possible at all?
I would like to achieve something like this:
<script>
export default {
name: 'setCss',
computed: {
cssVars() {
var ua = navigator.userAgent.toLowerCase();
var isAndroid = ua.indexOf("android") > -1; //&& ua.indexOf("mobile");
var path = '';
if(isAndroid) {
path = 'img/samsung/';
} else{
path = 'img/iphone/';
}
return {
'--bg-path': path,
}
}
}
};
</script>
and then with this CSS:
<style scoped>
div {
background-image: var(--bg-path) + '/1366x400#2x.jpg';
background-size: 1366px 400px;
}
#media not all,
not all,
only screen and (max-width: 480px) {
div {
background-image: var(--bg-path) + '/480x288.jpg';
background-size: 480px 288px;
}
}
</style>
This will not work but I am wondering if there is a way to do this?
For this to work, you will simply need to pass the computed property cssVars to the component as dynamic style attribute like:
<button :style="cssVars">My button</button>
Here is a working demo:
(here I am passing a hardcoded value for bgColor, but you can assume that being passed as a prop to a component also)
new Vue({
el: "#myApp",
data: {
bgColor: "green"
},
computed: {
cssVars() {
return {
'--bg-color': this.bgColor,
}
}
}
})
button {
background-color: var(--bg-color);
padding:10px 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="myApp">
<button :style="cssVars">My button</button>
</div>
You will need to use class/style binding.
In your case, binding style would look something like the following. First, you would need to bind a style with the computed background image (the rest of the styles don't need to be bound, they can remain in your style tag).
<template>
<div :style="{backgroundImage: computedBgImage}"></div>
</template>
...
computed:{
computedBgImage() {
let path;
if (isAndroid) {
path = 'img/samsung/';
} else{
path = 'img/iphone/';
}
return path + '/480x288.jpg'
}
}
...
If you need to use the same image in multiple places, consider defining a class for each image, and binding the class, instead of the style, as explained in the above link.

How to override css prefers-color-scheme setting

I am implementing a dark mode, as macOS, Windows and iOS have all introduced dark modes.
There is a native option for Safari, Chrome, and Firefox, using the following CSS media rule:
#media (prefers-color-scheme: dark) {
body {
color:#fff;
background:#333333
}
This will automatically identify systems that are set to dark modes, and apply the enclosed CSS rules.
However; even though users may have their system set to dark mode, it may be the case that they prefer the light or default theme of a specific website. There is also the case of Microsoft Edge users which does not (yet) support #media (prefers-color-scheme. For the best user experience, I want to ensure that these users can toggle between dark and default modes for those cases.
Is there a method that this can be performed, possibly with HTML 5 or JavaScript? I'd include the code I have tried, but I haven't been able to find any information on implementing this whatsoever!
I have determined an appropriate solution, it is as follows:
CSS will use variables and themes:
// root/default variables
:root {
--font-color: #000;
--link-color:#1C75B9;
--link-white-color:#fff;
--bg-color: rgb(243,243,243);
}
//dark theme
[data-theme="dark"] {
--font-color: #c1bfbd;
--link-color:#0a86da;
--link-white-color:#c1bfbd;
--bg-color: #333;
}
The variables are then called where necessary, for example:
//the redundancy is for backwards compatibility with browsers that do not support CSS variables.
body
{
color:#000;
color:var(--font-color);
background:rgb(243,243,243);
background:var(--bg-color);
}
JavaScript is used to identify which theme the user has set, or if they have over-ridden their OS theme, as well as to toggle between the two, this is included in the header prior to the output of the html <body>...</body>:
//determines if the user has a set theme
function detectColorScheme(){
var theme="light"; //default to light
//local storage is used to override OS theme settings
if(localStorage.getItem("theme")){
if(localStorage.getItem("theme") == "dark"){
var theme = "dark";
}
} else if(!window.matchMedia) {
//matchMedia method not supported
return false;
} else if(window.matchMedia("(prefers-color-scheme: dark)").matches) {
//OS theme setting detected as dark
var theme = "dark";
}
//dark theme preferred, set document with a `data-theme` attribute
if (theme=="dark") {
document.documentElement.setAttribute("data-theme", "dark");
}
}
detectColorScheme();
This javascript is used to toggle between the settings, it does not need to be included in the header of the page, but can be included wherever
//identify the toggle switch HTML element
const toggleSwitch = document.querySelector('#theme-switch input[type="checkbox"]');
//function that changes the theme, and sets a localStorage variable to track the theme between page loads
function switchTheme(e) {
if (e.target.checked) {
localStorage.setItem('theme', 'dark');
document.documentElement.setAttribute('data-theme', 'dark');
toggleSwitch.checked = true;
} else {
localStorage.setItem('theme', 'light');
document.documentElement.setAttribute('data-theme', 'light');
toggleSwitch.checked = false;
}
}
//listener for changing themes
toggleSwitch.addEventListener('change', switchTheme, false);
//pre-check the dark-theme checkbox if dark-theme is set
if (document.documentElement.getAttribute("data-theme") == "dark"){
toggleSwitch.checked = true;
}
finally, the HTML checkbox to toggle between themes:
<label id="theme-switch" class="theme-switch" for="checkbox_theme">
<input type="checkbox" id="checkbox_theme">
</label>
Through the use of CSS variables and JavaScript, we can automatically determine the users theme, apply it, and allow the user to over-ride it as well. [As of the current time of writing this (2019/06/10), only Firefox and Safari support the automatic theme detection]
Not sure, why all answers are so complicated.
Use CSS variables, set a default value, and an opposite value in a media query, as usual. Also set the values in two classes. Implement a toggle that toggles these classes when clicked.
By default, automatic light/dark mode is used based on the system color scheme. Using the toggle switches to manual light/dark mode. It returns to automatic light/dark mode after refreshing the page (or removing the class from the html element).
// toggle to switch classes between .light and .dark
// if no class is present (initial state), then assume current state based on system color scheme
// if system color scheme is not supported, then assume current state is light
function toggleDarkMode() {
if (document.documentElement.classList.contains("light")) {
document.documentElement.classList.remove("light")
document.documentElement.classList.add("dark")
} else if (document.documentElement.classList.contains("dark")) {
document.documentElement.classList.remove("dark")
document.documentElement.classList.add("light")
} else {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add("dark")
} else {
document.documentElement.classList.add("light")
}
}
}
/* automatic/manual light mode */
:root, :root.light {
--some-value: black;
--some-other-value: white;
}
/* automatic dark mode */
/* ❗️ keep the rules in sync with the manual dark mode below! */
#media (prefers-color-scheme: dark) {
:root {
--some-value: white;
--some-other-value: black;
}
}
/* manual dark mode
/* ❗️ keep the rules in sync with the automatic dark mode above! */
:root.dark {
--some-value: white;
--some-other-value: black;
}
/* use the variables */
body {
color: var(--some-value);
background-color: var(--some-other-value);
}
<button onClick="toggleDarkMode()">Toggle</button>
<h1>Hello world!</h1>
You can use my custom element <dark-mode-toggle> that initially adheres to the user's prefers-color-scheme setting, but that also allows the user to (permanently or temporarily) override it. The toggle works both with separate CSS files or with classes that are toggled. The README has examples for both approaches.
Here's an answer that respects the default prefers-color-scheme, and only then lets you toggle via localStorage. This assumes CSS does it faster than JS plus people will use the default scheme even without JS.
I don't like having to declare a default style and then re-declaring it as a standalone class called, but it's unavoidable. I at least used :root to avoid duplicated values.
Note this forum seems to block localStorage so you have to try the code somewhere else.
var theme, prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
if (prefersDarkScheme.matches)
theme = document.body.classList.contains("light-mode") ? "light" : "dark";
else
theme = document.body.classList.contains("dark-mode") ? "dark" : "light";
localStorage.setItem("theme", theme);
function toggle() {
var currentTheme = localStorage.getItem("theme");
if (currentTheme == "dark")
document.body.classList.toggle("light-mode");
else if (currentTheme == "light")
document.body.classList.toggle("dark-mode");
}
:root {
--text-for-light: black;
--bkg-for-light: white;
--link-for-light: green;
--text-for-dark: white;
--bkg-for-dark: black;
--link-for-dark: DeepSkyBlue;
}
body {color: var(--text-for-light); background-color: var(--bkg-for-light);}
a {color: var(--link-for-light);}
.dark-mode {color: var(--text-for-dark); background-color: var(--bkg-for-dark);}
.dark-mode a {color: var(--link-for-dark);}
.light-mode {color: var(--text-for-light); background-color: var(--bkg-for-light);}
.light-mode a {color: var(--link-for-light);}
#media (prefers-color-scheme: dark) {
body {color: var(--text-for-dark); background-color: var(--bkg-for-dark);}
a {color: var(--link-for-dark);}
}
<button onclick="toggle()">Toggle Light/Dark Mode</button>
<p> </p>
Test link
If you want just the auto detect part without the toggle button:
:root {
--text-for-light: black;
--bkg-for-light: white;
--link-for-light: green;
--text-for-dark: white;
--bkg-for-dark: black;
--link-for-dark: DeepSkyBlue;
}
body {color: var(--text-for-light); background-color: var(--bkg-for-light);}
a {color: var(--link-for-light);}
#media (prefers-color-scheme: dark) {
body {color: var(--text-for-dark); background-color: var(--bkg-for-dark);}
a {color: var(--link-for-dark);}
}
Test link
TL;DR
index.html
<!DOCTYPE html>
<html>
<head>
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<h1>Hello world</h1>
<button id="toggle">Toggle</button>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
style.css
.dark-mode {
background-color: black;
color: white;
}
.light-mode {
background-color: white;
color: black;
}
#media (prefers-color-scheme: dark) {
body {
background-color: black;
color: white;
}
}
script.js
/**
* Adopt:
* the theme from the system preferences; or
* the previously stored mode from the `localStorage`
*/
var initialMode = "light";
var prefersColorSchemeDark = window.matchMedia( "(prefers-color-scheme: dark)" );
if ( prefersColorSchemeDark.matches ) {
initialMode = "dark";
}
if( localStorage.getItem("initialMode") == null ) {
localStorage.setItem("initialMode", initialMode);
}
if( localStorage.getItem("currentMode") == null ) {
localStorage.setItem("currentMode", initialMode);
} else {
let currentMode = localStorage.getItem("currentMode");
if ( currentMode == "dark" && currentMode != initialMode ) {
document.body.classList.add("dark-mode");
} else if ( currentMode == "light" && currentMode != initialMode ) {
document.body.classList.add("light-mode");
}
}
/**
* Process the toggle then store to `localStorage`
*/
document.getElementById('toggle').addEventListener("click", function() {
var initialMode = localStorage.getItem("initialMode");
let currentMode = localStorage.getItem("currentMode");
if ( currentMode == "dark" && currentMode == initialMode ) {
document.body.classList.add("light-mode");
localStorage.setItem("currentMode", "light");
} else if ( currentMode == "light" && currentMode == initialMode ) {
document.body.classList.add("dark-mode");
localStorage.setItem("currentMode", "dark");
} else if ( currentMode != initialMode ) {
document.body.removeAttribute("class");
if( currentMode == "dark" ) {
localStorage.setItem("currentMode", "light");
} else {
localStorage.setItem("currentMode", "dark");
}
}
},
false);
Details
This solution assumes that:
Whatever was set on the system preferences (dark/light mode), that will be acknowledged as the initial mode
From the initial mode, the end-user then can toggle manually either dark mode or light mode
If the system does not have a dark mode feature, the light mode theme will be used
Whatever the theme (dark/light mode) that the end-user manually set previously, that will be the new initial mode on the next page reload/refresh
Took the solution provided by #JimmyBanks and 1) turned the checkbox into a toggling text button, and 2) added automatic theme switching on OS theme change.
CSS is unchanged, with light themes stored in the :root and dark themes stored under [data-theme="dark"]:
:root {
--color_01: #000;
--color_02: #fff;
--color_03: #888;
}
[data-theme="dark"] {
--color_01: #fff;
--color_02: #000;
--color_03: #777;
}
The <head> JS has some edits, including a few omissions and the move of the data-theme statement to the subsequent JS block:
var theme = 'light';
if (localStorage.getItem('theme')) {
if (localStorage.getItem('theme') === 'dark') {
theme = 'dark';
}
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme = 'dark';
}
And here is the edit to the second block of JS, plus related HTML. theme_switch toggles the theme, while theme_OS automatically updates the site's theme with changes to the OS theme.
var theme;
function theme_apply() {
'use strict';
if (theme === 'light') {
document.getElementById('theme_readout').innerHTML = 'Dark';
document.documentElement.setAttribute('data-theme', 'light');
localStorage.setItem('theme', 'light');
} else {
document.getElementById('theme_readout').innerHTML = 'Light';
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
}
}
theme_apply();
function theme_switch() {
'use strict';
if (theme === 'light') {
theme = 'dark';
} else {
theme = 'light';
}
theme_apply();
}
var theme_OS = window.matchMedia('(prefers-color-scheme: light)');
theme_OS.addEventListener('change', function (e) {
'use strict';
if (e.matches) {
theme = 'light';
} else {
theme = 'dark';
}
theme_apply();
});
<a onclick="theme_switch()">Theme: <span id="theme_readout"></span></a>
Please let me know if you have any suggestions for improvement!
None of the above suited me. I decided to approach the problem from a different perspective. Year is 2021.
The following offers:
System preferences respect.
System preferences overwrite.
Scrollbars color scheme respect.
Universal browser support. (IE end of life, August 17th 2021 🥳✌️🎉)
When you take a look at the MDN Web Docs page for prefers-color-scheme you can read the following:
The prefers-color-scheme CSS media feature is used to detect if the user has requested a light or dark color theme.
[...]
light Indicates that user has notified that they prefer an interface that has a light theme, or has not expressed an active preference.
So for any browsers, by default, the prefers-color-scheme is either set to light or isn't supported.
One of the problem I had with the accepted answer was that the changes were not affecting the scrollbar color. This can be handle using the color-scheme CSS property coupled to the :root pseudo element.
The other problem I had was that, If a user was to change the system settings to light or dark, the website wouldn't be affeted by it and would generate miss-matches between both styles. We can fix that behaviour by coupling window.matchMedia( '(prefers-color-scheme: light)' ) to a onchange event listener.
Here is the final script.
(() => {
var e = document.getElementById("tglScheme");
window.matchMedia("(prefers-color-scheme: dark)").matches
? (document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:dark}</style>'),
document.body.classList.add("dark"),
e && (e.checked = !0),
window.localStorage.getItem("scheme") &&
(document.getElementById("scheme").remove(), document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:light}</style>'), document.body.classList.remove("dark"), e && (e.checked = !1)),
e &&
e.addEventListener("click", () => {
e.checked
? (document.getElementById("scheme").remove(),
document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:dark}</style>'),
document.body.classList.add("dark"),
localStorage.removeItem("scheme"))
: (document.getElementById("scheme").remove(),
document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:light}</style>'),
document.body.classList.remove("dark"),
localStorage.setItem("scheme", 1));
}))
: (document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:light}</style>'),
e && (e.checked = !1),
window.localStorage.getItem("scheme") &&
(document.getElementById("scheme").remove(), document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:dark}</style>'), document.body.classList.add("dark"), e && (e.checked = !0)),
e &&
e.addEventListener("click", () => {
e.checked
? (document.getElementById("scheme").remove(),
document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:dark}</style>'),
document.body.classList.add("dark"),
localStorage.setItem("scheme", 1))
: (document.getElementById("scheme").remove(),
document.head.insertAdjacentHTML("beforeend", '<style id="scheme">:root{color-scheme:light}</style>'),
document.body.classList.remove("dark"),
localStorage.removeItem("scheme"));
}));
})(),
window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => {
location.reload(), localStorage.removeItem("scheme");
});
For the CSS side, we use the default variable custom property values fallback with the dark color in first position. We can define all the necessary dark colors via the :root element.
:root body.dark {
--app-bg-dark: #131313;
--app-tx-dark: #f8f9fa;
}
body{
background-color: var( --app-bg-dark, white );
color: var( --app-tx-dark, black );
}
/* if dark mode isn't set, fall back to light. */
And for the html, a simple checkbox <input id="tglScheme" type="checkbox">.
Finally here is the Codepen https://codepen.io/amarinediary/full/yLgppWW.
⚠️️ Codepen overwrites location.reload() so you won't be abble to test the live update on system change. Don't hesitate to try it on your localhost.
An alternative solution I found using the blog mybyways which is not mentioned anywhere else but works for me. This is useful only when html uses the prefers-color-scheme css media classes.
Unlike other answers, it uses the stylesheets' rules to append the class (as opposed to adding or removing "dark" or "light" from classList)
By default it takes the style of OS setting and overrides it when toggled. I tried from Google Chrome labs but it didn't work out for me.
function setPreferredColorScheme(mode = "dark") {
console.log("changing")
for (var i = document.styleSheets[0].rules.length - 1; i >= 0; i--) {
rule = document.styleSheets[0].rules[i].media;
if (rule.mediaText.includes("prefers-color-scheme")) {
console.log("includes color scheme")
switch (mode) {
case "light":
console.log("light")
rule.appendMedium("original-prefers-color-scheme");
if (rule.mediaText.includes("light")) rule.deleteMedium("(prefers-color-scheme: light)");
if (rule.mediaText.includes("dark")) rule.deleteMedium("(prefers-color-scheme: dark)");
break;
case "dark":
console.log("dark")
rule.appendMedium("(prefers-color-scheme: light)");
rule.appendMedium("(prefers-color-scheme: dark)");
if (rule.mediaText.includes("original")) rule.deleteMedium("original-prefers-color-scheme");
break;
default:
console.log("default")
rule.appendMedium("(prefers-color-scheme: dark)");
if (rule.mediaText.includes("light")) rule.deleteMedium("(prefers-color-scheme: light)");
if (rule.mediaText.includes("original")) rule.deleteMedium("original-prefers-color-scheme");
}
break;
}
}
}
#media (prefers-color-scheme: light) {
:root {
color: pink;
background-color: yellow;
}
}
#media (prefers-color-scheme: dark) {
:root {
color: red;
background-color: blue;
}
}
<body>
<button onClick="setPreferredColorScheme()">
toggle
</button>
</body>
Above is a working example ^
Full source: https://mybyways.com
I just did this today and worked with the dark mode preferences. I think this solution is simpler than all the ones I looked at here.
The toggle darkmode button is optional. Local storage is used, works with iPhone dark mode.
Finally, my research was all the posts here. I worked out an "optimized" solution. See what kind of traction you get. I included the SVG to make things easier.
EDIT: I updated the JS function to check for availability of local storage. I also moved the function variables into the function itself other than the toggle used for real-time setting storage. It is slightly longer this way but more correct and still shorter than most the others.
function checkForLocalStorage () {
try {
localStorage.setItem('test', 1)
localStorage.removeItem('test')
return true
} catch (e) { return false }
}
const hasLocalStorage = checkForLocalStorage()
let isThemeDark = null
function toggleDarkMode () {
const isPreferDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const localPref = hasLocalStorage ? localStorage.getItem('isThemeDark') : null
const hasLocalPref = !!localPref
if (isThemeDark === null && hasLocalPref) isThemeDark = localPref === 'dark'
else if (isThemeDark === null && isPreferDark) isThemeDark = true
else if (isThemeDark === null) isThemeDark = false
else isThemeDark = !isThemeDark
const theme = isThemeDark ? 'dark' : 'light'
if (hasLocalStorage) localStorage.setItem('isThemeDark', theme)
document.body.classList[isThemeDark ? 'add' : 'remove']('dark-mode')
}
toggleDarkMode()
body.dark-mode { background: #222; color: #f2f2f2; }
body.dark-mode #darkModeToggle svg { fill: #fff; }
<a href="#" id="darkModeToggle" onclick="toggleDarkMode()">
<svg width="24px" height="24px"><path d="M12,22 C17.5228475,22 22,17.5228475 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,17.5228475 6.4771525,22 12,22 Z M12,20.5 L12,3.5 C16.6944204,3.5 20.5,7.30557963 20.5,12 C20.5,16.6944204 16.6944204,20.5 12,20.5 Z"/></svg>
</a>
<div>Hello World!</div>
My Solution (3 options in radio inputs: dark, system, light) adaptation of JimmyBanks and Meanderbilt Solution:
its a bit verbose I guess, but I struggled a bit to wrap my head around it
const themeSwitches = document.querySelectorAll('[data-color-theme-toggle]')
function removeColorThemeLocalStorage() {
localStorage.removeItem('color-theme')
}
function saveColorTheme(colorTheme) {
if (colorTheme === 'system') {
removeColorThemeLocalStorage()
return
}
localStorage.setItem('color-theme', colorTheme)
}
function applyColorTheme() {
const localStorageColorTheme = localStorage.getItem('color-theme')
const colorTheme = localStorageColorTheme || null
if (colorTheme) {
document.documentElement.setAttribute('data-color-theme', colorTheme)
}
}
function themeSwitchHandler() {
themeSwitches.forEach(themeSwitch => {
const el = themeSwitch
if (el.value === localStorage.getItem('color-theme')) {
el.checked = true
}
el.addEventListener('change', () => {
if (el.value !== 'system') {
saveColorTheme(el.value)
applyColorTheme(el.value)
} else {
removeColorThemeLocalStorage()
document.documentElement.removeAttribute('data-color-theme')
}
})
})
applyColorTheme()
}
document.addEventListener('DOMContentLoaded', () => {
themeSwitchHandler()
applyColorTheme()
})
html {
--hue-main: 220;
--color-text: hsl(var(--hue-main), 10%, 25%);
--color-text--high-contrast: hsl(var(--hue-main), 10%, 5%);
--color-link: hsl(var(--hue-main), 40%, 30%);
--color-background: hsl(var(--hue-main), 51%, 98.5%);
}
#media (prefers-color-scheme: dark) {
html.no-js {
--color-text: hsl(var(--hue-main), 5%, 60%);
--color-text--high-contrast: hsl(var(--hue-main), 10%, 80%);
--color-link: hsl(var(--hue-main), 60%, 60%);
--color-background: hsl(var(--hue-main), 10%, 12.5%);
}
}
[data-color-theme='dark'] {
--color-text: hsl(var(--hue-main), 5%, 60%);
--color-text--high-contrast: hsl(var(--hue-main), 10%, 80%);
--color-link: hsl(var(--hue-main), 60%, 60%);
--color-background: hsl(var(--hue-main), 10%, 12.5%);
}
<div class="color-scheme-toggle" role="group" title="select a color scheme">
<p>saved setting: <span class="theme-readout">...</span></p>
<input type="radio" name="scheme" id="dark" value="dark" aria-label="dark color scheme"> <label for="dark">dark</label>
<input type="radio" name="scheme" id="system" value="system" aria-label="system color scheme" checked="system"> <label for="system">system</label>
<input type="radio" name="scheme" id="light" value="light" aria-label="light color scheme"> <label for="light">light</label>
</div>
I believe the best way is to natively follow system settings unless user says otherwise.
Create button in your html. And then bind three-position switch to it with js. With saving to browser's LocalStorage.
And, finally, stylize your switch element.
document.addEventListener("DOMContentLoaded", function(event) {
switchTheme('.theme-switch');
});
function switchTheme(selector) {
const switches = document.querySelectorAll(selector);
// let colorTheme = localStorage.getItem('colorTheme') || 'system'; //commented to avoid security issue
let colorTheme = 'system';
function changeState() {
// localStorage.setItem('colorTheme', colorTheme); //commented to avoid security issue
document.documentElement.setAttribute('data-theme', colorTheme);
}
changeState();
switches.forEach(el => {
el.addEventListener('click', () => {
switch (colorTheme) {
case 'dark':
colorTheme = 'light';
break
case 'light':
colorTheme = 'system';
break
default:
colorTheme = 'dark';
}
changeState();
});
});
}
:root:not([data-theme="dark"]) {
--bg: #fff;
}
#media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #000;
}
}
:root[data-theme="dark"] {
/* yep, you'll need to duplicate styles from above */
--bg: #000;
}
body {
background: var(--bg);
}
.theme-switch:after {
content: ': system';
}
:root[data-theme="dark"] .theme-switch:after {
content: ': dark';
}
:root[data-theme="light"] .theme-switch:after {
content: ': light';
}
<button class="theme-switch">Color scheme</button>
I suggest using SCSS. You can make it more simpler.
/* Dark Mode */
#mixin darkMixin {
body {
color: #fff;
background: #000;
}
}
#media (prefers-color-scheme: dark) {
#include darkMixin;
}
.darkMode {
#include darkMixin;
}
.lightMode {
body {
color: #000;
background: #fff;
}
}
And you can toggle/override using JavaScript.
(In this example, I used jQuery to make it look easy)
// dark
$('html').removeClass('lightMode').addClass('darkMode')
// light
$('html').removeClass('darkMode').addClass('lightMode')
If you want to detect, this is the code based on JimmyBanks' one.
function isDarkTheme(){
let theme="light"; //default to light
if (localStorage.getItem("theme")){
if (localStorage.getItem("theme") == "dark")
theme = "dark"
} else if (!window.matchMedia) {
return false
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
theme = "dark"
}
return theme=='dark'
}
To save the current theme, just use localStorage:
localStorage.setItem("theme", 'light')
or
localStorage.setItem("theme", 'dark')
My answer is based on this one, but I have included changes I had to make to get it working, plus I added the ability to persist in local storage.
The important point is that it works with #media (prefers-color-scheme: dark) CSS, and there was no need to create or duplicate additional CSS classes just for this to work. In other words it works with native CSS color scheme.
On the page I first added a sun/moon icon and set them to invisible.
<a href="javascript:toggleColorScheme();">
<span id="icon-sun">🌞</span>
<span id="icon-moon">🌚</span>
</a>
<style>
#icon-sun {
width: 1.5rem;
height: 1.5rem;
display: none;
}
#icon-moon {
width: 1.5rem;
height: 1.5rem;
display: none;
}
</style>
Then, this Javascript which does the bulk of the work.
// https://stackoverflow.com/questions/56300132/how-to-override-css-prefers-color-scheme-setting
// Return the system level color scheme, but if something's in local storage, return that
// Unless the system scheme matches the the stored scheme, in which case... remove from local storage
function getPreferredColorScheme(){
let systemScheme = 'light';
if(window.matchMedia('(prefers-color-scheme: dark)').matches){
systemScheme = 'dark';
}
let chosenScheme = systemScheme;
if(localStorage.getItem("scheme")){
chosenScheme = localStorage.getItem("scheme");
}
if(systemScheme === chosenScheme){
localStorage.removeItem("scheme");
}
return chosenScheme;
}
// Write chosen color scheme to local storage
// Unless the system scheme matches the the stored scheme, in which case... remove from local storage
function savePreferredColorScheme(scheme){
let systemScheme = 'light';
if(window.matchMedia('(prefers-color-scheme: dark)').matches){
systemScheme = 'dark';
}
if(systemScheme === scheme){
localStorage.removeItem("scheme");
}
else {
localStorage.setItem("scheme", scheme);
}
}
// Get the current scheme, and apply the opposite
function toggleColorScheme(){
let newScheme = "light";
let scheme = getPreferredColorScheme();
if (scheme === "light"){
newScheme = "dark";
}
applyPreferredColorScheme(newScheme);
savePreferredColorScheme(newScheme);
}
// Apply the chosen color scheme by traversing stylesheet rules, and applying a medium.
function applyPreferredColorScheme(scheme) {
for (var i = 0; i <= document.styleSheets[0].rules.length-1; i++) {
rule = document.styleSheets[0].rules[i].media;
if (rule && rule.mediaText.includes("prefers-color-scheme")) {
switch (scheme) {
case "light":
rule.appendMedium("original-prefers-color-scheme");
if (rule.mediaText.includes("light")) rule.deleteMedium("(prefers-color-scheme: light)");
if (rule.mediaText.includes("dark")) rule.deleteMedium("(prefers-color-scheme: dark)");
break;
case "dark":
rule.appendMedium("(prefers-color-scheme: light)");
rule.appendMedium("(prefers-color-scheme: dark)");
if (rule.mediaText.includes("original")) rule.deleteMedium("original-prefers-color-scheme");
break;
default:
rule.appendMedium("(prefers-color-scheme: dark)");
if (rule.mediaText.includes("light")) rule.deleteMedium("(prefers-color-scheme: light)");
if (rule.mediaText.includes("original")) rule.deleteMedium("original-prefers-color-scheme");
break;
}
}
}
// Change the toggle button to be the opposite of the current scheme
if(scheme === "dark"){
document.getElementById("icon-sun").style.display='inline';
document.getElementById("icon-moon").style.display='none';
}
else {
document.getElementById("icon-moon").style.display='inline';
document.getElementById("icon-sun").style.display='none';
}
}
applyPreferredColorScheme(getPreferredColorScheme());
So on page load, the applyPreferredColorScheme(getPreferredColorScheme()) method runs, which checks the system and local storage and figures out which theme to apply. It also switches between the sun or moon icon depending on the current theme.
When the user clicks the icon to toggle the theme, toggleColorScheme() runs which stores the chosen theme in local storage, but one difference - if the user switches back to the theme that matches their OS, the code simply removes the item from local storage. Trying to keep it as native as possible.
Looks like there's plenty of answers here already, but nothing quite matching my needs. I wanted to be able to:
Use the OS preference if no other preference is set.
If using the OS preference, have the page reflect changes to it instantly.
Allow a query string param to override the OS preference.
If the user explicitly sets a preference, use that and save for future use.
Have the checkbox on the page reflect the theme currently in effect.
Toggle the same checkbox is explicitly set a preference.
Have relatively short but clean and readable code.
My CSS looks a lot like that in other solutions. For completeness this is something like:
:root {
/* Support light and dark, with light preferred if no user preference */
color-scheme: light dark;
/* default light mode */
--bg-color: #f7f7f7;
--text-color: #222430;
/* dark mode overrides */
--dark-bg-color: #222430;
--dark-text-color: #f7f7f7;
}
.dark-theme {
/* Use dark mode overrides when class is applied */
--bg-color: var(--dark-bg-color);
--text-color: var(--dark-text-color);
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
There is a checkbox with id 'darkThemeToggle' in the HTML. The below runs on page load. (Written in TypeScript)
function manageTheme() {
const darkThemeCheckbox = document.querySelector("#darkThemeToggle") as HTMLInputElement;
const agentPrefersDarkQuery = matchMedia("(prefers-color-scheme: dark)");
function setTheme(arg?: MediaQueryListEvent | string) {
let chosenTheme = "";
if (typeof arg === "string") {
// If this function is called with a string, then an explict preference has
// been set by the user. Use that theme and save the setting for the future.
chosenTheme = arg;
localStorage.setItem("theme", chosenTheme);
} else {
// Use any saved preference, else check for query param, else any OS preference.
chosenTheme = localStorage.getItem("theme") ||
new URLSearchParams(window.location.search).get("theme") ||
(agentPrefersDarkQuery.matches ? "dark" : "light");
}
if (chosenTheme === "dark") {
document.documentElement.classList.add("dark-theme");
} else {
document.documentElement.classList.remove("dark-theme");
}
// Update the UX to reflect the theme that was ultimately applied.
darkThemeCheckbox.checked = (chosenTheme === "dark");
}
// Whenever the user changes the OS preference, refresh the applied theme.
agentPrefersDarkQuery.onchange = setTheme;
// Note that the 'change' event only fires on user action, (not when set in code), which is
// great, else this might cause an infinite loop with the code setting it in setTheme.
darkThemeCheckbox.addEventListener('change', ev => {
let themeChosen = darkThemeCheckbox.checked ? "dark" : "light";
setTheme(themeChosen);
});
setTheme(); // Run on initial load.
}
document.addEventListener("DOMContentLoaded", manageTheme);

Errors with changing a header's color

I have a webpage I am designing with html, css, and JS. I have them properly hooked up, but for some reason my header will not do what I want. I have tried everything, does anyone know where I went wrong?
I want it so that when I mouse over the header or click the header, the color will change. This does not happen.
The important code in a JS-Fiddle: https://jsfiddle.net/DITTO/c37zxdke/
//javascript
var col = document.getElementById("webTitle").style.color;
function orangeToBlue() {
col = "#197CFF";
}
function blueToGreen() {
col = "#19FF29";
}
function greenToPink() {
col = "#FF19EF";
}
function pinkToOrange() {
col = "#FF9C19";
}
function changeColor() {
if (document.getElementById("webTitle").style.color === "#FF9C19") {
orangeToBlue();
} else if (document.getElementById("webTitle").style.color === "#197CFF") {
blueToGreen();
} else if (document.getElementById("webTitle").style.color === "#19FF29") {
greenToPink();
} else if(document.getElementById("webTitle").style.color === "#FF19EF") {
pinkToOrange();
}
}
//html
<h1 id="webTitle" onmouseover="changeColor()">Webpage Title</h1>
//css
#webTitle:hover {
cursor: pointer;
}
#webTitle {
text-shadow: 3.5px 3.5px 0px rgba(0, 0, 0, 0.3);
font-size: 100px;
font-family: fantasy;
color: #FF9C19;
}
Did you try to get what
document.getElementById("webTitle").style.color output?
When I log this action it's give me a blank string, can you confirm?
If you have the same blank string, then look at this this post the answer looks really good, and has nice links.
Or you can just look at this answer (from a duplicate).
Hope it's gonna help you a bit.
And the answer of Eduardo Escobar is important too.
Your functions orangeToBlue(), blueToGreen(), greenToPink() and pinkToOrange() aren't making any effect, although they are being called, they're just defining a variable, nothing else.
You should use rgb(rrr,ggg,bbb) color values in javaScript at theplace of using #rrggbb color values and CSS for seting initial color for your text.
//javascript
document.getElementById("webTitle").style.color = "#197CFF";
function orangeToBlue() {
document.getElementById("webTitle").style.color = "#197CFF";
}
function blueToGreen() {
document.getElementById("webTitle").style.color = "#19FF29";
}
function greenToPink() {
document.getElementById("webTitle").style.color = "#FF19EF";
}
function pinkToOrange() {
document.getElementById("webTitle").style.color = "#FF9C19";
}
function changeColor() {
if (document.getElementById("webTitle").style.color === "rgb(255, 156, 25)") {
orangeToBlue();
}
else
if (document.getElementById("webTitle").style.color === "rgb(25, 124, 255)") {
blueToGreen();
}
else
if (document.getElementById("webTitle").style.color === "rgb(25, 255, 41)") {
greenToPink();
}
else
if (document.getElementById("webTitle").style.color === "rgb(255, 25, 239)") {
pinkToOrange();
}
}
//html
<h1 id="webTitle" onclick="changeColor()" style="color: #FF9C19;" >Webpage Title</h1>
//css
#webTitle:hover {
cursor: pointer;
}
#webTitle {
text-shadow: 3.5px 3.5px 0px rgba(0, 0, 0, 0.3);
font-size: 100px;
font-family: fantasy;
}
The fiddle I posted most recently under g4u's answer works, fiddle doesn't work. FIDDLE IS BROKEN! Try it in a notepad and run it.

Categories