My local storage works in terms of saying true and false, but when the page is refreshed, it reverts but keeps the value. For example, if I switch from default darkmode (false) to lightmode (true), it shows as true in the local storage (which is good). However, when I refresh the page, although the value still is true, the page has reverted to its default (false).
HTML:
<body onload="stored()">
<label for="ID_HERE" class="toggle-switchy">
<input checked type="checkbox" name="server" id="ID_HERE" />
<span class="toggle" onclick="toggle()" id="saveDarkLight"></span>
<span class="switch"></span>
</label>
</body>
JS:
function stored() {
var storedValue = localStorage.getItem("server");
if (storedValue) {
lightmode()
document.getElementById("ID_HERE").checked = true;
} else {
darkmode()
document.getElementById("ID_HERE").checked = false;
}
}
function toggle() {
if (document.getElementById("ID_HERE").checked) {
lightmode()
var input = document.getElementById("ID_HERE");
localStorage.setItem("server", input.checked);
}
else {
darkmode()
var input = document.getElementById("ID_HERE");
localStorage.setItem("server", input.checked);
}
}
function darkmode() {
const bodyChanges = document.querySelectorAll('.margin_body');
for (let i = 0; i < bodyChanges.length; i++) {
bodyChanges[i].style.background = '#0c0a0f';
}
}
function lightmode() {
const bodyChanges = document.querySelectorAll('.margin_body');
for (let i = 0; i < bodyChanges.length; i++) {
bodyChanges[i].style.background = 'white';
}
}
localStorage only saves string values, so saving true or false will convert it to 'true' or 'false'.
When you read back, you need to either JSON.parse it or check against the string values.
function stored() {
const storedValue = localStorage.getItem("server");
if(storedValue === 'true'){ // <------
lightmode()
document.getElementById("ID_HERE").checked = true;
}else{
darkmode()
document.getElementById("ID_HERE").checked = false;
}
}
First and foremost, I think you'll want the onclick event handler on the input element.
Also, the following is a quick and dirty implementation:
JS:
const SAVED_USER_INPUT = "savedUserInput";
const TARGET_ID = "toggle-text";
const BLUE = "blue";
const RED = "red";
const text = {
[BLUE]:
"Text color currently set to blue (default/unchecked state). Click to change to red",
[RED]: "Text color currently set to red. Click to change to blue",
};
const handleToggle = (event) => {
const isChecked = event.target.checked;
const color = isChecked ? RED : BLUE;
setColor(color);
setText(color);
savePreference(color);
};
const setColor = (fontColor = BLUE) => {
document.getElementById(TARGET_ID).style.color = fontColor;
return true;
};
const setText = (color) => {
document.getElementById(TARGET_ID).innerText = text[color];
return true;
};
const setChecked = (color) => {
if (color === RED) {
document.getElementsByTagName("input")[0].setAttribute("checked", true);
}
};
const savePreference = (color) => localStorage.setItem(SAVED_USER_INPUT, color);
const getSavedPreference = () => localStorage.getItem(SAVED_USER_INPUT);
// localStorage.clear()
window.onload = () => {
if (!(getSavedPreference() === null)) {
const savedColor = getSavedPreference();
setColor(savedColor);
setText(savedColor);
setChecked(savedColor);
} else {
setColor();
setText(BLUE);
setChecked(BLUE);
}
};
HTML:
<!DOCTYPE html>
<html>
<head>
<title>localStorage</title>
<script src="app.js"></script>
</head>
<body>
<label>
<input type="checkbox" onclick="handleToggle(event)">
<span id="toggle-text"></span>
</span>
</label>
</body>
</html>
Related
Here's Mozilla's code for handling touch gestures, such as two finger pinch/zoom, etc.
https://github.com/mdn/dom-examples/blob/main/touchevents/Multi-touch_interaction.html
And here is the live demo:
https://mdn.github.io/dom-examples/touchevents/Multi-touch_interaction.html
(This will only work on mobile—or at least it doesn't work on my Macbook Chrome browser.
I'm trying to replicate the same output using React. Here's what I have:
import React, { useState, useEffect } from 'react';
const TouchEventsContainer = () => {
const [logEvents, setLogEvents] = useState(false);
const [tpCache, setTpCache] = useState([]);
const [logText, setLogText] = useState("");
const [testText, setTestText] = useState("Tap Swipe");
const enableLog = () => {
setLogEvents(!logEvents);
setTestText(logText);
};
const clearLog = () => setLogText('');
const log = (name, ev, printTargetIds) => {
let s = `${name}: touches = ${ev.touches.length} ; targetTouches = ${ev.targetTouches.length} ; changedTouches = ${ev.changedTouches.length}`;
if (printTargetIds) {
s += "";
for (var i = 0; i < ev.targetTouches.length; i++) {
s += `... id = ${ev.targetTouches[i].identifier} <br>`;
}
}
setLogText((prevLogText) => prevLogText + s + "<br>");
};
const updateBackground = (ev) => {
switch (ev.targetTouches.length) {
case 1:
ev.target.style.background = "yellow";
break;
case 2:
ev.target.style.background = "pink";
break;
default:
ev.target.style.background = "lightblue";
}
}
const handlePinchZoom = (ev) => {
if (ev.targetTouches.length === 2 && ev.changedTouches.length === 2) {
let point1 = -1, point2 = -1;
for (let i = 0; i < tpCache.length; i++) {
if (tpCache[i].identifier === ev.targetTouches[0].identifier) point1 = i;
if (tpCache[i].identifier === ev.targetTouches[1].identifier) point2 = i;
}
if (point1 >= 0 && point2 >= 0) {
let diff1 = Math.abs(tpCache[point1].clientX - ev.targetTouches[0].clientX);
let diff2 = Math.abs(tpCache[point2].clientX - ev.targetTouches[1].clientX);
const PINCH_THRESHHOLD = ev.target.clientWidth / 10;
if (diff1 >= PINCH_THRESHHOLD && diff2 >= PINCH_THRESHHOLD)
ev.target.style.background = "green";
} else {
setTpCache([]);
}
}
}
const startHandler = (ev) => {
ev.preventDefault();
if (ev.targetTouches.length === 2) {
const newTpCache = [...tpCache];
for (let i = 0; i < ev.targetTouches.length; i++) {
newTpCache.push(ev.targetTouches[i]);
}
setTpCache(newTpCache);
}
if (logEvents) {
log("touchStart", ev, true);
}
updateBackground(ev);
};
const moveHandler = (ev) => {
ev.preventDefault();
if (logEvents) {
log("touchMove", ev, false);
}
if (!(ev.touches.length === 2 && ev.targetTouches.length === 2)) {
updateBackground(ev);
}
ev.target.style.outline = "dashed";
handlePinchZoom(ev);
};
const endHandler = (ev) => {
ev.preventDefault();
if (logEvents) {
log(ev.type, ev, false);
}
if (ev.targetTouches.length === 0) {
ev.target.style.background = "white";
ev.target.style.outline = "1px solid black";
}
};
const setHandlers = (name) => {
const el = document.getElementById(name);
el.ontouchstart = startHandler;
el.ontouchmove = moveHandler;
el.ontouchcancel = endHandler;
el.ontouchend = endHandler;
};
useEffect(() => {
setHandlers("target1");
setHandlers("target2");
setHandlers("target3");
setHandlers("target4");
}, []);
return (
<>
<h1>Multi-touch interaction</h1>
<div id="target1">{testText}</div>
<div id="target2"> Tap, Hold or Swipe me 2</div>
<div id="target3"> Tap, Hold or Swipe me 3</div>
<div id="target4"> Tap, Hold or Swipe me 4</div>
<button id="log" onClick={() => enableLog()}>Start/Stop event logging</button>
<button id="clearlog" onClick={() => clearLog()}>Clear the log</button>
<p></p>
<div>{logText}</div>
</>
);
};
export default TouchEventsContainer;
I have 2 problems:
My log output (currently in a ) doesn't produce anything. I think it's because my string contains HTML. I tried using "dangerousInnerHTML" and some other things, but to no avail.
The two finger pinch-zoom (when background color turns green) doesn't seem to be detected.
What is wrong with my React conversion?
Also I'm currently rendering my TouchEventsContainer within the context of a different JS view (on my app) for testing, but I don't think that's the issue.
I have 2 javascript files and I want to combine them. One is for dark-mode and the other one is for dark-mode switch button. Separately they work fine, but when combined dark-mode works like it should, but switch button place isn't saved after refreshing the page. What can I do to fix that?
/* DARK-MODE */
function myFunction() {
var element = document.body;
element.classList.toggle("dark-mode");
}
(function() {
let onpageLoad = localStorage.getItem("theme") || "";
let element = document.body;
element.classList.add(onpageLoad);
document.getElementById("theme").textContent = "DarkMode";
localStorage.getItem("theme") || "light";
})();
function themeToggle() {
let element = document.body;
element.classList.toggle("dark-mode");
let theme = localStorage.getItem("theme");
if (theme && theme === "dark-mode") {
localStorage.setItem("theme", "");
} else {
localStorage.setItem("theme", "dark-mode");
}
document.getElementById("theme").textContent = localStorage.getItem("theme");
};
/* DM-BUTTON */
function App() {}
App.prototype.setState = function(state) {
localStorage.setItem('checked', state);
}
App.prototype.getState = function() {
return localStorage.getItem('checked');
}
function init() {
var app = new App();
var state = app.getState();
var checkbox = document.querySelector('#toggle');
if (state == 'true') {
checkbox.checked = true;
}
checkbox.addEventListener('click', function() {
app.setState(checkbox.checked);
});
}
init();
<div class="toggle1">
<input type="checkbox" onclick="themeToggle()" class="dmbtn" id="toggle">
<label for="toggle"></label>
</div>
The do_game function is supposed to change the color of the body permanently, but instead it happens for an instant and goes back the way that it was.
let do_game = ()=>{
let colorArray = ["blue","cyan","gray","green","magenta","orange",
,"white","yellow"]
// let target = colorArray[Math.floor(Math.random() * 8)];
let target = colorArray[0];
console.log(target);
let input = validateValue(colorArray);
if(input === false){
return false;
}
if(compareInputToTarget(input, target) == false){
return false;
}
let body = document.getElementById("body");
body.style.background = target;
// window.backgroundTarget = target;
return target;
}
(function(){
document.getElementById("ok").addEventListener("click", do_game);
// body.style.background = window.backgroundTarget;
})()
Cannot reproduce based on the code you provided. The following adjustment to your code demonstrates a permanent change of color.
function validateValue() {
return true;
}
function compareInputToTarget(input, target) {
return true;
}
let do_game = ()=>{
let colorArray = ["blue","cyan","gray","green","magenta","orange",
,"white","yellow"]
// let target = colorArray[Math.floor(Math.random() * 8)];
let target = colorArray[0];
console.log(target);
let input = validateValue(colorArray);
if(input === false){
return false;
}
if(compareInputToTarget(input, target) == false){
return false;
}
let body = document.getElementById("body");
body.style.background = target;
// window.backgroundTarget = target;
return target;
}
(function(){
document.getElementById("ok").addEventListener("click", do_game);
// body.style.background = window.backgroundTarget;
})()
<body id="body">
<button id="ok">OK</button>
</body>
When searching for a way to warn a user before leaving web page if changes haven't been saved, I found this solution: https://stackoverflow.com/a/48238659/9512437, but the warning pops up even if the user is hitting the save button. I tried adding an event when the user clicks the submit button to set a variable to keep the warning from appearing, but now the warning never appears.
Here is what I tried:
<script>
"use strict";
var btn_click = false;
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size && !btn_click) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
addEventListener("")
})();
document.getElementById("submit").onclick = function save() {
btn_click = true;
}
</script>
Any idea what I did wrong?
Turns out I was doing a couple things wrong. My guess is adding the document.getElementById("submit").onclick under "use strict" either caused an error (https://www.w3schools.com/js/js_strict.asp) or caused a problem with the detection since simply adding the function even without the && !btn_click caused it to not work. I also had to change if (modified_inputs.size && !btn_click) { to if (modified_inputs.size >> 0 && !btn_click) {.
In the end, the solution that ended up working for me is as follows:
<script>
var btn_click = false;
function save() {
btn_click = true;
}
"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
const target = evt.target;
if (!(defaultValue in target || defaultValue in target.dataset)) {
target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
}
});
// detect input modifications
addEventListener("input", (evt) => {
const target = evt.target;
let original;
if (defaultValue in target) {
original = target[defaultValue];
} else {
original = target.dataset[defaultValue];
}
if (original !== ("" + (target.value || target.textContent)).trim()) {
if (!modified_inputs.has(target)) {
modified_inputs.add(target);
}
} else if (modified_inputs.has(target)) {
modified_inputs.delete(target);
}
});
addEventListener("beforeunload", (evt) => {
if (modified_inputs.size >> 0 && !btn_click) {
const unsaved_changes_warning = "Changes you made may not be saved.";
evt.returnValue = unsaved_changes_warning;
return unsaved_changes_warning;
}
});
addEventListener("")
})();
</script>
Then add your onclick to the element:
<button type="submit" class="w3-button w3-right w3-theme" id="button" onclick="save()">Save</button>
Here's the HTML button I'm working with:
<b>Other: </b><input type="number" id="AmntValue" data-target-element-id="SubmitAmnt" data-target-parameter="Amnt" onchange="setValueOnTarget(this);' +/* ' enableButton(SubmitAmnt);' */+ '">
<button class="button2" id="SubmitAmnt" type="button" data-redirect-src="https://hub.deltasigmapi.org/donations/donations.aspx?appealid=1989&NumberOfPaymentsDisplay=0&GiftRecurrenceDisplay=0&GiftRecurrence=onetime&GiftAmount=" onclick="disableButton(this); addValueToQueryString(this); redirectPage(this);">Continue To Payment</button>
When someone hits the button but the "Other" text field is blank, it's supposed to not redirect and instead show an error message. Right now the error message displays, but only for a quick moment before it redirects anyway.
Here is my complete JavaScript code:
function setValueOnTarget(sourceElem) {
var targetId = sourceElem.getAttribute('data-target-element-id');
if (targetId) {
var targetElem = document.getElementById(targetId);
if (targetElem) {
var valueToSet;
var parameterToSet;
if (sourceElem.nodeName.toUpperCase() == 'SELECT') {
valueToSet = sourceElem.options[sourceElem.selectedIndex].value;
}
if (sourceElem.nodeName.toUpperCase() == 'INPUT') {
if (sourceElem.type.toUpperCase() == 'NUMBER' || sourceElem.type.toUpperCase() == 'TEXT') {
valueToSet = sourceElem.value;
}
}
targetElem.setAttribute('data-value-set-by-other-element', valueToSet);
parameterToSet = sourceElem.getAttribute('data-target-parameter');
targetElem.setAttribute('data-target-parameter', parameterToSet);
EnableButton(targetElem)
}
}
}
function disableButton(btn) {
btn.disabled = true;
}
function EnableButton(btn) {
btn.disabled = false;
}
function addValueToQueryString(elem) {
var src = elem.getAttribute('data-redirect-src');
var newValue = elem.getAttribute('data-value-set-by-other-element');
var parameter = elem.getAttribute('data-target-parameter');
if (newValue && parameter) {
if (src && newValue && parameter) {
var newSrc;
newSrc = src + newValue;
elem.setAttribute('data-redirect-src', newSrc);
} else {
displayError('Could not find the URL to redirect to');
}
} else {
displayError('No value or parameter has been set. Please set a proper value.');
}
}
function redirectPage(elem) {
var src = elem.getAttribute('data-redirect-src');
window.location = src;
}
function displayError(message) {
var userMessage = document.getElementById('userMessage');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'red';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
function displaySuccess(message) {
var userMessage = document.getElementById('userMessage1');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'green';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
I'm not sure if something's wrong with the code I put in the button or in the JavaScript.
Disable button by default
The button should be disabled by default, and should only be enabled when the expected input value is detected. It appears you already have a mechanism for this in your example, but you have some impediments to overcome first:
button should be disabled by default. Do this in the HTML:<button disabled …>Continue To Payment</button>
input's onchange handler should just call setValueOnTarget(), because this function already calls EnableButton(). In the HTML:<input onchange="setValueOnTarget(this);" … >
Remove the call to redirectPage() from the button's onclick handler and move it into addValueToQueryString() after you have assigned a value to newSrc.
Add a call to EnableButton() after you call displayError() in cases where you want to allow the user to modify the input and try again.
For example:
function setValueOnTarget(sourceElem) {
var targetId = sourceElem.getAttribute('data-target-element-id');
if (targetId) {
var targetElem = document.getElementById(targetId);
console.log(targetElem);
if (targetElem) {
var valueToSet;
var parameterToSet;
if (sourceElem.nodeName.toUpperCase() == 'SELECT') {
valueToSet = sourceElem.options[sourceElem.selectedIndex].value;
}
if (sourceElem.nodeName.toUpperCase() == 'INPUT') {
if (sourceElem.type.toUpperCase() == 'NUMBER' || sourceElem.type.toUpperCase() == 'TEXT') {
valueToSet = sourceElem.value;
}
}
targetElem.setAttribute('data-value-set-by-other-element', valueToSet);
parameterToSet = sourceElem.getAttribute('data-target-parameter');
targetElem.setAttribute('data-target-parameter', parameterToSet);
EnableButton(targetElem);
}
}
}
function disableButton(btn) {
btn.disabled = true;
}
function EnableButton(btn) {
btn.disabled = false;
}
function addValueToQueryString(elem) {
var src = elem.getAttribute('data-redirect-src');
var newValue = elem.getAttribute('data-value-set-by-other-element');
var parameter = elem.getAttribute('data-target-parameter');
if (newValue && parameter) {
if (src && newValue && parameter) {
var newSrc;
newSrc = src + newValue;
elem.setAttribute('data-redirect-src', newSrc);
redirectPage(elem);
} else {
displayError('Could not find the URL to redirect to');
}
} else {
displayError('No value or parameter has been set. Please set a proper value.');
EnableButton(elem);
}
}
function redirectPage(elem) {
var src = elem.getAttribute('data-redirect-src');
window.location = src;
}
function displayError(message) {
var userMessage = document.getElementById('userMessage');
userMessage.innerHTML = message;
userMessage.style.backgroundColor = 'red';
userMessage.style.color = 'white';
userMessage.style.display = 'block';
}
<b>Other: </b>
<input
type="number"
id="AmntValue"
data-target-element-id="SubmitAmnt"
data-target-parameter="Amnt"
onchange="setValueOnTarget(this);">
<button
disabled
class="button2"
id="SubmitAmnt"
type="button"
data-redirect-src="https://hub.deltasigmapi.org/donations/donations.aspx?appealid=1989&NumberOfPaymentsDisplay=0&GiftRecurrenceDisplay=0&GiftRecurrence=onetime&GiftAmount="
onclick="disableButton(this); addValueToQueryString(this);">Continue To Payment</button>
<div id="userMessage"></div>