i hope you guys fine, well..
I'm doing a To Do List, and there is a problem in my code, which I've been trying to solve for a few days, and no effective results was made..
If you guys test in the snippet with me, i am sure, that will be more
clear to understand.
When i click in some list element, my javascript should change or add the className, and add a class call 'selected'.
because, when i will click in the remove button, they will delete all elements with 'selected' classList in the list. (as you can see in the code)
But the className a not being add to the tag in the first click, just works if i click in the element one more time.
i simplified my code, just to show the real problem:
Link to jsfiddle : https://jsfiddle.net/myqrzcs2/
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
const completedLine = document.querySelector('ol');
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
i.addEventListener('click', function semNomeDois() {
listas.forEach((j) => j.classList.remove('selected'));
this.classList.add('selected');
});
});
}
}
completedLine.addEventListener('click', umClick);
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.lista:focus {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
But how can i make the class be add, just in the first click, not in the second?
I think you got off on the wrong foot in programming this.
Here is the way I use, may it inspire you.
const
textoTarefa = document.getElementById('texto-tarefa')
, criarTarefa = document.getElementById('criar-tarefa')
, removerSelec = document.getElementById('remover-selecionado')
, listaTarefas = document.getElementById('lista-tarefas')
;
var li_selected = null
;
textoTarefa.oninput = () =>
{
criarTarefa.disabled = (textoTarefa.value.trim().length ===0 )
}
criarTarefa.onclick = () =>
{
listaTarefas.appendChild( document.createElement('li')).textContent = textoTarefa.value.trim()
textoTarefa.value = ''
textoTarefa.focus()
criarTarefa.disabled = true
}
listaTarefas.onclick = ({target}) =>
{
if (!target.matches('li')) return
if (!!li_selected && li_selected !== target ) li_selected.classList.remove('listaSelect')
li_selected = target.classList.toggle('listaSelect') ? target : null
removerSelec.disabled = !li_selected
}
removerSelec.onclick = () =>
{
listaTarefas.removeChild(li_selected)
li_selected = null
removerSelec.disabled = true
}
.listaSelect {
background: #ff0000c4;
}
ol#lista-tarefas {
cursor : pointer
}
<input id='texto-tarefa' type="text" value="">
<button id='criar-tarefa' disabled>Add</button>
<button id='remover-selecionado' disabled>Remove Selected</button>
<ol id='lista-tarefas'></ol>
You were unnecessarily adding an event listener to each item in the list.
You can check the updated fiddle here: https://jsfiddle.net/msa9v2nf/
Since you're already checking which target element is clicked, there isn't any need to add an individual listener to each child item in the list.
I updated the umClick function:
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
listas.forEach((j) => j.classList.remove('selected'));
event.target.classList.add('selected');
});
}
}
The problem is you call the function umClick and call the function to add .selected within a click event in the same function umClick.
What happens is the click event completedLine.addEventListener('click', umClick); happens before the i.addEventListener('click', function semNomeDois() event. This is why you need a first click on the ol tag for only the first time.
To fixes this you have multiple options:
instead of calling click event on ol tag you can call mousedown which happens before click event.
Calling a click event on the li elements on creation, which needs a new function.
Depending on Vektor's answer, you can remove the unnecessary click event inside the first click event.
Also, I've made the red highlight on the .selected class instead of :focus, just to make it clear when the item is selected.
.selected {
background: red;
}
First Solution
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
const completedLine = document.querySelector('ol');
function umClick(event) {
if (event.target.tagName === 'LI') {
const listas = document.querySelectorAll('.lista');
listas.forEach((i) => {
i.addEventListener('click', function semNomeDois() {
listas.forEach((j) =>{
if(j != event.target)
j.classList.remove('selected');
});
this.classList.add('selected');
});
});
}
}
completedLine.addEventListener('mousedown', umClick);
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.selected {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
Second Solution
const textoTarefa = document.getElementById('texto-tarefa');
const criarTarefa = document.getElementById('criar-tarefa');
const listaTarefas = document.getElementById('lista-tarefas');
criarTarefa.onclick = function click() {
const lista = document.createElement('li');
lista.className = 'lista';
lista.id = 'lista';
lista.tabIndex = '0';
lista.innerHTML = textoTarefa.value;
listaTarefas.appendChild(lista);
lista.addEventListener('click',function(){
itemClick(this);
});
document.body.appendChild(listaTarefas);
textoTarefa.value = '';
};
function itemClick(item) {
const listas = document.querySelectorAll('.lista');
listas.forEach((j) =>j.classList.remove('selected'));
item.classList.add('selected');
}
function removeSelected() {
// teste
const listaSelected = document.querySelectorAll('.selected');
for (let i = 0; i < listaSelected.length; i += 1) {
listaSelected[i].remove();
}
}
.selected {
background: red;
}
<!DOCTYPE html>
<html>
<head>
<link rel='stylesheet' href='style.css'>
</head>
<body>
<header>
<h1>My List</h1>
</header>
<input id='texto-tarefa' type="text" />
<button id='criar-tarefa' type="submit" onClick='click()'>Add</button>
<ol id='lista-tarefas'>
</ol>
<button id='remover-selecionado' type="submit" onClick='removeSelected()'>Remove Selected (Only One)</button>
<script src="script.js"></script>
</body>
</html>
I am not fully understand your problem but,
If you want to add the style when selecting a item, just add the style to
.selected
If you want in focus, and remove the class when there is no focus, you may add an eventlistener to control that.
Related
I am trying to create a to-do list in HTML, CSS and pure JS.
const dSubmit = document.getElementById('submit');
const storeData = [];
let typer = document.getElementById('type');
let input = document.getElementById('text');
const list = document.getElementById('listHolder');
dSubmit.addEventListener("click", (e) => {
e.preventDefault();
if (input.value == "") {
typer.innerHTML = "Please enter a task";
} else {
typer.innerHTML = "";
store();
}
});
function store() {
const tData = document.getElementById('text').value;
storeData.push(tData);
updater();
input.value = "";
}
function deleter (index) {
storeData.splice(index, 1);
updater();
}
function updater() {
let htmlCode = "";
storeData.forEach(function(item, index){
htmlCode += "<div class='test'><div id = "+ index +">" + item + "</div><div class='sideBtn'><button type='button' class='edit' onClick= 'editF("+ index +")'>Edit</button><button class='delBtn' onClick= 'deleter("+ index +")'>Delete</button> </div> </div>"
})
list.innerHTML = htmlCode;
}
function editF (index) {
let tempOne = document.getElementById(index);
let tempTwo = "<input id='inputText"+String(index)+"' type='text' name='task' value ='" + String(storeData[index]) + "'><button id='saveText"+String(index)+"' onClick= 'save("+index+")' >Save</button>"
tempOne.innerHTML = tempTwo;
}
function save (index) {
console.log('test1')
let tempOne= document.getElementById('saveText'+String(index));
let tempTwo = document.getElementById('inputText'+String(index));
console.log('test2')
tempOne.addEventListener("click", function foo (){
console.log('test3')
storeData.splice(index,1,tempTwo.value)
updater()
}
)
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8">
<title>To Do List</title>
</head>
<body>
<h1>To-do-list</h1>
<form>
<label for="task">Please enter item:</label>
<input type="text" name="task" id="text">
<button id="submit">Submit</button>
</form>
<div id='type'></div>
<div>List:</div>
<div id="listHolder" class="test"></div>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
I am facing problems with the save function. If I edit an item in the to-do list and click the save button, the function executes up to the point of console.log('test2'). If I click save again the function executes in its entirety.
I would like to ask why the first click results in execution of the save function up to 'test2'?
Additionally would anyone be kind enough to critique my JS? are there things in dire need of improvement? or is there a more practical/efficient method of writing my JS code?
Thank you for your help in advance.
After the 'test2' log, you are adding an event listener, and the rest of the code is inside of the listener block. The code in the listener block is only executed once that listener receives a 'click' event, which is why it works the second time.
What have I done wrong? Here is what the result tells me:
Uncaught ReferenceError: addToDoButton is not defined
Here is my JavaScript:
let addToDo = document.getElementById("edit_button");
let toDoContainer = document.getElementById("to_do_container");
let inputField = document.getElementById("inputField");
addToDoButton.addEventListener("click", function(){
var paragraph = document.createElement("p");
paragraph.classList.add("paragraph-styling");
paragraph.innerText = inputField.value;
toDoContainer.appendChild(paragraph);
inputField.value = " ";
paragraph.addEventListener("click", function(){
paragraph.style.textDecoration = "line-through";
})
paragraph.addEventListener("dblclick", function(){
paragraph.removeChild(paragraph)
})
})
Here is my HTML:
<html>
<head>
<link rel="stylesheet", href="style.css">
<h1 class="tdl_header">TO DO LIST</h1>
</head>
<body class="body">
<div class="container">
<input id="inputField" type = "text"
placeholder="add your list">
<button id="addToDo"> + </button>
<div class="to-dos" id="toDoContainer" >
</div>
</div>
<script src="to_do_list.js"></script>
</body>
</html>
let addToDo = document.getElementById("edit_button");
let toDoContainer = document.getElementById("to_do_container");
let inputField = document.getElementById("inputField");
addToDoButton.addEventListener("click", function() {
var paragraph = document.createElement("p");
paragraph.classList.add("paragraph-styling");
paragraph.innerText = inputField.value;
toDoContainer.appendChild(paragraph);
inputField.value = " ";
paragraph.addEventListener("click", function() {
paragraph.style.textDecoration = "line-through";
})
paragraph.addEventListener("dblclick", function() {
paragraph.removeChild(paragraph)
})
})
<html>
<head>
<link rel="stylesheet" , href="style.css">
<h1 class="tdl_header">TO DO LIST</h1>
</head>
<body class="body">
<div class="container">
<input id="inputField" type="text" placeholder="add your list">
<button id="addToDo"> + </button>
<div class="to-dos" id="toDoContainer">
</div>
</div>
<script src="to_do_list.js"></script>
</body>
</html>
You called your variable addToDo but want to add the listener to addToDoButton which doesn't exist.
EDIT:
As #Beriu mentioned, the id of the button also doesn't match the string in the selector.
You have two issues there:
you want to select a button wiht getElementById('edit_button') which doesn't exists
you want to addEventListener to a DOM element which can't be selcted
Solution:
let addToDo = document.getElementById("addToDo");
let toDoContainer = document.getElementById("to_do_container");
let inputField = document.getElementById("inputField");
addToDo.addEventListener("click", function() {
// your code
})
I would suggest you attach your event handler for the click and double click on the "tasks" to the container, then it is easier to use the event.target for the action to take place.
I did some rework of class and id to make them more consistent and match the event handler attachment here also.
I also do not append "blank" tasks.
function handleToDoPlus(event) {
let inputField = document.getElementById("input-field");
let newValue = inputField.value;
let hasValue = newValue.length > 0
event.target.nextElementSibling.innerText = hasValue ? "" : "No Value Entered";
if (hasValue) {
let paragraph = document.createElement("p");
paragraph.classList.add("paragraph-styling");
paragraph.innerText = newValue;
let toDoContainer = document.getElementById("to-do-container");
toDoContainer.appendChild(paragraph);
inputField.value = "";
}
}
function handleDoubleClick(event) {
console.log('double click!');
if (event.target.className === 'paragraph-styling') {
event.currentTarget.removeChild(event.target)
}
}
function handleClickToDone(event) {
console.log('Click!');
if (event.target.className === 'paragraph-styling') {
event.target.style.textDecoration = "line-through";
}
}
let addToDo = document.getElementById("add-to-do");
addToDo.addEventListener("click", handleToDoPlus);
let toDoContainer = document.getElementById("to-do-container");
toDoContainer.addEventListener("dblclick", handleDoubleClick);
toDoContainer.addEventListener("click", handleClickToDone);
//let inputField = document.getElementById("input-field");
.error-message {
color: #FF0000;
}
<div class="container">
<input id="input-field" type="text" placeholder="add your list">
<button id="add-to-do"> + </button> <span class="error-message"></span>
<div id="to-do-container" class="to-dos">
</div>
</div>
Super new to all of this so this might be some beginner troubleshooting. The list seems to be working where I'm adding a list element to the UL with a checkbox and delete button. When checkbox is checked it puts a line through the text and when the delete button is clicked it deletes the list element. The assignment asks to save to localStorage so that when refreshed, the list items still remain, and I'm getting super confused by this. What I have now seems to be saving my list elements to an array but I don't understand how to get them to save and stay on the page.
const form = document.querySelector('form');
const input = document.querySelector('#todoInput');
const newElement = document.querySelector('ul');
const savedToDos = JSON.parse(localStorage.getItem('todos')) || [];
newElement.addEventListener('click', function(e) {
if(e.target.tagName === 'BUTTON') {
e.target.parentElement.remove()
}
})
function addToList(text) {
const li = document.createElement('li');
const checkbox = document.createElement('input');
const button = document.createElement('button');
button.innerText = "Delete";
checkbox.type = 'checkbox';
checkbox.addEventListener('change', function() {
li.style.textDecoration = checkbox.checked ? 'line-through' : 'none';
})
li.innerText = text;
li.insertBefore(checkbox, li.firstChild);
li.appendChild(button);
return li;
};
form.addEventListener('submit', function(e) {
e.preventDefault();
const newListItem = addToList(input.value);
input.value = '';
newElement.append(newListItem);
savedToDos.push(newListItem.innerText);
localStorage.setItem('todos', JSON.stringify(savedToDos));
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToDo App</title>
<link rel="stylesheet" href="app.css">
</head>
<body>
<div>
<h1>Todo List</h1>
<form action="">
<input type="text" id="todoInput" placeholder="Add To Todo List">
<button class="add-button">Add</button>
</form>
<ul id="todoList">
</ul>
</div>
<script src="app.js"></script>
</body>
</html>
It looks like you're failing to populate the DOM when the page loads.
After you retrieve the items from local storage (which you're already doing), loop through the list and add each of them to the DOM:
// After this line, which you've already written:
const savedToDos = JSON.parse(localStorage.getItem('todos')) || [];
// Loop through savedToDos, and for each one, insert a new list:
savedToDos.forEach(function(value) {
const newListItem = addToList(value);
newElement.append(newListItem);
});
Every browser has local storage where we can store data and cookies. just go to the developer tools by pressing F12, then go to the Application tab. In the Storage section expand Local Storage.
this piece of code might help you
// Store Task
function storeTaskInLocalStorage(task) {
let tasks;
if(localStorage.getItem('tasks') === null){
tasks = [];
} else {
tasks = JSON.parse(localStorage.getItem('tasks'));
}
tasks.push(task);
localStorage.setItem('tasks', JSON.stringify(tasks));
}
I am creating a simple to do list using jQuery and local storage. I am also trying to add a button for each li I add to clear the item from the list. My list does not stick upon refresh and I can't figure out how to load the button, does the button need to happen on the HTML side?
The adding to the list functions work great its just the storage to local storage that I seem to be missing something.
I created a jsfiddle for this code and the local storage seems to work fine but it will not work on my xampp. Also I can get the done button to appear but it won't removeItem.
https://jsfiddle.net/blen6035/287pc153/7/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task List</title>
<link rel="stylesheet" href="main.css">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="tasks.js"></script>
</head>
<body>
<aside>
<h2>Add a task</h2>
<label for="task">Task:</label>
<input type="text" id="task" name="task"><br>
<label> </label>
<input type="button" id="add" name="add" value="Add Task">
</aside>
<main>
<h1>Task list</h1>
<ul id="listOfTasks"></ul>
</main>
<footer></footer>
</body>
</html>
"use strict"
$(document).ready(function() {
let listOfTasks = JSON.parse( localStorage.getItem("tasks"));
if( listOfTasks == undefined ){
listOfTasks = [];
}
for( let i = 0; i < listOfTasks.length; i++){
let li = $('<li> Done
</li>').text(listOfTasks[i]);
$('#listOfTasks').append(li);
}
$('#add').click(function(){
let task = $('#task').val();
listOfTasks.push(task);
localStorage.setItem("tasks", JSON.stringify(listOfTasks)
);
let li = $('<li></li>').text(task);
$('#listOfTasks').append('<li>'+ task +'<input type="submit"
class="done" value= "Done">' + '</li>');
$('#task').val(' ').focus();
});
$('.done').on('click', '.delete',function(){
$(this).parent().remove();
});
/*$('#done').click(function(){
localStorage.removeItem;
$('#listOfTasks').html('');
});*/
}); // end ready
Is this what you are trying to do ?
Note that I had to polyfill local storage to make this work in a snippet, replace fakeLocalStorage by localStorage
const listOfTasksElement = $('#listOfTasks')
const taskInputElement = $('#task')
const listOfTasks = JSON.parse(fakeLocalStorage.getItem('tasks')) || []
const updateTasks = () => fakeLocalStorage.setItem('tasks', JSON.stringify(listOfTasks))
const addTask = task => {
const taskElement = $('<li></li>').text(task)
const doneElement = $('<span>Done</span>').click(() => {
const index = listOfTasksElement.find('li').index(taskElement)
taskElement.remove()
listOfTasks.splice(index, 1)
updateTasks()
})
taskElement.append(doneElement)
listOfTasksElement.append(taskElement)
listOfTasks.push(task)
updateTasks()
}
listOfTasks.forEach(addTask)
$('#add').click(() => {
addTask(taskInputElement.val())
taskInputElement.val('').focus()
})
<ul id="listOfTasks"></ul>
<input id="task"><button id="add">Add</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
// local storage doesn't work in stack overflow snippets,
// this is just a poor in-memory implementation
const fakeLocalStorage = {
_data: {},
setItem(k, v) { return this._data[k] = v },
getItem(k) { return this._data.hasOwnProperty(k) ? this._data[k] : null }
}
</script>
I am a newbie trying to learn JavaScripts. So I'm trying to clone an app called Momentum, and I am facing a problem with adding and removing the form and name of the user.
as it can be seen in the loadName() function, if there is a name, it should activate greetUser() function to remove the "showing" class from the form and add the "showing" to greeting class list. If there isn't a name, it should display a form where the user can enter their name.
However, even if I assign the name or not, neither the form will display nor the name.
I have tried changing the names, css file, and other things that I could think of but did not work as I expected. Below is the code that I am working with. It probably is some stupid mistake that I've made, but I just am not able to find out what the problem is.
greetings.js
const form = document.querySelector(".js-form");
const input = form.querySelector("input");
const greeting = document.querySelector(".js-greetings");
const USER_LS = "currentUser";
const SHOWING_CN = "showing";
function saveName(text) {
localStorage.setItem(USER_LS, text);
}
function handleSubmit() {
event.preventDefault();
const currentValue = input.value;
greetUser(currentValue);
saveName(currentValue);
}
function askForName() {
form.classList.add(SHOWING_CN);
form.addEventListener("submit", handleSubmit);
}
function greetUser(text) {
form.classList.remove(SHOWING_CN);
greeting.classList.add(SHOWING_CN);
greeting.innerText = `Hello, ${text}`;
}
function loadName() {
const currentUser = localStorage.getItem(USER_LS);
if (currentUser === null) {
askForName();
} else {
greetUser(currentUser);
}
}
function init() {
loadName();
}
index.css
.form,
.greetings {
display: none;
}
.showing {
display: block;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Something</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div class="js-clock">
<h1>00:00</h1>
</div>
<form class="js-form form">
<input type="text" placeholder="What is your name?" />
</form>
<h4 class="js-greetings greetings"></h4>
<script src="clock.js"></script>
<script src="greetings.js"></script>
</body>
</html>
There was an issue with firing off your loadName() function. This was being called from init(), but nothing was calling init(). I changed it to call loadName() once the window has loaded. There was also an issue with getting the constant USER_LS from local storage when it hadn't yet been set. I've just referred directly to it since it is defined globally. I've demonstrated the USER_LS being set to a name as well as to null (this is commented out since a constant can only be defined once) to show how the inputs appear for each scenario:
const form = document.querySelector(".js-form");
const input = form.querySelector("input");
const greeting = document.querySelector(".js-greetings");
// const USER_LS = null;
const USER_LS = "Bob";
const SHOWING_CN = "showing";
function saveName(text) {
localStorage.setItem(USER_LS, text);
}
function handleSubmit() {
event.preventDefault();
const currentValue = input.value;
greetUser(currentValue);
saveName(currentValue);
}
function askForName() {
form.classList.add(SHOWING_CN);
form.addEventListener("submit", handleSubmit);
}
function greetUser(text) {
form.classList.remove(SHOWING_CN);
greeting.classList.add(SHOWING_CN);
greeting.innerText = `Hello, ${text}`;
}
function loadName() {
const currentUser = USER_LS;
if (currentUser === null) {
askForName();
} else {
greetUser(currentUser);
}
}
window.load = loadName();
.form,
.greetings {
display: none;
}
.showing {
display: block;
}
<div class="js-clock">
<h1>00:00</h1>
</div>
<form class="js-form form">
<input type="text" placeholder="What is your name?" />
</form>
<h4 class="js-greetings greetings"></h4>
<script src="clock.js"></script>
<script src="greetings.js"></script>