I have this very "not clean" code where I open a modal with an animation:
const modal = document.getElementById("modal");
const modalDarkBackground = document.getElementById("modalDarkBackground");
document.getElementById("openModal").onclick = function() {
modal.style.opacity = "1";
modal.style.zIndex = "9999";
modal.style.transform = "translate(-50%, -50%) scale(1)";
modalDarkBackground.style.zIndex = "9998";
modalDarkBackground.style.opacity = "1";
}
document.getElementById("closeModal").onclick = function() {
modal.style.transform = "translate(-50%, -50%) scale(1.1)";
setTimeout(function() {
modal.style.opacity = "0";
modal.style.zIndex = "-1";
modal.style.transform = "translate(-50%, -50%) scale(0)";
modalDarkBackground.style.zIndex = "-1";
modalDarkBackground.style.opacity = "0";
}, 500);
}
* {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
#modalDarkBackground {
height: 100vh;
width: 100%;
background: rgba(0,0,0,0.5);
transition: all 0.5s;
position: absolute;
top: 0;
left: 0;
opacity: 0;
z-index: -1;
}
#modal {
width: 300px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
overflow: hidden;
transition: all 0.5s;
opacity: 0;
background: #fff;
}
#modal h1 {
background: #E6E6E6;
color: #ff0C0C;
font-size: 20px;
padding: 0.5em;
border-bottom: 1px solid #C0C0C0;
}
#modal p {
min-height: 70px;
padding: 0.5em;
color: #ff0C0C;
font-weight: 400;
font-size: 15px;
}
#modal button {
display: block;
width: 50%;
padding: 0.5em 1em;
background: #EDB44C;
color: #fff;
font-size: 15px;
margin: 0 auto 0.5em auto;
border: 1px solid #BEBEBE;
border-radius: 3px;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<button id="openModal">
Open Modal
</button>
<div id="modalDarkBackground"></div>
<div id="modal">
<h1>Error</h1>
<p id="modalContent">
You do not seem to have an Internet connection.
Please check your connection.
</p>
<button id="closeModal">
OK
</button>
</div>
I noticed that my JavaScript code is very bad with inline styles and way too long. Is there a way to optimize this JavaScript code with e.g. AddClass and maybe make a single function out of it? Sorry, I am a JavaScript newbie
Thanks in advance!
Instead of hard-wiring the effected DOM nodes with their intended behavior, and instead of CSS-data baked into the JavaScript code as well, one could give a component (DOM) and module (JavaScript) based approach more than just a single thought.
On top one leaves the animation behavior entirely to the CSS by targeting this behavior via a common set of css class names which will be used by the JavaScript logic.
Of cause one has to start with a more generic and better structured markup. Especially the nesting is crucial for how one can target the layout rules, and even more, how one can achieve a fine grained targeting of element specific css transformations.
Thus, the OP's original CSS got slightly reassembled according to the new markup.
The OP might also notice the class name specific rules for/under active, before-deactivation and deactivating which each target a specific state of a just or still raised modal component and which are customizable via related data-* attributes at every modal component's root node.
The idea behind a modals-module is convenience and flexibility. The module internally stores modal items by each item's corresponding element-node.
The module features just two methods get and initialize. The latter identifies all modal related element-nodes by their specific data-component-modal attribute and creates a node specific modal-item.
Thus every modal related element-node can be used by the module's get method, either to retrieve an already existing modal-item, or to create, store and get a new element-node specific modal-item.
Each item owns exactly two methods activate and deactivate which literally trigger a behavior according to its naming/wording.
Thus the component approach allows the usage of more than just one modal component within one and the same document. It allows other program logic or components to retrieve and use modal-items individually by their element-node reference and e.g. trigger modal behavior like raising/opening (activate) or closing (deactivate) the modal related element-node.
The implementation of the final executable snippet's main function does demonstrate the just said exemplarily ...
function main() {
modals.initialize();
document
.querySelectorAll('[data-activate-modal]')
.forEach(triggerNode => document
.querySelectorAll(triggerNode.dataset.activateModal)
.forEach(componentNode => triggerNode
.addEventListener('click', modals.get(componentNode).activate)
)
);
}
... modals does get initialized first. Then there are some buttons which we want to be triggers for raising different modals. Such a button/trigger gets identified by its data-activate-modal attribute. And the value of the latter will be used as element-query for the targeted modal. For instance a button like ...
<button data-activate-modal='#warningModal'>
Open Warning Modal
</button>
... does target any modal which gets queried by '#warningModal' which of cause for the example is just a sole modal component due to the used id based selector.
Finally any triggering element is going to handle any of its targeted modal-items by activate-ing each related item on such a trigger's e.g. click event.
But of cause there are many more options left with the approach of storing and retrieving modal items via their related element-node references.
// `modals` module.
const modals = (function () {
function getSafeInteger(value) {
value = parseInt(value, 10);
return Number.isSafeInteger(value) ? value : 0;
}
function activateBoundModalTarget() {
this.classList.add('active');
}
function deactivateBoundModalTarget() {
const modalNode = this;
let {
deactivationDuration: delayDefault,
deactivationDelay: delayBefore,
} = modalNode.dataset;
delayDefault = getSafeInteger(delayDefault);
delayBefore = getSafeInteger(delayBefore);
modalNode.classList.add('before-deactivation', 'deactivating');
setTimeout(() => {
modalNode.classList.remove('active', 'before-deactivation');
setTimeout(() =>
modalNode.classList.remove('deactivating'), delayDefault
);
}, delayBefore);
}
// `modal` item/type factory
function createModal(rootNode) {
const activate = activateBoundModalTarget.bind(rootNode);
const deactivate = deactivateBoundModalTarget.bind(rootNode);
rootNode
.querySelectorAll('[data-trigger-inactive]')
.forEach(triggerNode => triggerNode
.addEventListener('click', deactivate)
);
return {
rootNode,
activate,
deactivate
}
}
const storage = new WeakMap;
function getModal(rootNode) {
let modal = storage.get(rootNode);
if (
!modal &&
(rootNode instanceof HTMLElement) &&
rootNode.hasAttribute('data-component-modal')
) {
if (modal = createModal(rootNode)) {
storage.set(rootNode, modal);
}
}
return modal ?? null;
}
function initialize() {
document
.querySelectorAll('[data-component-modal]')
.forEach(getModal);
}
// export module.
return {
get: getModal,
initialize,
};
}());
function main() {
modals.initialize();
document
.querySelectorAll('[data-activate-modal]')
.forEach(triggerNode => document
.querySelectorAll(triggerNode.dataset.activateModal)
.forEach(componentNode => triggerNode
.addEventListener('click', modals.get(componentNode).activate)
)
);
}
main();
* {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
[data-component-modal] {
z-index: -1;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
[data-component-modal] .background {
opacity: 0;
z-index: 0;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100vh;
background: rgba(192, 192, 192, 0.9);
transition: all 0.5s;
}
[data-component-modal] .content {
zoom: .8;
opacity: 0;
z-index: 1;
position: absolute;
left: 50%;
top: 50%;
width: 300px;
background: #fff;
overflow: hidden;
transition: all 0.5s;
transform: translate(-50%, -50%) scale(1);
}
[data-component-modal].deactivating,
[data-component-modal].active {
z-index: 99;
}
[data-component-modal].active .background,
[data-component-modal].active .content {
opacity: 1;
}
[data-component-modal].active.before-deactivation .content {
transform: translate(-50%, -50%) scale(1.1);
}
[data-component-modal].error .background {
background: rgba(255, 192, 192, 0.9);
}
[data-component-modal] .content h1 {
background: #E6E6E6;
font-size: 20px;
padding: 0.5em;
border-bottom: 1px solid #C0C0C0;
}
[data-component-modal] .content p {
min-height: 70px;
padding: 0.5em;
font-weight: 400;
font-size: 15px;
}
[data-component-modal].error .content h1,
[data-component-modal].error .content p {
color: #ff0C0C;
}
[data-component-modal] .content button {
display: block;
width: 50%;
padding: 0.5em 1em;
background: #EDB44C;
color: #fff;
font-size: 15px;
margin: 0 auto 0.5em auto;
border: 1px solid #BEBEBE;
border-radius: 3px;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<button data-activate-modal='#errorModal'>
Open Error Modal
</button>
<button data-activate-modal='#warningModal'>
Open Warning Modal
</button>
<button data-activate-modal='#errorModal'>
Open Error Modal
</button>
<button data-activate-modal='#warningModal'>
Open Warning Modal
</button>
<div
data-component-modal
data-deactivation-duration='500'
id="errorModal"
class="error"
>
<div class="content">
<h1>Error</h1>
<p id="modalContent">
You do not seem to have an Internet connection.
Please check your connection.
</p>
<button data-trigger-inactive>
OK
</button>
</div>
<div class="background"></div>
</div>
<div
data-component-modal
data-deactivation-duration='500'
data-deactivation-delay='500'
id="warningModal"
>
<div class="content">
<h1>Warning</h1>
<p id="modalContent">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<button data-trigger-inactive>
OK
</button>
<button data-trigger-inactive>
close
</button>
</div>
<div class="background"></div>
</div>
You can describe transition in css classes and add/remove them to show/hide the modal
const modal = document.getElementById("modal");
const modalDarkBackground = document.getElementById("modalDarkBackground");
const show = () => {
modal.classList.add('show');
modalDarkBackground.classList.add('show');
};
const hide = () => {
modal.classList.add('prepare-hide');
setTimeout(function() {
modal.classList.remove('prepare-hide');
modal.classList.remove('show');
modalDarkBackground.classList.remove('show');
}, 500);
};
modalDarkBackground.onclick = hide;
document.getElementById("openModal").onclick = show;
document.getElementById("closeModal").onclick = hide;
* {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
#modalDarkBackground {
height: 100vh;
width: 100%;
background: rgba(0,0,0,0.5);
transition: all 0.5s;
position: absolute;
top: 0;
left: 0;
opacity: 0;
z-index: -1;
}
#modal {
width: 300px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
overflow: hidden;
transition: all 0.5s;
opacity: 0;
background: #fff;
}
#modal h1 {
background: #E6E6E6;
color: #ff0C0C;
font-size: 20px;
padding: 0.5em;
border-bottom: 1px solid #C0C0C0;
}
#modal p {
min-height: 70px;
padding: 0.5em;
color: #ff0C0C;
font-weight: 400;
font-size: 15px;
}
#modal button {
display: block;
width: 50%;
padding: 0.5em 1em;
background: #EDB44C;
color: #fff;
font-size: 15px;
margin: 0 auto 0.5em auto;
border: 1px solid #BEBEBE;
border-radius: 3px;
}
#modalDarkBackground.show {
z-index: 9998;
opacity: 1;
}
#modal.show {
opacity: 1;
z-index: 9999;
transform: translate(-50%, -50%) scale(1);
}
#modal.prepare-hide {
transform: translate(-50%, -50%) scale(1.1);
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<button id="openModal">
Open Modal
</button>
<div id="modalDarkBackground"></div>
<div id="modal">
<h1>Error</h1>
<p id="modalContent">
You do not seem to have an Internet connection. Please check your connection.
</p>
<button id="closeModal">
OK
</button>
</div>
You can rewrite the JS code using style classes.
And I moved styles from JS to a style block.
Now the JS looks better.
const modal = document.getElementById('modal');
const modalDarkBackground = document.getElementById('modalDarkBackground');
document.getElementById('openModal').onclick = function() {
modal
.classList.add('openModal');
modalDarkBackground
.classList.add('modalDarkBackgroundOpen');
}
document.getElementById('closeModal').onclick = function() {
modal
.classList.add('transformScale');
setTimeout(function() {
modal
.classList.replace('openModal', 'closeModal');
modalDarkBackground
.classList.replace('modalDarkBackgroundOpen', 'modalDarkBackgroundClose');
}, 500);
}
* {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
#modalDarkBackground {
height: 100vh;
width: 100%;
background: rgba(0,0,0,0.5);
transition: all 0.5s;
position: absolute;
top: 0;
left: 0;
opacity: 0;
z-index: -1;
}
#modal {
width: 300px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
overflow: hidden;
transition: all 0.5s;
opacity: 0;
background: #fff;
}
#modal h1 {
background: #E6E6E6;
color: #ff0C0C;
font-size: 20px;
padding: 0.5em;
border-bottom: 1px solid #C0C0C0;
}
#modal p {
min-height: 70px;
padding: 0.5em;
color: #ff0C0C;
font-weight: 400;
font-size: 15px;
}
#modal button {
display: block;
width: 50%;
padding: 0.5em 1em;
background: #EDB44C;
color: #fff;
font-size: 15px;
margin: 0 auto 0.5em auto;
border: 1px solid #BEBEBE;
border-radius: 3px;
}
#modal.openModal {
opacity: 1;
z-index: 9999;
transform: translate(-50%, -50%) scale(1);
}
#modal.closeModal {
opacity: 0;
z-index: -1;
transform: translate(-50%, -50%) scale(0);
}
#modal.transformScale {
transform: translate(-50%, -50%) scale(1.1);
}
#modalDarkBackground.modalDarkBackgroundOpen {
z-index: 9998;
opacity: 1;
}
#modalDarkBackground.modalDarkBackgroundClose {
z-index: -1;
opacity: 0;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<button id="openModal">
Open Modal
</button>
<div id="modalDarkBackground"></div>
<div id="modal">
<h1>Error</h1>
<p id="modalContent">
You do not seem to have an Internet connection.
Please check your connection.
</p>
<button id="closeModal">
OK
</button>
</div>
You can add a close class while closing the modal, which can have different transition property - this property will use cubic-berzier to give the effect of expanding the modal, before completely contracting it. Then you can use setTimeout to remove the close class.
Here is the code:
const modal = document.getElementById("modal");
const modalDarkBackground = document.getElementById("modalDarkBackground");
document.getElementById("openModal").onclick = function () {
modal.classList.add("open");
modalDarkBackground.classList.add("open");
}
document.getElementById("closeModal").onclick = function () {
modal.classList.add("close");
modal.classList.remove("open");
modalDarkBackground.classList.remove("open");
setTimeout(function () { modal.classList.remove("close"); }, 500);
}
* {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
#modalDarkBackground {
height: 100vh;
width: 100%;
background: rgba(0, 0, 0, 0.5);
transition: all 0.5s;
position: absolute;
top: 0;
left: 0;
opacity: 0;
z-index: -1;
}
#modalDarkBackground.open {
opacity: 1;
z-index: 9998;
}
#modal {
width: 300px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
overflow: hidden;
transition: all 0.5s;
opacity: 0;
background: #fff;
}
#modal.open {
opacity: 1;
z-index: 9999;
transform: translate(-50%, -50%) scale(1);
}
#modal.close {
opacity: 0;
transform: translate(-50%, -50%) scale(0);
transition: all 0.5s cubic-bezier(0.17, -0.16, 0.54, -0.42) !important;
}
#modal h1 {
background: #E6E6E6;
color: #ff0C0C;
font-size: 20px;
padding: 0.5em;
border-bottom: 1px solid #C0C0C0;
}
#modal p {
min-height: 70px;
padding: 0.5em;
color: #ff0C0C;
font-weight: 400;
font-size: 15px;
}
#modal button {
display: block;
width: 50%;
padding: 0.5em 1em;
background: #EDB44C;
color: #fff;
font-size: 15px;
margin: 0 auto 0.5em auto;
border: 1px solid #BEBEBE;
border-radius: 3px;
}
<button id="openModal">
Open Modal
</button>
<div id="modalDarkBackground"></div>
<div id="modal">
<h1>Error</h1>
<p id="modalContent">
You do not seem to have an Internet connection. Please check your connection.
</p>
<button id="closeModal">
OK
</button>
</div>
Related
I have looked about for quite a while now, and nothing seems to work or fit my current problem.
I am creating a To-Do project and I want chrome to remember your To-Dos, even if you refresh the browser.
I have tried a few methods, but this is where I am at the moment. The javascript updates the localstorage everytime the 'createPost' function is called, and then it is loaded by the code at the bottom (i flagged the relavant lines).
View the updated demo here: https://dominicody.netlify.app/todo/
( the main website isnt finished so dont judge please lol)
let form = document.getElementById("todo-form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let todos = document.getElementById("todos");
let right = document.getElementById("right")
let noToDo = document.getElementById("notodo")
let counter = 0; /* new line */
form.addEventListener("submit", e => {
e.preventDefault();
console.log("button clicked");
formValidation();
});
let formValidation = () => {
if (input.value === "") {
msg.innerHTML = "To-Do cannot be blank";
} else {
msg.innerHTML = "";
acceptData();
}
};
let data = [];
let acceptData = () => {
data = [...data, {
"text": input.value
}];
input.value = "";
localStorage.setItem('todoItemsRef', JSON.stringify(data));
createToDo(data[data.length - 1]);
};
let createToDo = () => {
counter++; /* new line */
todos.innerHTML += `
<div>
<p>${data.text}</p>
<span class="options">
<i onClick="completedToDo(this)" class='bx bx-check-circle'></i>
<i onClick="editToDo(this)" class='bx bx-edit-alt'></i>
<i onClick="deleteToDo(this)" class='bx bx-trash' ></i>
</span>
</div>
`;
input.value = "";
};
let completedToDo = (e) => {
e.parentElement.parentElement.classList.toggle('completed')
}
let deleteToDo = (e) => {
e.parentElement.parentElement.remove();
counter--;
if (counter === 0) {
showNoToDo();
}
};
let editToDo = (e) => {
input.value = e.parentElement.previousElementSibling.innerHTML;
e.parentElement.parentElement.remove();
};
let hideNoToDo = () => {
noToDo.classList.add('hidden')
form.classList.remove('form-reg-position')
}
let showNoToDo = () => {
noToDo.classList.remove('hidden')
form.classList.add('form-reg-position')
}
/* localStorage.setItem('todoItemsRef', JSON.stringify(data)); */
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('todoItemsRef');
if (ref) {
data = JSON.parse(ref);
data.forEach(t => {
createToDo(t);
});
}
});
#import url('https://fonts.googleapis.com/css2?family=Kanit:wght#100;200;300;400;500;600;700;800;900&family=League+Spartan:wght#200;300;400;500;600;700;800;900&family=Noto+Serif:wght#400;700&display=swap');
:root {
--primary-color: rgb(240, 69, 114);
--primary-color-ligh: rgb(220, 100, 124);
--secondary-color: rgb(25, 24, 44);
--secondary-color-ligh: rgb(64, 64, 83);
--white: rgb(255, 255, 255);
--primary-font: 'League Spartan', sans-serif;
--secondary-font: 'Noto Serif', serif;
--kanit-font: 'Kanit', sans-serif;
--swiper-theme-color: rgb(240, 69, 114);
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'League Spartan', sans-serif;
}
body {
width: 100%;
background-color: #fff;
max-height: 100%;
overflow: hidden;
}
h1 {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
font-size: 4rem;
opacity: 0.5;
}
.todo-main {
position: absolute;
top: 30%;
left: 50%;
width: 75%;
height: 70%;
transform: translateX(-50%);
overflow-y: auto;
overflow-x: hidden;
}
.no-todo {
position: absolute;
top: 23%;
left: 50%;
transform: translateX(-50%);
text-align: center;
}
.no-todo img {
width: 150px;
}
.no-todo p {
font-family: var(--primary-font);
font-weight: 600;
font-size: 1.3rem;
margin-top: 24px;
margin-bottom: 10px;
}
.no-todo p2 {
font-family: var(--primary-font);
font-weight: 600;
font-size: 1.2rem;
color: #000;
opacity: 0.6;
}
.no-todo.hidden {
display: none;
}
form {
margin-left: 100%;
transform: translateX(-50%);
width: 100%;
;
}
.textarea {
margin-top: 24px;
min-width: 95%;
max-width: 95%;
min-height: 40px;
max-height: 40px;
display: flex;
outline: none;
border: none;
border: 2px solid var(--secondary-color);
border-radius: 5px;
padding: 10px;
font-size: 20px;
font-weight: 400;
margin-top: 5px;
color: #000;
transform: translateX(-50%);
overflow-y: hidden;
transition-duration: 0.7s;
}
.form-reg-position {
transform: translateY(250%) translateX(-50%);
transition-duration: 0.7s;
}
.textarea::placeholder {
color: #000;
}
#todos div {
display: flex;
align-items: center;
justify-content: space-between;
transform: translateX(-50%);
font-family: var(--kanit-font);
font-weight: 500;
font-size: 1.2rem;
margin-bottom: 15px;
width: 95%;
margin-left: 50%;
border: 2px solid var(--secondary-color);
border-radius: 5px;
padding: 8px;
color: #000;
}
#todos div.completed {
background-color: #00FF00;
color: #000;
}
.options {
display: flex;
gap: 25px;
color: var(--secondary-color);
}
i {
cursor: pointer;
}
#msg {
position: absolute;
left: 0%;
transform: translateX(-50%);
color: red;
margin-bottom: 10px;
}
/* TOP NAV */
.top-nav-img-only {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 8%;
display: flex;
align-items: center;
z-index: 99;
}
.top-nav-img-only a img {
position: absolute;
top: 20%;
left: 5%;
height: 60%;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bydom</title>
<link rel="stylesheet" href="/todo/todo.css">
<link href='https://unpkg.com/boxicons#2.1.2/css/boxicons.min.css' rel='stylesheet'>
</head>
<body>
<div class="top-nav-img-only">
<a href="/">
<img src="/images/logo.png">
</a>
</div>
<h1>todos</h1>
<div class="no-todo" id="notodo">
<img src="/svg/checklist.svg">
<p>Add your first To-Do</p>
<p2>What's first on your list?</p2>
</div>
<div class="todo-main">
<form class="form-reg-position" id="todo-form">
<br><br>
<input class="textarea" type="text" name="todo" id="input" placeholder="Add new To-do"></input>
<br><br>
<div id="msg"></div>
</form>
<div id="todos">
</div>
</div>
<script src="/todo/todo.js"></script>
</body>
</html>
The first thing you need to do is update Local Storage whenever you change something in the tasks.
You can create a function to update Local Storage and this function will be called whenever you update something in the tasks, when you mark it as complete and when you delete a task.
const updateLocalStorage = () => {
}
This function will pick up all the tasks that are on the screen
Add task in Local Storage
localStorage.setItem("tasks", JSON.stringify([...JSON.parse(localStorage.getItem("tasks") || "[]"), { task: task.value, completed: false }]));
There seem to be a few issues with the example/code provided, but the main thing seems to be how you were trying to store the todo list. Your list was an array called data and when each new todo item was created, it would overwrite the previous value of data["text"]. Also, because of the way you structured your array (which was more like an object), you could not stringify the list.
So the first fix was to make your array an array of objects [{},{}]. This would allow each item on the list to have its own text value.
Another issue was calling a function that didn't exist renderTodo. (OP did mention in a comment this was later corrected).
There are also a lot of weird ways some of the code is structured, but with all of that aside, here is a basic example of what you seem to need. I removed functions and code that wasn't necessary for the question asked. This solution will save your todo list in localStorage and load it when the page loads. Anything else is a different question/topic.
let form = document.getElementById("todo-form"),
input = document.getElementById("input"),
msg = document.getElementById("msg"),
todos = document.getElementById("todos"),
data = [];
form.addEventListener("submit", e => {
e.preventDefault();
formValidation();
});
let formValidation = () => {
if (input.value === "") {
msg.innerHTML = "To-Do cannot be blank";
} else {
msg.innerHTML = "";
acceptData();
}
};
let acceptData = () => {
data = [...data, {"text": input.value}];
input.value = "";
localStorage.setItem('todoItemsRef', JSON.stringify(data));
renderTodo(data[data.length-1]);
};
let renderTodo = (t) => {
let todo = document.createElement("div");
todo.innerHTML = `<p>${t.text}</p>`;
todos.append(todo);
};
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('todoItemsRef');
if(ref) {
data = JSON.parse(ref);
data.forEach(t => {
renderTodo(t);
});
}
});
:root {
--primary-color: rgb(240, 69, 114);
--primary-color-ligh: rgb(220, 100, 124);
--secondary-color: rgb(25, 24, 44);
--secondary-color-ligh: rgb(64, 64, 83);
--white: rgb(255, 255, 255);
--primary-font: 'League Spartan', sans-serif;
--secondary-font: 'Noto Serif', serif;
--kanit-font: 'Kanit', sans-serif;
--swiper-theme-color: rgb(240, 69, 114);
}
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: 'League Spartan', sans-serif;
}
body {
width: 100%;
background-color: #fff;
max-height: 100%;
overflow: hidden;
}
h1 {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
font-size: 4rem;
opacity: 0.5;
}
.todo-main {
position: absolute;
top: 30%;
left: 50%;
width: 75%;
height: 70%;
transform: translateX(-50%);
overflow-y: auto;
overflow-x: hidden;
}
.textarea {
margin-top: 24px;
min-width: 95%;
max-width: 95%;
min-height: 40px;
max-height: 40px;
display: flex;
outline: none;
border: none;
border: 2px solid var(--secondary-color);
border-radius: 5px;
padding: 10px;
font-size: 20px;
font-weight: 400;
margin-top: 5px;
color: #000;
transform: translateX(-50%);
overflow-y: hidden;
transition-duration: 0.7s;
}
form {
margin-left: 100%;
transform: translateX(-50%);
width: 100%;
}
#todos div {
display: flex;
align-items: center;
justify-content: space-between;
transform: translateX(-50%);
font-family: var(--kanit-font);
font-weight: 500;
font-size: 1.2rem;
margin-bottom: 15px;
width: 95%;
margin-left: 50%;
border: 2px solid var(--secondary-color);
border-radius: 5px;
padding: 8px;
color: #000;
}
<h1>todos</h1>
<div class="todo-main">
<form class="form-reg-position" id="todo-form">
<br><br>
<input class="textarea" type="text" name="todo" id="input" placeholder="Add new To-do">
<br><br>
<div id="msg"></div>
</form>
<div id="todos"></div>
</div>
NOTE This example will not run on Stack Overflow because of a lack of access to the localStorage object.
EDIT I also have a relevant CodePen with more of the original code for reference. localStorage will function in this CodePen example.
I am trying to create a pop-up that will alert users that the site is not for children. I have created the codes but it's not working
HTML
<botton>
<button data-modal-target="#modal">Open modal</button>
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">Parental Guidance Required</div>
<button data-close-button class="close-button">×</button>
</div>
<div class="modal-body">
This website has not been designed for the use of underage children unsupervised. Hence we recommend children be supervised by their parents, guidance, teacher, or another responsible adult. We shall not be responsible for any consequence if this warning is ignored.
</div>
</div>
<div id="overlay"></div>
</botton>
<script src="./assets/js/script.js"></script>
CSS
I am trying to create a pop-up that will alert users that the site is not for children. I have created the codes but it's not working
*,*::after,*::before {
box-sizing: border-box;
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale (0);;
transition: 200ms ease-in-out;
border: 1px solid black;
border-radius: 10px;
z-index: 10;
background-color: white; align-content: center;
width: 500px;
max-width: 80%;
}
.modal.active {
transform: translate(-50%, -50%) scale (1);;
}
.modal-header {
padding: 10px 15px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid black
}
.modal-header.title {
font-size: 1.25rem;
font-weight: bold;
}
.modal-header .close-button {
cursor: pointer;
border: none;
outline: none;
background: none;
font-size:1.25rem;
font-weight: bold;
}
.modal-body {
padding: 10px 15px;
}
#overlay {
position: fixed;
opacity: 0;
transition: 200ms ease-in-out;
top: 0;
left: 0;
right:0;
bottom: 0
Background-color rgba(0, 0, 0, 0.5)
pointer-events none
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
}
javascript
I am trying to create a pop-up that will alert users that the site is not for children. I have created the codes but it's not working
//modal button
const openModalButtons = document.querySelectorAll('[data-modal-target]')
const closeModalButtons = document.querySelectorAll('[data-close-button]')
const overlay = document.getElementById('overlay')
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = document.querySelector (button.dataset.modalTarget)
openModal (modal)
})
})
overlay.addEventListener('click', () => {
const modals = document.querySelectorAll('.modal.active')
modals.forEach(modal =>{
closeModal(modal)
})
})
closeModalButtons.forEach(button => {
button.addEventListener('click',()=> {
const modal = button.closest('.modal')
closeModal (modal)
})
})
function openModal(modal) {
if (modal==null) return
modal.classList.add('active')
overlay.classList.add('active')
}
function closeModal (modal) {
if (modal==null) return
modal.classList.remove('active')
overlay.classList.remove('active')
}
const openModalButton = document.querySelector("[data-modal-target]");
const closeModalButton = document.querySelector("[data-close-button]");
const modal = document.querySelector(".modal");
const overlay = document.getElementById("overlay");
openModalButton.addEventListener("click", openModal);
closeModalButton.addEventListener("click", closeModal);
function openModal() {
modal.classList.add("active");
overlay.classList.add("active");
}
function closeModal() {
modal.classList.remove("active");
overlay.classList.remove("active");
}
*,
*::after,
*::before {
box-sizing: border-box;
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale (0);
transition: 200ms ease-in-out;
border: 1px solid black;
border-radius: 10px;
z-index: 10;
background-color: white;
align-content: center;
width: 500px;
max-width: 80%;
display:none;
}
.modal.active {
transform: translate(-50%, -50%) scale (1);
display:block;
}
.modal-header {
padding: 10px 15px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid black;
}
.modal-header.title {
font-size: 1.25rem;
font-weight: bold;
}
.modal-header .close-button {
cursor: pointer;
border: none;
outline: none;
background: none;
font-size: 1.25rem;
font-weight: bold;
}
.modal-body {
padding: 10px 15px;
}
#overlay {
position: fixed;
opacity: 0;
transition: 200ms ease-in-out;
top: 0;
left: 0;
right: 0;
bottom: 0 Background-color rgba(0, 0, 0, 0.5) pointer-events none;
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
<botton>
<button data-modal-target="#modal">Open modal</button>
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">Parental Guidance Required</div>
<button data-close-button class="close-button">×</button>
</div>
<div class="modal-body">
This website has not been designed for the use of underage children unsupervised. Hence we recommend children be supervised by their parents, guidance, teacher, or another responsible adult. We shall not be responsible for any consequence if this warning is ignored.
</div>
</div>
<div id="overlay"></div>
</botton>
I removed loops and added display property to modal's active and inactive state in CSS.
I'm working on a project for computer science where I have to make a website. For this, me and my partner wanted a modal with information about us. One of the requirements is that we make the site work on different platforms. This is where the problem lies. Opening the modal on my phone while having the phone standing up works fine, but when transitioning to landscape it breaks and it does not return to normal when I turn it back.
Here is a snippet of the html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Machine Learning</title>
<script src="scripts/embedHTML.js"></script>
<script src="scripts/popUp.js"></script>
<script src="scripts/underline.js"></script>
<script src="scripts/modal.js"></script>
<link rel="stylesheet" href="stylesheets/main.css" />
<link rel="stylesheet" href="stylesheets/about-us.css" />
<link rel="stylesheet" href="stylesheets/popup.css" />
<link rel="stylesheet" href="stylesheets/modal.css" />
<link rel="shortcut icon" type="image/ico" href="images/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<header>
<img id="logo" src="images/logo.png" alt="logo image"></img>
<h1 id="title">Machine Learning</h1>
</header>
<article id="about-tijmen" class="modal-data" embed-html="pages/Tijmen.html"></article>
<article id="about-marijn" class="modal-data" embed-html="pages/Marijn.html"></article>
<div id="glass-pane" class="glass-pane"></div>
<article id="modal" class="modal">
<button id="closeModalButton" onclick="toggleModal()">x</button>
<article id="modal-dialogue"></article>
</article>
<script>
embedHTML();
</script>
</body>
</html>
The CSS:
.modal-data
{
display: none;
}
.modal {
pointer-events: none;
opacity: 0;
position: fixed;
z-index: 1000;
background: red;
border-radius: 20px;
border: 2px solid black;
margin-top: 0px;
padding-bottom: 20px;
left: 50%;
top: 50%;
transition: opacity .25s linear, display .25s linear;
transform: translateX(-50%) translateY(-50%);
overflow: hidden;
box-shadow: 0 0 100px 10px black inset;
width: 80vw;
}
.modal.open
{
pointer-events: all;
opacity: 100;
}
.glass-pane
{
pointer-events: none;
transition: opacity .25s linear;
background: rgba(0, 102, 255, 0.5);
z-index: 110;
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
opacity: 0;
}
.glass-pane.shown
{
pointer-events: all;
opacity: 1;
}
#closeModalButton {
position: absolute;
top: 0;
right: 0;
width: 40px;
line-height: 20px;
margin: 0;
border: 2px solid black;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-left-radius: 20px;
cursor: pointer;
font-size: 15px;
}
#modal-dialogue p {
color: rgb(250, 232, 235);
width: 90%;
margin: auto;
margin-top: 2%;
}
ol{
color: rgb(250, 232, 235);
display: table;
margin: 0 auto;
}
The javascript:
function toggleModal()
{
let modal = document.getElementById("modal");
let glass = document.getElementById("glass-pane")
if(modal.classList.contains("open"))
{
modal.classList.remove("open");
glass.classList.remove("shown");
}
else
{
modal.classList.add("open");
glass.classList.add("shown");
}
}
function setModalContent(id)
{
let dialogue = document.getElementById("modal-dialogue");
let data = document.getElementById(id);
dialogue.innerHTML = data.innerHTML;
}
and, of course, a video showing the problem.
https://youtu.be/s3bsN72-qYE
You can add css max-height and overflow-y to your modal content to give the content a scrollbar inside the modal and ensure the modal doesn't go outside the page.
This may need an #media query, eg
article {
overflow-y: auto;
}
#media screen (height:300px) {
article {
max-height: 200px;
}
}
#media screen (height:600px) {
article {
max-height: 400px;
}
}
or may be set better using relative height such as % or vh (reference)
article {
max-height: 75vh;
overflow-y: auto;
}
I was trying to create an animated hamburger nav menu. The functionality is simple. It will be in a fixed position & incorporate in a circle. When I click that circle the background will overlap the entire screen & nav links will be visible. I have used pseudo-class to create functionality. it works well, but a little mistake appeared. When I clicked the nav links the background should scale to 0. But it is nowhere can be done using CSS. So I used JS click event for it. It worked for the first try after that the menu keep scaled to 0 unit. I need to fix it so that the js click event functionality reset itself on every try. The link is given below.
codepen here
Code-
const navElement = document.querySelector('.navigation__nav');
const navBackground = document.querySelector('.navigation__background');
const navgationList = document.querySelectorAll('.navigation__item');
navgationList.forEach(function(items) {
let x = items;
items.addEventListener('click', closeNav);
function closeNav() {
navElement.style.transform = 'scale(0)';
navBackground.style.transform = 'scale(0)';
}
});
body {
background-color: cyan;
}
.navigation__checkbox {
display: none;
}
.navigation__button {
background-color: #fff;
height: 7rem;
width: 7rem;
position: fixed;
top: 6rem;
right: 6rem;
border-radius: 50%;
z-index: 2000;
box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.1);
text-align: center;
cursor: pointer;
}
#media only screen and (max-width: 56.25em) {
.navigation__button {
top: 4rem;
right: 4rem;
}
}
#media only screen and (max-width: 37.5em) {
.navigation__button {
top: 3rem;
right: 3rem;
}
}
.navigation__background {
height: 6rem;
width: 6rem;
border-radius: 50%;
position: fixed;
top: 6.5rem;
right: 6.5rem;
background-image: radial-gradient(#4ea5f7, #0400ff);
z-index: 1000;
transition: transform 0.8s cubic-bezier(0.86, 0, 0.07, 1);
}
#media only screen and (max-width: 56.25em) {
.navigation__background {
top: 4.5rem;
right: 4.5rem;
}
}
#media only screen and (max-width: 37.5em) {
.navigation__background {
top: 3.5rem;
right: 3.5rem;
}
}
.navigation__nav {
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1500;
opacity: 0;
width: 0;
transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.navigation__list {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
list-style: none;
text-align: center;
width: 100%;
}
.navigation__item {
margin: 1rem;
}
.navigation__link:link,
.navigation__link:visited {
display: inline-block;
font-size: 3rem;
font-weight: 300;
padding: 1rem 2rem;
color: #fff;
text-decoration: none;
text-transform: uppercase;
background-image: linear-gradient(120deg, transparent 0%, transparent 50%, #fff 50%);
background-size: 220%;
transition: all .4s;
}
.navigation__link:link span,
.navigation__link:visited span {
margin-right: 1.5rem;
display: inline-block;
}
.navigation__link:hover,
.navigation__link:active {
background-position: 100%;
color: #0400ff;
transform: translateX(1rem);
}
.navigation__checkbox:checked~.navigation__background {
transform: scale(80);
}
.navigation__checkbox:checked~.navigation__nav {
opacity: 1;
width: 100%;
}
.navigation__icon {
position: relative;
margin-top: 3.5rem;
}
.navigation__icon,
.navigation__icon::before,
.navigation__icon::after {
width: 3rem;
height: 2px;
background-color: #333;
display: inline-block;
}
.navigation__icon::before,
.navigation__icon::after {
content: "";
position: absolute;
left: 0;
transition: all .2s;
}
.navigation__icon::before {
top: -.8rem;
}
.navigation__icon::after {
top: .8rem;
}
.navigation__button:hover .navigation__icon::before {
top: -1rem;
}
.navigation__button:hover .navigation__icon::after {
top: 1rem;
}
.navigation__checkbox:checked+.navigation__button .navigation__icon {
background-color: transparent;
}
.navigation__checkbox:checked+.navigation__button .navigation__icon::before {
top: 0;
transform: rotate(135deg);
}
.navigation__checkbox:checked+.navigation__button .navigation__icon::after {
top: 0;
transform: rotate(-135deg);
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Menu</title>
</head>
<body>
<div class="navigation">
<input type="checkbox" class="navigation__checkbox" id="navi-toggle">
<label for="navi-toggle" class="navigation__button">
<span class="navigation__icon"> </span>
</label>
<div class="navigation__background"> </div>
<nav class="navigation__nav">
<ul class="navigation__list">
<li class="navigation__item"><span>01</span> Our Works</li>
<li class="navigation__item"><span>02</span> Features</li>
<li class="navigation__item"><span>03</span> Our Services</li>
<li class="navigation__item"><span>04</span> Blogs</li>
<li class="navigation__item"><span>05</span> Contact Us</li>
<li class="navigation__item"><span>06</span> Know About Author</li>
</ul>
</nav>
</div>
</body>
</html>
[p.s.- comment out the js code first to see the working CSS.]
Although this is for practices hence any help will be a big help for me to understand js and dom.
I reworked a bit your closeNav function in order to get what you want working.
function closeNav(){
navElement.style.transform = 'scale(0)';
navBackground.style.transform = 'scale(0)';
// setTimeout will be useful for resetting our elements once they
// reached scale(0)
setTimeout(() => {
// You can check or uncheck a checkbox in javascript using .checked,
// here in order to set the styles back to normal.
document.querySelector('.navigation__checkbox').checked = false;
// Wait a bit more that the elements have been reset to remove the
// styles given in JS above.
setTimeout(() => {
navElement.style = '';
navBackground.style = '';
}, 500);
}, 1000);
}
You'll need to rework the timeouts so that they exactly meet your transition duration !
The CSS already handles what happens when the checkbox isn't checked. This works fine for me.
function closeNav(){
document.querySelector('.navigation__checkbox').checked = false;
}
I cant close my overlay modal Box.I try when the x is clicked than remove the class list,than the modal-box should be closed,but it is not recognized by js and css.First i have one ovarlay where should be showed the name and the position of the coas,after that when the user clicks on the arrow the modal box pop up in full width and height and that'sworks correct and after that i have one X span #times where when i click on the X i want the mdoal box to be disapered.I tried with addevent listener and everything itdoesnt work.
JS CODE
const makingTheInstructorSection = (arrInst) => {
for(let coach of arrInst) {
$('.instructorBoxes').append
(`
<div class="infoPerInstructor">
<img src="ImageGalleryPictures/instructor.jpg"/>
<div class="firstOverlay">
<p class="arrowPointerInstructor">→</p>
<div class="text">
<p class="arrowPointerInstructor">→</p>
<h3>${coach.name}</h3>
<p>${coach.position}</p>
</div>
</div>
<div class="secondOverlayOnClick">
<span class="closeTheInstructorBox">×</span>
<h5>Why you coach:</h5>
<p>${coach.WhyYouCoach}</p>
<h5>Favorite Movement:</h5>
<p>${coach.favoriteMovement}</p>
<h5>Favorite Quote:</h5>
<p>${coach.favoriteQuote}</p>
</div>
</div>
`)
}
}
// the code that i am trygin first,i am nto removing here
// because even here i dont get the span "x" in the console
// when i click that x in the browser
for(let item of closing) {
item.addEventListener("click" , (e) => {
console.log(e.target);
})
}
THE CSS CODE
.instructorBoxes {
display: grid;
grid-template-columns: repeat(auto-fill,minmax(300px,1fr));
grid-gap: 8px;
}
.instructorBoxes > div > img {
width: 100%;
height: 100%;
object-fit: cover;
}
.instructorBoxes div {
width: 100%;
height: 100%;
}
.infoPerInstructor {
position: relative;
width: 50%;
}
.image {
display: block;
width: 100%;
height: auto;
}
.firstOverlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
/* background-color: #008CBA; */
background-color: #570194;
overflow: hidden;
width: 100%;
height: 100%;
transform: scale(0);
transition: .3s ease;
cursor: pointer;
z-index:1;
}
.infoPerInstructor:hover .firstOverlay {
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
}
.text {
color: white;
font-size: 20px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.arrowPointerInstructor {
float: right;
font-size: 50px;
color: white;
}
.secondOverlayOnClick {
display: none;
}
.activeteTheSecondOverlayOnClick {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0, 0.9);
overflow-x: hidden;
transition: 0.5s;
display: block;
}
/* .DEactiveteTheSecondOverlayOnClick {
display: none;
} */
.activeteTheSecondOverlayOnClick p , h1 {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #818181;
display: block;
transition: 0.3s;
}
.closeTheInstructorBox {
font-size: 30px;
color: red;
cursor: pointer;
float: right;
right: 0;
z-index: 100;
}
I found an answer.Because it is overlay it does not work,the best way to do is to make add event listener on body,and then check
if some event target contains that specific class list,then it will be found,and throught that event can you search the "modal box" depending on the structure on your html and remove some class list etc...
body.addEventListener("click" , (e) => {
if(e.target.classList.contains("closeTheInstructorBox")) {
console.log("x e ");
console.log(e.target.parentElement)
e.target.parentElement.remove("activeteTheSecondOverlayOnClick")
}