Adding additional data to the local storage in Vue.js - javascript

I have an app To Do List. Using button you add task and new task adds to the list of items with checkbox and delete button in front of each one. I want to save all value from inputs and marked chekboxes on page (store it) once page renewed using browser. To do it I`m using mounted and watch, for now it stores only value from inputs, but not marked chekboxes. Please help to fix it. Below my code:
Vue.createApp({
data(){
return{
placeholder: 'Start typing',
inputvalue: '',
notes: [],
checked: []
}
},
mounted() {
try {
this.notes = JSON.parse(localStorage.getItem('note'))
} catch(e) {
this.notes = []
}
},
watch: {
notes: {
handler: function() {
localStorage.setItem('note', JSON.stringify(this.notes));
},
deep: true
}
},
methods: {
addnewtask(){
if (this.inputvalue !== ''){
this.notes.push(this.inputvalue)
this.inputvalue=''
}
},
removetask(index){
if (confirm('Do you really want to delete?'))
this.notes.splice(index, 1)
}
}
}).mount(app)
body {
font-family: sans-serif;
font-size: 14px;
color: #030303;
background: #3d5f82;
}
h1 {
font-weight: 500;
text-transform: uppercase;
text-align: center;
font-style: solid;
}
.btn {
color: #31d78c;
place-content: center;
place-items: center;
width: fit-content;
border-radius: 99px;
border: 1px solid #31d78c;
text-decoration: none;
text-transform: uppercase;
margin-right: 10px;
margin-top: 10px;
padding: 10px;
font-weight: 700;
background: #fff;
}
.btn:hover {
cursor: pointer;
background-color:rgb(231, 239, 235);
}
.btn.danger {
color: #eb3c15;
place-content: center;
place-items: center;
width: fit-content;
border-radius: 99px;
border: 1px solid #eb3c15;
text-decoration: none;
text-transform: uppercase;
margin-right: 10px;
margin-top: 10px;
padding: 10px;
font-weight: 700;
background: #fff;
}
.btn.danger:hover {
cursor: pointer;
background-color:rgb(236, 219, 219);
}
.container {
margin: 0 auto;
max-width: 1000px;
}
.form-control {
position: relative;
margin-bottom: 10px;
}
.form-control input,
.form-control select {
margin: 0;
outline: none;
border: 2px solid #ccc;
display: block;
width: 95%;
color: #2c3e50;
padding: 0.5rem 1.5rem;
border-radius: 3px;
font-size: 1rem;
}
.card {
overflow: hidden;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 10px;
box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.2);
background: #fff;
}
.card.center {
display: flex;
flex-direction: column;
align-items: center;
}
.list {
margin: 0;
padding: 0;
list-style: none;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0;
transition: .22s all;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
<!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>To Do List</title>
</head>
<link rel="stylesheet" href="style.css">
<style>
[v-cloak] {
display:none;
}
</style>
<body>
<div class="container" id="app" v-cloak>
<div class="card">
<h1>To Do List</h1>
<div class="form-control">
<input
type="text"
v-bind:placeholder="placeholder"
v-model="inputvalue"
v-on:keypress.enter="addnewtask"
/>
<button class="btn" v-on:click="addnewtask">Add Task</button>
</div>
<hr />
<ul class="list" v-if="notes.length !== 0"...>
<li class="list-item" v-for="(note, index) in notes" v-bind:key="note">
<div>
<input type="checkbox" v-model="checked[note]"/>
<span :style="checked[note] ? 'text-decoration: line-through' : ''">
{{index+1}}) {{note}}
</span>
</div>
<button class="btn danger" v-on:click="removetask(index)">Delete</button>
</li>
<hr />
<li>
<strong>Total: {{notes.length}}</strong>
</li>
</ul>
<div v-else>No task exist, please add first one.</div>
</div>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="Vue3.js"></script>
</body>
</html>

I would recommend storing the checkbox value inside your items array instead of separately storing which items are selected. For example you could store this array of objects:
[
{
title: "Title",
selected: true
}
]
Then use the item.selected value inside your checkbox to mark it selected or not selected.
On a second note, instead of manually storing your data in localStorage, I'd recommend using Vuex with localStorage (https://medium.com/js-dojo/how-to-permanently-save-data-with-vuex-localstorage-in-your-vue-app-f1d5c140db69)
You can set it up such that it automatically stores your Vuex state in localStorage and when you refresh the page it will reload it back into your state. Hope this helps!

Related

Why is a button firing the event of a previous element/tag?

I'm not sure what's causing the form button to fire the event of turning the page theme back to white after the dark mode has been clicked and enabled.
Not sure if that may be confusing, but for example if you open the page it's automatically on the light mode theme, when you click "dark" to switch the theme to dark and then click the button "search" while the theme is in "dark", the page will switch back to "light". What am I doing wrong or missing out? Please advise. Also, how could I refractor this JS better and simpler?
Thanks!
HTML - left out the head part intentionally
<html lang="en" color-mode="light">
<body>
<header class="header-container">
<h1 class="title">devfinder</h1>
<div class="light-dark mode">
<span
class="theme-toggle-btn light-hidden light"
aria-label="light theme toggle button">
LIGHT
<img class="light-icon" src="assets/icon-sun.svg" alt="" />
</span>
<span
class="theme-toggle-btn dark-hidden"
aria-label="dark theme toggle button">
DARK
<img src="assets/icon-moon.svg" alt="" />
</span>
</div>
</header>
<main class="content-container">
<section>
<form autocomplete="off" class="form" id="search">
<input
type="text"
id="search"
placeholder="Search GitHub username…" />
<button class="btn">Search</button>
</form>
JS
const themeBtn = document.querySelectorAll(".theme-toggle-btn");
const toggle = function (e) {
if (e.currentTarget.classList.contains("light-hidden")) {
document.documentElement.setAttribute("color-mode", "light");
localStorage.setItem("color-mode", "light");
return;
}
document.documentElement.setAttribute("color-mode", "dark");
localStorage.setItem("color-mode", "dark");
};
themeBtn.forEach((btn) => {
btn.addEventListener("click", toggle);
});
CSS
:root {
--monoFont: 'Space Mono', monospace;
--accent-blue: #0079FF;
--error-red: #F74646;
--light-hover: #60ABFF;
}
:root[color-mode="light"] {
--primary-text-color:#697C9A;
--secondary-text-color: #4B6A9B;
--accent-color: #2B3442;
--background-color: #F6F8FF;
--container-background: #FEFEFE;
--font-color: #222731;
}
:root[color-mode="dark"] {
--primary-text-color: #FFFFFF;
--background-color: #141D2F;
--container-background: #1E2A47;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
:root[color-mode="light"] .light-hidden,
:root[color-mode="dark"] .dark-hidden {
display: none;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: var(--background-color);
height: 100vh;
margin: 0;
color: var(--primary-text-color);
font-family: var(--monoFont);
}
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 2em;
}
.header-container, .content-container {
width: 100%;
max-width: 730px;
}
/* header title */
.title {
color: var(--font-color);
font-size: 1.63rem;
}
/* theme toggle btn */
.theme-toggle-btn {
background-color: var(--background-color);
border: none;
color: var(--primary-text-color);
font-size: .7rem;
font-weight: 700;
letter-spacing: 1.5px;
cursor: pointer;
}
.theme-toggle-btn img {
margin: 0 0 -0.45em 0.75em;
width: 20px;
height: 20px;
}
/* search form */
.form {
position: relative;
display: flex;
align-items: center;
height: 69px;
}
.form input {
background-color: var(--container-background);
border: none;
width: 100%;
padding-left: 1.5em;
margin-bottom: 2em;
color: var(--font-color);
font-size: 1.05rem;
font-family: inherit;
font-weight: 400;
border-radius: 10px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;;
}
.form input::placeholder {
color: var(--secondary-text-color);
}
.btn {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-80%);
width: 100%;
max-width: 106px;
height: 50px;
background-color: var(--accent-blue);
border: none;
color: #FFFFFF;
font-size: 1rem;
border-radius: 10px;
cursor: pointer;
}
.btn:hover {
background-color: var(--light-hover);
}
I think when you're clicking the "Search" button, it's refreshing the page and reloads the html template. Because you have "color-mode="light" within the html element, it will then load the page in light mode rather than dark mode.
I think the problem is: your not consuming the selection from localStorage. To persist the user selection, you should check the preference in localStorage when the page loads.

I want to create complete function of todo list in javascript

when user click on checkbox todo list background become blue text decoration on list paragraph is line-through how i can to do this using javascript
Javascript code
function createlist()
{
document.getElementById("tolist").insertAdjacentHTML ("afterend",`<div id="todolistcontainer">
<input type="checkbox" id="checkbox-list" onclick="complete()">
<p id="tasktodo"></p>
<i class="fa-sharp fa-solid fa-xmark" id="todoicon" onclick="deletelist()"></i>
</div>`);
document.getElementById("tasktodo").innerHTML=document.getElementById("input").value;
}
function deletelist()
{
document.getElementById("todolistcontainer").remove();
}
HTML Code
<!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">
<link rel="stylesheet" href="style.css">
<script src="main.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<title>To Do List JavaScript</title>
</head>
<body>
<div class="container">
<input type="text" id="input" >
<button id="button" onclick="createlist()">Add</button>
</div>
<div id="tolist"></div>
</body>
</html>
CSS code
#import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght#500&display=swap');
body{
margin-left: 30%;
margin-right: 30%;
font-family:fira Code;
}
.container{
display: flex;
flex-direction: row;
justify-content: center;
border: 5px solid black;
width:100%;
margin-bottom: 10px;
}
.container #input{
border: 0px solid black;
width:100%;
margin: auto;
font-size:40px;
}
.container #input:focus{
outline:none;
}
.container #button{
border: 0px solid black;
width:30%;
margin: auto;
background-color: blue;
color: white;
height:100%;
font-size:40px;
}
#todolistcontainer{
border:1px solid black;
width:80%;
height: 100px;
margin-top:12px;
margin:auto;
display: flex;
flex-direction: row;
margin-top: 12%;
}
#tasktodo{
font-size:150%;
width:30%;
align-self: center;
margin: auto;
}
#checkbox-list{
width:8%;
}
#checkbox-list:checked{
accent-color: blue;
}
#todoicon{
display: flex;
flex-direction: row;
align-self: center;
width:12%;
font-size:50px;
}
MY todo-complete javascript function
function complete()
{
if(document.getElementById("checkbox-list.parentNode").checked)
{
document.getElementById("tasktodo").style.textDecoration="line-through";
document.getElementById("tasktodo").style.color="grey";
document.getElementById("todolistcontainer").style.backgroundColor="blue";
}
else{
document.getElementById("tasktodo").style.textDecoration="none";
document.getElementById("tasktodo").style.color="black";
document.getElementById("todolistcontainer").style.backgroundColor="white";
}
}
But output is not satisfied
I am learning html dom and got stuck at this project
A few things to learn here:
You can't use ID's more than once. Everytime you were adding a todolist item, it was using the same ID and your code would only ever affect the first instance of those IDs. Instead, use classes.
Rather than use IDs and a whole bunch of 'getElementById', use relative paths. For example, you can send your complete() function a reference to the checkbox like this onclick="complete(this)". Then in your function, you can find the container using closest() like this: elem.closest('.todolistcontainer').
Finally, don't make your life hard by manually changing styles - instead use classes. A single class for your checked state would do the trick. Then you can just turn on and off the class based on the checkbox
function complete(elem) {
let container = elem.closest('.todolistcontainer');
if (elem.checked) container.classList.add('ischecked');
else container.classList.remove('ischecked');
}
function createlist() {
let todo = document.getElementById("input").value;
document.getElementById("tolist").insertAdjacentHTML("afterend", `<div class="todolistcontainer">
<input type="checkbox" onclick="complete(this)">
<p class="tasktodo">${todo}</p>
<i class="fa-sharp fa-solid fa-xmark" class="todoicon" onclick="deletelist(this)"></i>
</div>`);
}
function deletelist(elem) {
elem.closest('.todolistcontainer').remove();
}
#import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght#500&display=swap');
body {
margin-left: 30%;
margin-right: 30%;
font-family: fira Code;
}
.container {
display: flex;
flex-direction: row;
justify-content: center;
border: 5px solid black;
width: 100%;
margin-bottom: 10px;
}
.container #input {
border: 0px solid black;
width: 100%;
margin: auto;
font-size: 40px;
}
.container #input:focus {
outline: none;
}
.container #button {
border: 0px solid black;
width: 30%;
margin: auto;
background-color: blue;
color: white;
height: 100%;
font-size: 40px;
}
.todolistcontainer {
border: 1px solid black;
width: 80%;
height: 100px;
margin-top: 12px;
margin: auto;
display: flex;
flex-direction: row;
margin-top: 12%;
}
.tasktodo {
font-size: 150%;
width: 30%;
align-self: center;
margin: auto;
}
.checkbox-list {
width: 8%;
}
.checkbox-list:checked {
accent-color: blue;
}
.todoicon {
display: flex;
flex-direction: row;
align-self: center;
width: 12%;
font-size: 50px;
}
/* NEW CSS */
.todolistcontainer.ischecked .tasktodo {
text-decoration: line-through;
background: blue;
color: #fff;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div class="container">
<input type="text" id="input">
<button id="button" onclick="createlist()">Add</button>
</div>
<div id="tolist"></div>
Kinglish beat me to the punch - but I have one additional thing to add:
Instead of inline JS use event delegation on your list container to catch events from its child elements as they "bubble up" the DOM. You can then check to see if the element that fired the event was a button, check its checked status, and then use nextElementSibling to update the paragraph style.
const input = document.querySelector('input');
const list = document.querySelector('.tolist');
const add = document.querySelector('.add');
add.addEventListener('click', createList);
list.addEventListener('click', handleList);
function createList(e) {
const html = `
<div class="todoContainer">
<input type="checkbox" class="checkbox completed">
<p class="tasktodo">${input.value}</p>
</div>
`;
list.insertAdjacentHTML('beforeend', html);
}
function handleList(e) {
if (e.target.matches('.checkbox')) {
const { checked, nextElementSibling: nes } = e.target;
if (checked) {
nes.classList.add('strike');
nes.closest('.todoContainer').classList.add('blue');
} else {
nes.classList.remove('strike');
nes.closest('.todoContainer').classList.remove('blue');
}
}
}
#import url(https://fonts.googleapis.com/css2?family=Fira+Code:wght#500&display=swap);.container,.container input{width:100%}.container .add,.container input{border:0 solid #000;margin:auto;font-size:30px}body{margin-left:30%;margin-right:30%;font-family:fira Code}.container{display:flex;flex-direction:row;justify-content:center;border:5px solid #000;margin-bottom:10px}.container .input:focus{outline:0}.container .add{width:30%;background-color:#00f;color:#fff;height:100%}.todoContainer{border:1px solid #000;width:80%;height:100px;margin:12% auto auto;display:flex;flex-direction:row}.tasktodo{font-size:150%;width:30%;align-self:center;margin:auto}.checkbox{width:8%}.checkbox:checked{accent-color:blue}
.checkbox:hover { cursor: pointer; }
.strike { text-decoration: line-through; color: grey; }
.blue { background-color: lightblue; }
<div class="container">
<input type="text">
<button class="add">Add</button>
</div>
<div class="tolist"></div>

Using submit button and enter key to add to-do-list

I am doing a simple to-do list to keep learning JS and so far I have two issues that I can't solve, I would like to know if you guys could help me.
First, I would like to use the button and the enter key to add a task instead of just pressing enter, I know I could use a form and the preventDefault(); method but I would like to explore other options, I can make it work by changing the event listener to 'click' since the button is a submit type but then the text won't add when pressing enter.
Second, the "There are no pending tasks." message div shows up at the beginning when no tasks are added and disappear when a task is added, so far its ok but when I delete all the tasks the message won't show up again, maybe my logic is failing somehow but can't figure it out where.
let board = document.querySelector('.to-do-board');
let taskContainer = document.querySelector('.container-task');
let taskbar = document.querySelector('.add-task');
let button = document.querySelector('.add-btn');
let noTasksMsg = document.querySelector('.message');
taskbar.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && taskbar.value !== '') {
if (taskContainer === null) {
noTasksMsg.style.display = 'flex';
} else {
noTasksMsg.style.display = 'none';
}
let div = document.createElement('div');
div.className = 'item-task';
div.innerHTML = taskbar.value;
taskContainer.insertAdjacentElement('beforeend', div)
let deleteButton = document.createElement('button')
deleteButton.className = 'deleteBtn';
deleteButton.textContent = "X";
div.appendChild(deleteButton);
taskbar.value = '';
deleteButton.addEventListener('click', () => {
div.remove();
})
}
})
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
letter-spacing: 2px;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
margin: 15px;
}
.to-do-board {
width: 500px;
background: #88CCF1;
border-radius: 10px;
}
.title {
background: #3587A4;
border-top-right-radius: 10px;
border-top-left-radius: 10px;
}
h1 {
text-align: center;
color: white;
padding: .5em 0 .5em 0;
font-size: 22px;
}
.inputs {
display: flex;
justify-content: center;
margin: 1em;
}
.add-task {
padding: .5em;
width: 80%;
margin-right: 1em;
}
.add-btn {
padding: 0 1em 0 1em;
font-size: large;
font-weight: bolder;
color: white;
border: none;
border-radius: 10px;
background: #2D848A;
cursor: pointer;
}
.add-btn:hover {
background: #3587A4;
}
.container-task {
display: flex;
flex-direction: column;
align-items: center;
padding: 1em;
}
.item-task {
display: flex;
justify-content: space-between;
background: #C1DFF0;
width: 90%;
border-radius: 10px;
margin-bottom: 10px;
}
.deleteBtn {
border: none;
background: #d45151;
width: 3em;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
height: 2.5em;
color: white;
font-weight: bold;
cursor: pointer;
}
.deleteBtn:hover {
background: rgb(235, 95, 95);
}
.message {
text-align: center;
padding: 1em;
color: white;
background: #63afb1;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" lang="en">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="main.js" defer></script>
<title>To-do List!</title>
</head>
<body>
<div class="container">
<div class="to-do-board">
<div class="title"><h1>To-Do List!</h1></div>
<div class="inputs">
<input type="text" placeholder="Add a task" class="add-task">
<input type="submit" value="+" class="add-btn">
</div>
<div class="container-task">
<div class="message">There are no pending tasks.</div>
</div>
</div>
</div>
</body>
</html>
Thank you very much.
You can extract the code inside your eventListener to a separate function, pass it the event object and slightly modify it to check if e.type is click in addition to checking for the enter key but I'd still recommend against it and recommend using <form>.
let board = document.querySelector('.to-do-board');
let taskContainer = document.querySelector('.container-task');
let taskbar = document.querySelector('.add-task');
let button = document.querySelector('.add-btn');
taskbar.addEventListener('keypress', handleEvent)
button.addEventListener('click', handleEvent)
function handleEvent(e){
if ((e.type === 'click' || e.key === 'Enter') && taskbar.value !== '') {
if(taskContainer.querySelector(".message") != null){
taskContainer.querySelector(".message").remove()
}
let div = document.createElement('div');
div.className = 'item-task';
div.innerHTML = taskbar.value;
taskContainer.appendChild(div)
let deleteButton = document.createElement('button')
deleteButton.className = 'deleteBtn';
deleteButton.textContent = "X";
div.appendChild(deleteButton);
taskbar.value = '';
deleteButton.addEventListener('click', () => {
div.remove();
if(taskContainer.children.length == 0){
const message = document.createElement("div")
message.classList.add("message")
message.textContent = "There are no pending tasks."
taskContainer.appendChild(message);
}
})
}
}
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
letter-spacing: 2px;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
margin: 15px;
}
.to-do-board {
width: 500px;
background: #88CCF1;
border-radius: 10px;
}
.title {
background: #3587A4;
border-top-right-radius: 10px;
border-top-left-radius: 10px;
}
h1 {
text-align: center;
color: white;
padding: .5em 0 .5em 0;
font-size: 22px;
}
.inputs {
display: flex;
justify-content: center;
margin: 1em;
}
.add-task {
padding: .5em;
width: 80%;
margin-right: 1em;
}
.add-btn {
padding: 0 1em 0 1em;
font-size: large;
font-weight: bolder;
color: white;
border: none;
border-radius: 10px;
background: #2D848A;
cursor: pointer;
}
.add-btn:hover {
background: #3587A4;
}
.container-task {
display: flex;
flex-direction: column;
align-items: center;
padding: 1em;
}
.item-task {
display: flex;
justify-content: space-between;
background: #C1DFF0;
width: 90%;
border-radius: 10px;
margin-bottom: 10px;
}
.deleteBtn {
border: none;
background: #d45151;
width: 3em;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
height: 2.5em;
color: white;
font-weight: bold;
cursor: pointer;
}
.deleteBtn:hover {
background: rgb(235, 95, 95);
}
.message {
text-align: center;
padding: 1em;
color: white;
background: #63afb1;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" lang="en">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<script src="main.js" defer></script>
<title>To-do List!</title>
</head>
<body>
<div class="container">
<div class="to-do-board">
<div class="title"><h1>To-Do List!</h1></div>
<div class="inputs">
<input type="text" placeholder="Add a task" class="add-task">
<input type="submit" value="+" class="add-btn">
</div>
<div class="container-task">
<div class="message">
You have not added any tasks.
</div>
</div>
</div>
</div>
</body>
</html>

Unhandled error during execution of mounted hook in Vue.js

I have an app To Do List. Using button you add task and new task adds to the list of items with checkbox and delete button in front of each one. I want to save all value and marked info on page (store it) once page renewed using browser. When I`m using mounted and watch.There are 2 issues: 1)When I use CodePen or GitHub to view the site and app it shows nothing and in console.log:"Unhandled error during execution of mounted hook"; 2)In Visual Studio Code it shows everything on preview, but stores only value of input-not marked chekboxes. Please help to solve it. Below my code:
Vue.createApp({
data(){
return{
placeholder: 'Start typing',
inputvalue: '',
notes: [],
checked: []
}
},
mounted() {
this.notes = JSON.parse(localStorage.getItem('note')) || [];
},
watch: {
notes: {
handler: function() {
localStorage.setItem('note', JSON.stringify(this.notes));
},
deep: true
}
},
methods: {
addnewtask(){
if (this.inputvalue !== ''){
this.notes.push(this.inputvalue)
this.inputvalue=''
}
},
removetask(index){
if (confirm('Do you really want to delete?'))
this.notes.splice(index, 1)
}
}
}).mount(app)
body {
font-family: sans-serif;
font-size: 14px;
color: #030303;
background: #3d5f82;
}
h1 {
font-weight: 500;
text-transform: uppercase;
text-align: center;
font-style: solid;
}
.btn {
color: #31d78c;
place-content: center;
place-items: center;
width: fit-content;
border-radius: 99px;
border: 1px solid #31d78c;
text-decoration: none;
text-transform: uppercase;
margin-right: 10px;
margin-top: 10px;
padding: 10px;
font-weight: 700;
background: #fff;
}
.btn:hover {
cursor: pointer;
background-color:rgb(231, 239, 235);
}
.btn.danger {
color: #eb3c15;
place-content: center;
place-items: center;
width: fit-content;
border-radius: 99px;
border: 1px solid #eb3c15;
text-decoration: none;
text-transform: uppercase;
margin-right: 10px;
margin-top: 10px;
padding: 10px;
font-weight: 700;
background: #fff;
}
.btn.danger:hover {
cursor: pointer;
background-color:rgb(236, 219, 219);
}
.container {
margin: 0 auto;
max-width: 1000px;
}
.form-control {
position: relative;
margin-bottom: 10px;
}
.form-control input,
.form-control select {
margin: 0;
outline: none;
border: 2px solid #ccc;
display: block;
width: 95%;
color: #2c3e50;
padding: 0.5rem 1.5rem;
border-radius: 3px;
font-size: 1rem;
}
.card {
overflow: hidden;
padding: 1rem;
margin-bottom: 1rem;
border-radius: 10px;
box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.2);
background: #fff;
}
.card.center {
display: flex;
flex-direction: column;
align-items: center;
}
.list {
margin: 0;
padding: 0;
list-style: none;
}
.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0;
transition: .22s all;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
<!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>To Do List</title>
</head>
<link rel="stylesheet" href="style.css">
<style>
[v-cloak] {
display:none;
}
</style>
<body>
<div class="container" id="app" v-cloak>
<div class="card">
<h1>To Do List</h1>
<div class="form-control">
<input
type="text"
v-bind:placeholder="placeholder"
v-model="inputvalue"
v-on:keypress.enter="addnewtask"
/>
<button class="btn" v-on:click="addnewtask">Add Task</button>
</div>
<hr />
<ul class="list" v-if="notes.length !== 0"...>
<li class="list-item" v-for="(note, index) in notes" v-bind:key="note">
<div>
<input type="checkbox" v-model="checked[note]"/>
<span :style="checked[note] ? 'text-decoration: line-through' : ''">
{{index+1}}) {{note}}
</span>
</div>
<button class="btn danger" v-on:click="removetask(index)">Delete</button>
</li>
<hr />
<li>
<strong>Total: {{notes.length}}</strong>
</li>
</ul>
<div v-else>No task exist, please add first one.</div>
</div>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="Vue3.js"></script>
</body>
</html>
Try adding JSON.parse in a try-catch block
mounted() {
try {
this.notes = JSON.parse(localStorage.getItem('note'))
} catch(e) {
this.notes = []
}
}

