I'm trying to accomplish a few things to get dark mode working on my site.
Use a checkbox to switch between light and dark themes (the checkbox will set the data-theme attribute to user's preference). Here't the js below
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();
//identify the toggle switch HTML element
const themeSwitch = document.getElementById('hm-theme-switch-toggle');
//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');
themeSwitch.checked = true;
} else {
localStorage.setItem('theme', 'light');
document.documentElement.setAttribute('data-theme', 'light');
themeSwitch.checked = false;
}
}
//listener for changing themes
themeSwitch.addEventListener('change', switchTheme, false);
//pre-check the dark-theme checkbox if dark-theme is set
if (document.documentElement.getAttribute("data-theme") == "dark"){
themeSwitch.checked = true;
}
My html and css is setup that I have a pseudo element to change the text if the checkbox is checked, along with the entire theme changing to dark mode. For some reason it's not firing though even though I have the label pointed to the input id and the correct id is called in js.
<div class="hm-theme-switch-wrapper">
<input type="checkbox" class="hm-theme-toggle" id="hm-theme-switch-toggle" name="hm-checkbox" />
<label for="hm-theme-switch-toggle" id="hm-theme-switch" style="text-align:right;"><i class="hm-theme-state-icon"></i><span>Dark</span></label>
</div>
.hm-theme-switch-wrapper {
background-color: none;
border-radius: 150px;
border: 2px solid var(--hm-color-text);
overflow: auto;
float: right;
width: 100px;
label {
display: flex;
align-items: center;
padding: 5px 10px;
margin: 0;
}
input.hm-theme-toggle {
display: none;
}
input#hm-theme-switch-toggle:checked + label span {
display: none;
}
input#hm-theme-switch-toggle:checked + label:after {
content: "Light";
}
.hm-theme-state-icon {
background-image : url(https://michaelcraig.design/wp-content/uploads/2021/04/light_dark.svg);
background-size: cover;
display: inline-block;
height: 20px;
width: 20px;
margin-right: 10px;
}
.hm-theme-state {
text-align:center;
display:block;
border-radius:4px;
color: var(--hm-color-text);
font-size: 16px;
font-weight: 600;
}
}
I appreciate any help and suggestions. You can also view this live # https://michaelcraig.design
Try put this lines:
const themeSwitch = document.getElementById("hm-theme-switch-toggle");
themeSwitch.addEventListener("change", switchTheme, false);
.. inside a ready trigger:
$(document).ready(function(){
const themeSwitch = document.getElementById("hm-theme-switch-toggle");
themeSwitch.addEventListener("change", switchTheme, false);
});
Maybe DOM is not ready when getElementById run.
Edit: Since OP is not using jQuery, here's the vanilla JS equivalent:
document.addEventListener('DOMContentLoaded', () => {
const themeSwitch = document.getElementById("hm-theme-switch-toggle");
themeSwitch.addEventListener("change", switchTheme, false);
});
Related
I have This simple website that i need to make it change from light theme to dark theme, the light theme works fine, but the dark theme only changes its button properly because when i click in the button to change the "body" elements should change its class from "light-theme" to "dark-theme", instead it changes to "light-theme dark-theme"
here's HTML
`
<body class="light-theme">
<h1>Task List</h1>
<p id="msg">Current tasks:</p>
<ul>
<li class="list">Add visual styles</li>
<li class="list">add light and dark themes</li>
<li>Enable switching the theme</li>
</ul>
<div>
<button class="btn">Dark</button>
</div>
<script src="app.js"></script>
<noscript>You need to enable JavaScript to view the full site</noscript>
Heres CSS
:root {
--green: #00FF00;
--white: #FFFFFF;
--black: #000000;
}
.btn {
position: absolute;
top: 20px;
left: 250px;
height: 50px;
width: 50px;
border-radius: 50%;
border: none;
color: var(--btnFontColor);
background-color: var(--btnBg);
}
.btn:focus {
outline-style: none;
}
body {
background: var(--bg);
}
ul {
font-family: helvetica;
}
li {
list-style: circle;
}
.list {
list-style: square;
}
.light-theme {
--bg: var(--green);
--fontColor: var(--black);
--btnBg: var(--black);
--btnFontColor: var(--white);
}
.dark-theme{
--bg: var(--black);
--fontColor: var(--green);
--btnBg: var(--white);
--btnFontColor: var(--black);
}
and heres JavaScript
'use strict';
const switcher = document.querySelector('.btn');
switcher.addEventListener('click', function () {
document.body.classList.toggle('dark-theme')
var className = document.body.className;
if(className == "light-theme") {
this.textContent = "Dark";
} else {
this.textContent = "Light";
}
console.log('current class name: ' + className);
});
`
I tried to change some things in css but later found that the problem might be in the javascript, but my code is exactly as the code in my course is.
when i click in the button to change the "body" elements should change its class from "light-theme" to "dark-theme", instead it changes to "light-theme dark-theme"
That's indeed true - your JS code is only toggling the class "dark-theme" and does nothing with the "light-theme" class.
So a simple fix would be to toggle both classes:
switcher.addEventListener('click', function () {
document.body.classList.toggle('dark-theme')
document.body.classList.toggle('light-theme'); // add this line
var className = document.body.className;
if(className == "light-theme") {
this.textContent = "Dark";
} else {
this.textContent = "Light";
}
console.log('current class name: ' + className);
});
But you could simplify your code because you really don't need 2 classes here. If light theme is the default, just remove the light-theme class and all its CSS rules, and apply those to body instead. The .dark-theme rules will override these when the class is set, but not otherwise.
In this darkmode code I allow the user to set the theme through system preferences while also allowing the user to override the system preferred theme by toggling the button or radio.
How do I set checked state for the system default radio when the user changes their system preference to auto?
Since it changes prefers-color-scheme to either light or dark how would I detect the user changing their preference to auto in order to update the radio's checked state? I tried adding the checked state inside of if (window.matchMedia("(prefers-color-scheme: no-preference)").matches) {} but it didn't execute.
Also I can't figure out why my window.addEventListener("load", (e) => {} isn't detecting the user selected theme on load. It's defaulting to light even when the page is refreshed and the theme in settings is set to dark.
https://codepen.io/moofawsaw/pen/VwmaPMV
$(document).ready(function() {
function setLight() {
$("body").removeClass("dark-theme");
$("body").addClass("light-theme");
$("#light").prop("checked", true);
$('input[name="theme"]').change();
}
function setDark() {
$("body").addClass("dark-theme");
$("body").removeClass("light-theme");
$("#dark").prop("checked", true);
$('input[name="theme"]').change();
}
function setMode() {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
$("#dark").prop("checked", true);
$('input[name="theme"]').change();
setDark();
} else {
$("#light").prop("checked", true);
$('input[name="theme"]').change();
setLight();
}
}
//Check the mode on load and style accordingly
if (localStorage.getItem("mode") == "dark-theme") {
setDark();
} else {
setLight();
}
//Check for when system default is changed -> change theme
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
if (localStorage.getItem("mode") !== null) {
//Manually set so don't do anything
} else {
setMode();
}
});
window.addEventListener("load", (e) => {
//If the mode is mannually set on load - choose that mode
if (localStorage.getItem("mode") !== null) {
// Do nothing the mode has been set manually
} else {
// Set the mode to the default
setMode();
}
});
//add toggle
$("#toggleTheme").on("click", function() {
if ($("body").hasClass("dark-theme")) {
localStorage.setItem("mode", "light-theme");
setLight();
} else {
localStorage.setItem("mode", "dark-theme");
setDark();
}
});
$("#light").on("click", function() {
localStorage.setItem("mode", "light-theme");
setLight();
});
$("#dark").on("click", function() {
localStorage.setItem("mode", "dark-theme");
setDark();
});
$("#default").on("click", function() {
localStorage.removeItem("mode");
if (localStorage.getItem("mode") !== null) {
setMode();
}
});
});
//For selecting radio
$('input[name="theme"]').on("change", function() {
if ($(this).is(":checked")) {
$("label.checked").removeClass("checked");
$(this).closest("label").addClass("checked");
}
});
body {
--font-color: blue;
--bg-color: white;
--bg-span: #ececec;
}
body.dark-theme {
--font-color: white;
--bg-color: black;
--bg-span: white;
}
#media (prefers-color-scheme: dark) {
body {
--font-color: white;
--bg-color: black;
--bg-span: white;
}
body.light-theme {
--font-color: blue;
--bg-color: white;
--bg-span: #ececec;
}
}
body {
color: var(--font-color);
background: var(--bg-color);
}
button {
padding: 0.3rem 0.9rem;
outline: none;
color: var(--font-color);
background: var(--bg-color);
}
span {
padding: 0.9rem;
color: var(--font-color);
background: var(--bg-span);
}
img {
max-width: 190px;
}
/*input {
display: none;
}*/
label {
overflow: hidden;
display: flex;
flex-direction: column;
margin-right: 0.9rem;
border: 1px solid whitesmoke;
border-radius: 9px;
}
label.checked {
border: 3px solid blue;
}
.theme {
display: flex;
}
.buttons,
.theme {
padding: 1.3rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="buttons">
<button id="toggleTheme">Mode</button>
</div>
<div class="theme">
<label for="light">
<img src="https://github.githubassets.com/images/modules/settings/color_mode_light.svg">
<input type="radio" name="theme" id="light">
<span>Light</span>
</label>
<label for="dark">
<img src="https://github.githubassets.com/images/modules/settings/color_mode_dark.svg">
<input type="radio" name="theme" id="dark">
<span>Dark</span>
</label>
<label for="default">
<img src="https://github.githubassets.com/images/modules/settings/color_mode_auto.svg">
<input type="radio" name="theme" id="default">
<span>System Default</span>
</label>
</div>
The feature no-preference of the prefers-color-scheme #media rule is deprecated and that's why it does not work any more on latest browsers.
The load window event does execute but it doesn't have the appropriate logic in your code. Your code does nothing if the mode local storage setting is not null, which of course is the case after selecting something.
You should have the following behaviours regarding mode setting:
light for light scheme that will override system/browser setting
dark for dark scheme that will override system/browser setting
Anything elese other than dark/light, including null/undefined, will apply what browser setting has, which it can have its own override or reading the OS setting
Also, setLight/setDark procedures should just apply the theme and not alter any input elements state because it will end up with circular value setting bugs:
function setLight() {
$("body").removeClass("dark-theme");
$("body").addClass("light-theme");
}
function setDark() {
$("body").addClass("dark-theme");
$("body").removeClass("light-theme");
}
Instead, you can have all the inputs change logic inside setMode which should check mode setting and handle all logic like this:
function setMode() {
// have a variable to indicate what mode
// we should apply at the end of the procedure
let lightMode;
// always unregister media listener,
// because we only need it if mode is not set,
// which then we'll have to read media queries using matchMedia
unregisterMediaListener();
switch (localStorage.getItem("mode")) {
case 'light-theme': {
lightMode = true;
$("#light").prop("checked", true);
break;
}
case 'dark-theme': {
lightMode = false;
$("#dark").prop("checked", true);
break;
}
default: { // using system setting
$("#default").prop("checked", true);
// set lightMode indicator using the matchMedia query result
lightMode = window.matchMedia("(prefers-color-scheme: light)").matches;
// and register media change listener for changes
registerMediaListener();
}
}
// allpy new input values
$('input[name="theme"]').change();
// set the actual mode
if (lightMode) {
setLight();
} else {
setDark();
}
}
And finally the matchMedia prefers-color-scheme: light change listener:
// we set a mediaMatch variable so we can use it and unregister the listener
let mm;
function registerMediaListener() {
mm = window.matchMedia("(prefers-color-scheme: light)");
mm.addEventListener('change', onLightSchemeChange);
}
function unregisterMediaListener() {
if (mm) mm.removeListener(onLightSchemeChange);
}
function onLightSchemeChange(scheme) {
if (scheme.matches) {
// We have browser/system light scheme
setLight();
} else {
setDark();
}
}
Working example
You can check this forked and modified pen that is actually working and does select the appropriate setting/option when the page loads: https://codepen.io/clytras/pen/MWberor
I have made a dark mode button with my own dark theme. The theme is saved by Local Storage. Also when I click the button, then it's icon not change (moon to sun). But if I reload the page, the site is still in dark mode but the button icon's not changed. So heres a link which show you the problem if youo don't understant what i am talking about. codepen demo Also heres my code:
$(document).ready(function () {
$('body').toggleClass(localStorage.toggled);
$(".darkmode-btn").on("click", function(){
if (localStorage.toggled != 'dark') {
$('body').toggleClass('dark', true);
localStorage.toggled = "dark";
} else {
$('body').toggleClass('dark', false);
localStorage.toggled = "";
$(this).toggleClass("sun")
}
});
});
.dark{background-color: #222;}
.darkmode-btn {
height: 60px;
line-height: 60px;
color: #fff;
text-align: center;
font-size: 20px;
margin-left: 20px;
z-index: 100;
cursor: pointer;
background-color: #08f;
}
.darkmode-btn:before,.darkmode-btn.moon:before,.darkmode-btn.sun:before {
font-family: "fontawesome";
}
.darkmode-btn:before {
content: '\f186';
}
.darkmode-btn.moon:before {
content: '\f186';
}
.darkmode-btn.sun:before {
content: '\f185';
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div class="darkmode-btn">
</div>
</body>
$(".darkmode-btn").on("click", function(){
if (localStorage.toggled != 'dark') {
$('body').toggleClass('dark', true);
localStorage.toggled = "dark";
$(this).addClass("moon").removeClass("sun")
} else {
$('body').toggleClass('dark', false);
localStorage.toggled = "";
//$(this).toggleClass("sun").removeClass("sun")
$(this).addClass("sun").removeClass("moon")
}
});
});
This should help you.
I'm trying to learn the vanilla JS way of toggling a button color with addEventListener. I'm having some trouble understanding how to switch the button back to the original color. Here is my code:
HTML
<h1>Hello</h1>
<section id="container"></section>
CSS
h1 {
font-family: sans;
}
#container {
padding: 2em;
background-color: tomato;
}
#container2 {
padding: 2em;
background-color: blue;
}
JS
var el = document.getElementById('container');
el.addEventListener('click', function() {
this.style.backgroundColor = 'blue';
});
jsbin
You can restore the declared CSS style by clearing the background color, like this:
this.style.backgroundColor = '';
Fiddle at http://jsfiddle.net/xza5bt0q/
Click the box to toggle the color.
You could check if a color is set and remove it otherwise:
el.addEventListener('click', function() {
if (this.style.backgroundColor == 'blue') {
this.style.backgroundColor = null;
} else {
this.style.backgroundColor = 'blue';
}
});
Try like the given code snippet here
HTML
<div id="toggleArea" class='active'> Toggle area see in class name </div>
<button id="btnToggle"> Toggle Button </button>
JavaScript
const toggleArea = document.getElementById('toggleArea')
const btnToggle = document.getElementById('btnToggle')
btnToggle.addEventListener('click', function() {
if (toggleArea.classList.contains('active')) {
toggleArea.classList.remove("active");
toggleArea.classList.add("disable");
} else {
toggleArea.classList.remove("disable");
toggleArea.classList.add("active");
}
toggleArea.innerHTML = toggleArea.classList[0]
})
I would recommend toggling a CSS class when clicking. Then you can use the class to control color and other properties.
CSS
.container-active {
background-color: blue;
}
JS
var ACTIVE_CLASS = 'container-active'; // define a css class 'constant'
var el = document.getElementById('container');
el.addEventListener('click', function () {
var classes,
idx;
// simplest case: class name is empty
if (this.className === '') {
this.className = ACTIVE_CLASS;
return;
}
// next: class name matches active
if (this.className === ACTIVE_CLASS) {
this.className = '';
return;
}
// otherwise, if more complex, parse and modify classes
classes = this.className.split(' ');
idx = classes.indexOf(activeClass);
if (idx === -1) {
classes.push(activeClass);
} else {
classes.splice(idx, 1);
}
this.className = classes.join(' ');
});
Try the following codes: HTML, CSS, and Javascript.
In this code, the default color of the button is set to be blue. Once you click the button, it changes the background-color to red. If you click it again, the background-color changes to the default i.e. blue.
HTML
<button id="button" class="blueColor" onclick="toggle()">GET STARTED</button>
CSS
#button{
color: white;
width: 175px;
height: 50px;
border-radius: 5px;
font-family: arial;
font-weight: bold;
word-spacing: 3px;
border: none;
outline: none;
}
.blueColor{
background: blue;
}
.redColor{
background: red;
}
Javascript
<script>
function toggle(){
var colorofbutton = document.querySelector("#button");
if(document.getElementsByClassName("blueColor")[0])
{
colorofbutton.classList.remove('blueColor');
colorofbutton.classList.add('redColor');
}
else
{
colorofbutton.classList.remove('redColor');
colorofbutton.classList.add('blueColor');
}
}
</script>
I'm attempting to customise the control buttons on my video player. Currently I have a button that plays and pauses my video. This is working great. Though I want a visual representation of the play and pause buttons, instead of them staying the same when in the paused state or when the video is playing. I plan on having two seperate images for play and pause.
My problem is that I can't quite get my javascript to toggle my buttons, I'm thinking the best way to toggle the buttons is when one is paused, hide one element and when the video is playing hide the other element.
So here is what I have currently:
function playPause() {
mediaPlayer = document.getElementById('media-video');
if (mediaPlayer.paused)
mediaPlayer.play();
$('.play-btn').hide();
else
mediaPlayer.pause();
$('.pause-btn').hide();
}
Any help is greatly appreciated.
You need to use more Braces '{}' in if and else
function playPause() {
var mediaPlayer = document.getElementById('media-video');
if (mediaPlayer.paused) {
mediaPlayer.play();
$('.pause-btn').show();
$('.play-btn').hide();
} else {
mediaPlayer.pause();
$('.play-btn').show();
$('.pause-btn').hide();
}
}
I think it's works well.
For e.g.:
function togglePlayPause() {
// If the mediaPlayer is currently paused or has ended
if (mediaPlayer.paused || mediaPlayer.ended) {
// Change the button to be a pause button
changeButtonType(playPauseBtn, 'pause');
// Play the media
mediaPlayer.play();
}
// Otherwise it must currently be playing
else {
// Change the button to be a play button
changeButtonType(playPauseBtn, 'play');
// Pause the media
mediaPlayer.pause();
}}
Source: http://www.creativebloq.com/html5/build-custom-html5-video-player-9134473
/* in Jquery*/
$('#play-pause-button').click(function () {
if ($("#media-video").get(0).paused) {
$("#media-video").get(0).play();
}
else {
$("#media-video").get(0).pause();
}
});
Video is a DOM element not the javascript function;
Here are two examples, each comes with html, css and js.
const $ = (s, c = document) => c.querySelector(s);
const $$ = (s, c = document) => Array.prototype.slice.call(c.querySelectorAll(s));
function main(){
/* Example 1 */
// optional code to trigger click for label when 'Space' key pressed
$$('label.btn-toggle').forEach((label) => {
label.addEventListener('keypress', (e) => {
if(e.key === ' ' || e.code === 'Space'){
let input = e.target.control;
if(input){
input.click();
}
}
});
});
$('#btnPlayPause>input[type="checkbox"]').addEventListener('click', (e) => {
let input = e.target;
if(input.parentNode.getAttribute('aria-disabled') === 'true'){
e.preventDefault();
return;
}
if(input.checked){
// TODO play
console.log('playing');
}else{
// TODO pause
console.log('paused');
}
});
/* Example 2 */
$('#btnMuteUnmute').addEventListener('click', (e) => {
let button = e.target;
let isOn = button.value==='on';
if(!isOn){
// TODO mute
console.log('muted');
}else{
// TODO unmute
console.log('unmuted');
}
button.value = isOn?'off':'on';
});
}
document.addEventListener('DOMContentLoaded', main);
/* Example 1 */
label.btn-toggle{
font-size: 13.3333px;
font-family: sans-serif;
display: inline-flex;
flex-flow: row wrap;
align-content: center;
justify-content: center;
cursor: default;
box-sizing: border-box;
margin: 0px;
padding: 1px 6px;
background-color: #efefef;
color: buttontext;
border: 1px solid buttonborder;
border-radius: 2px;
user-select: none;
}
label.btn-toggle:hover{
background-color: #dfdfdf;
}
label.btn-toggle:active{
background-color: #f5f5f5;
}
.btn-toggle>input[type="checkbox"]:not(:checked)~.on-text{
display: none;
}
.btn-toggle>input[type="checkbox"]:checked~.off-text{
display: none;
}
#btnPlayPause{
width: 60px;
height: 60px;
}
/* Example 2 */
button.btn-toggle[value="on"]::after{
content: attr(data-on-text);
}
button.btn-toggle[value="off"]::after{
content: attr(data-off-text);
}
#btnMuteUnmute{
width: 60px;
height: 60px;
}
<!-- Example 1 -->
<label id="btnPlayPause" role="button" class="btn-toggle" tabindex="0" title="Play / Pause">
<input type="checkbox" tabindex="-1" autocomplete="off" hidden aria-hidden="true" />
<span class="on-text">Pause</span>
<span class="off-text">Play</span>
</label>
<!-- Example 2 -->
<button id="btnMuteUnmute" type="button" class="btn-toggle"
value="off" data-on-text="Unmute" data-off-text="Mute" title="Mute / Unmute"></button>