How can i remove the user input added paragraph automatically after 2 seconds in javascript?

i just learned how to create a to-do list in java script and as a personal project i wanted to use the information i learned in to-do app making by creating a tell your secret website which like the
const mytext = document.getElementById('mytext');
const btn = document.getElementById('btn');
const items = document.getElementById('items');
btn.addEventListener('click', function(e){
e.preventDefault();
const paragraph = document.createElement('p');
paragraph.classList.add("item");
paragraph.innerText = mytext.value;
items.appendChild(paragraph);
mytext.value = '';
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: rgb(231, 237, 241);
}
main {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10%;
font-family: "Source Sans Pro", sans-serif;
}
h2 {
color: rgb(71, 80, 102);
font-size: 40px;
margin-bottom: 30px;
}
.myform {
display: flex;
justify-content: center;
align-items: center;
}
#btn {
margin-left: 10px;
width: 40px;
height: 100px;
white-space: pre-line;
text-align: center;
font-size: 15px;
font-weight: 600;
border: none;
border-radius: 10px;
cursor: pointer;
box-shadow: 2px 2px rgb(184, 182, 182);
color: rgb(35, 70, 136);
}
#btn:active {
color: rgb(48, 95, 182);
box-shadow: 0 0 2px grey;
}
#mytext {
background-color: aliceblue;
border-radius: 10px;
border: none;
padding: 7px;
box-shadow: 1px 1px rgb(200, 207, 212);
outline: none;
}
.items {
border-radius: 5px;
font-family: cursive;
color: rgb(61, 61, 60);
width: 400px;
display: flex;
justify-content: center;
margin-top: 10px;
padding: 5px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght#200;300;400;600;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./styles.css">
<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>Document</title>
</head>
<body>
<main>
<h2>Write Your Secret</h2>
<div class="container">
<form class="myform" action="">
<textarea name="text" id="mytext" cols="30" rows="10" placeholder="Write Whatever You Wish"></textarea>
<button id="btn">S
h
a
r
e</button>
</form>
<div class="items" id="items"></div>
</div>
</main>
<script src="./app.js"></script>
</body>
</html>
to-do app user writes something in the box (his/her secret) and the secret is displayed on the screen but
this is what i need:
i need the displayed paragraph to be removed automatically after 2 second like the secret vanishes 2 second after you write it.even better if it vanishes slowly like the ink vanishes in harry potter movie in tom riddle diary but that's not important i just want to remove the secret after 2 seconds first and then worry about the style that it vanishes.
With the simple addition of this code:
setTimeout(() => paragraph.classList.add("hidden"), 2000)
Which adds the class "hidden" after 2 seconds it will do what you want. You could make class hidden do anything, such as just set the visibility to hidden but you can also do transition effects like the one you deswcribe:
.hidden {
visibility: hidden;
opacity: 0;
transition: visibility 0s 2s, opacity 2s linear;
}
If using a transition like above you can also add this line to remove the element when the transition completes
paragraph.addEventListener('transitionend',() => paragraph.remove())
Live example below
const mytext = document.getElementById('mytext');
const btn = document.getElementById('btn');
const items = document.getElementById('items');
btn.addEventListener('click', function(e){
e.preventDefault();
const paragraph = document.createElement('p');
paragraph.classList.add("item");
paragraph.innerText = mytext.value;
items.appendChild(paragraph);
mytext.value = '';
paragraph.addEventListener('transitionend',() => paragraph.remove())
setTimeout(() => paragraph.classList.add("hidden"), 2000)
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: rgb(231, 237, 241);
}
main {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10%;
font-family: "Source Sans Pro", sans-serif;
}
h2 {
color: rgb(71, 80, 102);
font-size: 40px;
margin-bottom: 30px;
}
.myform {
display: flex;
justify-content: center;
align-items: center;
}
#btn {
margin-left: 10px;
width: 40px;
height: 100px;
white-space: pre-line;
text-align: center;
font-size: 15px;
font-weight: 600;
border: none;
border-radius: 10px;
cursor: pointer;
box-shadow: 2px 2px rgb(184, 182, 182);
color: rgb(35, 70, 136);
}
#btn:active {
color: rgb(48, 95, 182);
box-shadow: 0 0 2px grey;
}
#mytext {
background-color: aliceblue;
border-radius: 10px;
border: none;
padding: 7px;
box-shadow: 1px 1px rgb(200, 207, 212);
outline: none;
}
.items {
border-radius: 5px;
font-family: cursive;
color: rgb(61, 61, 60);
width: 400px;
display: flex;
justify-content: center;
margin-top: 10px;
padding: 5px;
}
.hidden {
visibility: hidden;
opacity: 0;
transition: visibility 0s 2s, opacity 2s linear;
}
<main>
<h2>Write Your Secret</h2>
<div class="container">
<form class="myform" action="">
<textarea name="text" id="mytext" cols="30" rows="10" placeholder="Write Whatever You Wish"></textarea>
<button id="btn">S
h
a
r
e</button>
</form>
<div class="items" id="items"></div>
</div>
</main>
const btn = document.getElementById('btn');
const items = document.getElementById('items');
const clearInput = () => {
setInterval(function(){mytext.value = ''; }, 2000);
}
btn.addEventListener('click', function(e){
e.preventDefault();
const paragraph = document.createElement('p');
paragraph.classList.add("item");
paragraph.innerText = mytext.value;
items.appendChild(paragraph);
mytext.value = '';
});
clearInput()

Categories

Resources