This question already has answers here:
javascript show hidden element when input is filled
(4 answers)
Closed last month.
I'm trying to build to-do app and when I add something without typing anything (blank) it still comes with a delete button. I've set it as if you don't type anything in input, then the alert will show as "Please add a task." Here's what I did so far.
window.addEventListener('load', () => {
const taskForm = document.querySelector('#task-form');
const taskInput = document.querySelector('#task-input');
const taskList = document.querySelector('#task-list');
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const task = taskInput.value;
addTask(task);
taskInput.value = '';
if (!task) {
alert('Please add the task.');
return;
}
});
// Add button
const addTask = (task) => {
const divItem = document.createElement('div');
const showItem = taskList.appendChild(divItem);
showItem.innerHTML = task;
// Delete button
const buttonDelete = document.createElement('button');
buttonDelete.innerHTML = 'Delete';
divItem.appendChild(buttonDelete);
buttonDelete.addEventListener('click', (e) => {
e.preventDefault();
deleteTask(buttonDelete);
});
};
const deleteTask = (buttonDelete) => {
const selectTask = buttonDelete.closest('div');
taskList.removeChild(selectTask);
};
});
<form id="task-form">
<input id="task-input" type="text" placeholder="What's your plans?" />
<input id="task-submit" type="submit" value="Push" />
</form>
<div>
<h1>Tasks</h1>
<div id="task-list"></div>
</div>
You want to check if taskInput.value is blank and then return BEFORE you call the addTask method.
const task = taskInput.value;
if (!task) {
alert('Please add the task.');
return;
}
addTask(task);
taskInput.value = '';
You just need to check if there is no value before adding the task (and not after)
window.addEventListener('load', () => {
const taskForm = document.querySelector('#task-form');
const taskInput = document.querySelector('#task-input');
const taskList = document.querySelector('#task-list');
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const task = taskInput.value;
if (!task) {
alert('Please add the task.');
return;
}
addTask(task);
taskInput.value = '';
});
// Add button
const addTask = (task) => {
const divItem = document.createElement('div');
const showItem = taskList.appendChild(divItem);
showItem.innerHTML = task;
// Delete button
const buttonDelete = document.createElement('button');
buttonDelete.innerHTML = 'Delete';
divItem.appendChild(buttonDelete);
buttonDelete.addEventListener('click', (e) => {
e.preventDefault();
deleteTask(buttonDelete);
});
};
const deleteTask = (buttonDelete) => {
const selectTask = buttonDelete.closest('div');
taskList.removeChild(selectTask);
};
});
<form id="task-form">
<input id="task-input" type="text" placeholder="What's your plans?" />
<input id="task-submit" type="submit" value="Push" />
</form>
<div>
<h1>Tasks</h1>
<div id="task-list"></div>
</div>
window.addEventListener('load', () => {
const taskForm = document.querySelector('#task-form');
const taskInput = document.querySelector('#task-input');
const taskList = document.querySelector('#task-list');
taskForm.addEventListener('submit', (e) => {
e.preventDefault();
const task = taskInput.value;
addTask(task);
taskInput.value = '';
});
// Add button
const addTask = (task) => {
if (!task || task == "" || task == undefine) {
alert('Please add the task.');
return;
}else{
const divItem = document.createElement('div');
const showItem = taskList.appendChild(divItem);
showItem.innerHTML = task;
// Delete button
const buttonDelete = document.createElement('button');
buttonDelete.innerHTML = 'Delete';
divItem.appendChild(buttonDelete);
buttonDelete.addEventListener('click', (e) => {
e.preventDefault();
deleteTask(buttonDelete);
});
}
};
const deleteTask = (buttonDelete) => {
const selectTask = buttonDelete.closest('div');
taskList.removeChild(selectTask);
};
});
Related
I have an input form field that outputs text on submit to another created input, essentially an editable todo list. I have tried to make the input text value auto grow, but cannot figure out how to do it. Right now the user has to scroll over to see the rest of the text on each list item. This should not be.
What I tried:
I have tried creating a span and attaching editableContent but that makes my input text disappear.
I have tried setting an attribute on max-length on the created input but cannot get it to work. What is the best way to accomplish auto growing the text input value?
Here is the full codepen
const createTodoText = (todo) => {
const itemText = document.createElement("INPUT");
// const itemText = document.createElement("span");
// itemText.contentEditable
// itemText.contentEditable = 'true'
itemText.classList.add("todoText");
itemText.value = todo.name;
itemText.addEventListener("click", (e) => {
e.currentTarget.classList.add("active");
});
// update todo item when user clicks away
itemText.addEventListener("blur", (e) => {
todo.name = e.currentTarget.value;
renderTodos();
});
return itemText;
};
There you go: -
// select DOM elements
const todoForm = document.querySelector(".todo-form");
const addButton = document.querySelector(".add-button");
const input = document.querySelector(".todo-input");
const ul = document.getElementById("todoList");
let todos = [];
todoForm.addEventListener("submit", function (e) {
e.preventDefault();
addTodo(input.value);
});
const addTodo = (input) => {
if (input !== "") {
const todo = {
id: Date.now(),
name: input,
completed: false
};
todos.push(todo);
renderTodos();
todoForm.reset();
}
};
const renderTodos = (todo) => {
ul.innerHTML = "";
todos.forEach((item) => {
let li = document.createElement("LI");
// li.classList.add('item');
li.setAttribute("class", "item");
li.setAttribute("data-key", item.id);
const itemText = createTodoText(item);
const cb = buildCheckbox(item);
const db = buildDeleteButton(item);
// if (item.completed === true) {
// li.classList.add('checked');
// }
li.append(cb);
li.append(db);
li.append(itemText);
ul.append(li);
});
};
const createTodoText = (todo) => {
const itemText = document.createElement("span");
itemText.setAttribute('role','textbox');
itemText.setAttribute('contenteditable',"true");
itemText.classList.add("todoText");
itemText.innerHTML = todo.name;
itemText.addEventListener("click", (e) => {
e.currentTarget.classList.add("active");
});
// update todo item when user clicks away
itemText.addEventListener("blur", (e) => {
todo.name = e.target.textContent;
renderTodos();
});
return itemText;
};
const buildCheckbox = (todo) => {
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.name = 'checkbox';
cb.classList.add('checkbox');
cb.checked = todo.completed;
// checkbox not staying on current state ??
cb.addEventListener('click', function (e) {
if (e.target.type === 'checkbox') {
// todo.completed = e.target.value;
todo.completed = e.currentTarget.checked
e.target.parentElement.classList.toggle('checked');
}
});
return cb;
};
const buildDeleteButton = (todo) => {
const deleteButton = document.createElement("button");
deleteButton.className = "delete-button";
deleteButton.innerText = "x";
deleteButton.addEventListener("click", function (e) {
// duplicates children sometimes ??
const div = this.parentElement;
div.style.display = "none";
todos = todos.filter((item) => item.id !== todo.id);
});
return deleteButton;
};
// //------ Local Storage ------
function addToLocalStorage(todos) {}
function getFromLocalStorage() {}
// getFromLocalStorage();
This is the Javscript code part. In createTodoText, you can see the changes i've made. It's working according to what you want. What i've done is simple used 'span' instead of 'input'.
How about trying something like
if (todo.name.length) {itemText.size = todo.name.length;}
I have a problem with the local storage it seems the items are getting saved to local storage but I cannot make it work to load at start.
Any tips and advice much appreciated.
I am posting the code below.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
let LIST;
let id;
let loadSTORAGE = localStorage.getItem("STORAGE");
if (loadSTORAGE) {
LIST = JSON.parse(loadSTORAGE);
id = LIST.length;
loadList(LIST);
} else {
LIST = [];
id = 0;
}
function loadList() {
LIST.forEach(function() {
addTask();
});
}
addButton.addEventListener("click", addTask);
input.addEventListener("keyup", function(event) {
(event.keyCode === 13 ? addTask() : null)
})
function addTask() {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.innerHTML = "<button>Reset</button>"
checkBtn.innerHTML = "<button>Done</button>"
if (input.value !== "") {
newTask.textContent = input.value;
list.appendChild(newTask);
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
LIST.push({
name: input.value,
id: id,
});
id++
input.value = "";
console.log(LIST);
localStorage.setItem("STORAGE", JSON.stringify(LIST));
}
checkBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
completed.appendChild(parent);
});
delBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
});
}
You need to break out the logic of building the item and getting the value. Something like the following where the addTask just makes sure there is input and calls a method that builds an item. Now with the localstorage call, you can call just the code that builds the item.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
const loadSTORAGE = localStorage.getItem("STORAGE");
const LIST = loadSTORAGE ? JSON.parse(loadSTORAGE) : [];
let id = LIST.length;
loadList(LIST);
function loadList() {
LIST.forEach(function(data) {
addTaskElement(data);
});
}
function addTask() {
if (input.value !== "") {
cons newItem = {
name: input.value,
id: id,
};
LIST.push(newItem);
id++;
localStorage.setItem("STORAGE", JSON.stringify(LIST));
input.value = "";
addTaskElement(newItem);
}
}
function addTaskElement(data) {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.textContent = "Reset"
checkBtn.textContent = "Done"
newTask.textContent = data.name;
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
list.appendChild(newTask);
}
This is the js code
let form = document.getElementById('todoForm');
let input = document.getElementById('todoInput');
let btn = document.getElementById('btn');
let todos = [];
const loadTodos = () => {
let parent = document.getElementById('todoList');
todos.forEach(todo => {
let newLi = document.createElement('li');
newLi.innerHTML = `<li>${todo.text}</li>`
parent.appendChild(newLi);
})
}
btn.addEventListener('click', (e) => {
e.preventDefault();
let text = input.value;
let todo = {
id: todos.length + 1,
text: text,
complete: false,
}
todos.push(todo);
loadTodos();
})
window.onload = () => {
loadTodos();
}
When I add a todo for the first time its ok, but the seconed time will print the first todo again include the seconed.
example:
first todo
2.first todo
3.seconed todo
You should make another function to handle single todo added, below is your updated code
let form = document.getElementById('todoForm');
let input = document.getElementById('todoInput');
let btn = document.getElementById('btn');
let todos = [];
const loadTodos = () => {
let parent = document.getElementById('todoList');
todos.forEach(todo => {
let newLi = document.createElement('li');
newLi.innerHTML = `<li>${todo.text}</li>`
parent.appendChild(newLi);
})
}
const renderNewToDo = (todo) => {
let parent = document.getElementById('todoList');
let newLi = document.createElement('li');
newLi.innerHTML = `<li>${todo.text}</li>`
parent.appendChild(newLi);
}
btn.addEventListener('click', (e) => {
e.preventDefault();
let text = input.value;
let todo = {
id: todos.length + 1,
text: text,
complete: false,
}
todos.push(todo);
renderNewToDo(todo);
})
window.onload = () => {
loadTodos();
}
I'm trying to make Axios.get() Send its Data to the HTML.
I'm Making To-Do app which as you guessed generates Todo list items on the page after entering value of the item in the input field(Also this items are being saved in the MongoDB)
I want to display every item from mongoDB in the style of Todo List items (With edit and delete button)
Any tips please?
let taskInput = document.getElementById("new-task");
let paginationBlock = document.getElementById("pagination");
let addButton = document.getElementsByTagName("button")[0];
addButton.setAttribute("id", "add");
let incompleteTaskHolder = document.getElementById("incomplete-tasks");
let paginationHolder = document.getElementById("pagination");
let listItem;
let label;
let editButton;
let deleteButton;
let editInput;
let checkBox;
let pageCount = 1;
let currentPage = 1;
let deleteCount = 0;
let storeData = [{}]
const setPageCount = () => {
const items = [...incompleteTaskHolder.children];
pageCount = Math.ceil(items.length / 5);
};
setPageCount();
const renderPagination = () => {
const items = [...incompleteTaskHolder.children]
paginationBlock.innerHTML = "";
for (let i = 1; i <= pageCount; i++) {
let pageBtn = document.createElement("button");
pageBtn.id = "pageBtn";
pageBtn.addEventListener("click", () => {
currentPage = i;
paginationDisplay();
});
pageBtn.innerText = i;
paginationBlock.append(pageBtn);
}
};
const paginationLimit = () => {
const items = [...incompleteTaskHolder.children];
if (items.length % 5 === 0) {
items.style.display = "none";
}
};
const paginationDisplay = () => {
const items = [...incompleteTaskHolder.children];
const start = (currentPage - 1) * 5;
const end = start + 5;
items.forEach((item, index) => {
if (index >= start && index < end) {
item.style.display = "block";
} else {
item.style.display = "none";
}
});
};
const sendData = () => {
let getValue = document.getElementById('new-task').value
axios.post('http://localhost:3000/add',{
todo : getValue
}).then(res => {
storeData.push({id : res.data._id})
console.log('res', res);
}).catch(err => {
console.log('err',err);
})
console.log(storeData)
}
const getAll = (data) => {
axios.get('http://localhost:3000').then(res => {
incompleteTaskHolder.innerHTML +=res.data
console.log('res',res.data);
}).catch(err => {
console.log('err',err);
})
}
getAll();
const deleteData = (id) => {
axios.delete(`http://localhost:3000/delete/${id}`,{
id : storeData
}).then(res => {
console.log('res',res)
}).catch(err => {
console.log('err',err)
})
};
let createNewTaskElement = function (taskString) {
listItem = document.createElement("li");
checkBox = document.createElement("input");
label = document.createElement("label");
editButton = document.createElement("button");
deleteButton = document.createElement("button");
editInput = document.createElement("input");
label.innerText = taskString;
checkBox.type = "checkbox";
editInput.type = "text";
editButton.innerText = "Edit";
editButton.className = "edit";
deleteButton.innerText = "Delete";
deleteButton.className = "delete";
listItem.appendChild(checkBox);
listItem.appendChild(label);
listItem.appendChild(editInput);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
return listItem;
};
let addTask = function (showData) {
listItem = createNewTaskElement(taskInput.value);
document.getElementById("incomplete-tasks").appendChild(listItem);
bindTaskEvents(listItem, editButton);
setPageCount();
renderPagination();
paginationDisplay();
sendData();
};
let getInput = document.getElementById("new-task");
getInput.addEventListener("keyup", (event) => {
if (event.keyCode === 13) {
event.preventDefault();
document.getElementById("add").click();
}
});
let editTask = function () {
listItem = this.parentNode;
editInput = listItem.querySelector("input[type=text]");
label = listItem.querySelector("label");
containsClass = listItem.classList.contains("editMode");
if (containsClass) {
label.innerText = editInput.value;
} else {
editInput.value = label.innerText;
updateData();
}
listItem.classList.toggle("editMode");
};
let deleteTask = function () {
listItem = this.parentNode;
ul = listItem.parentNode;
ul.removeChild(listItem);
setPageCount();
renderPagination();
paginationDisplay();
deleteData();
};
addButton.onclick = addTask;
let bindTaskEvents = function (taskListItem) {
editButton = taskListItem.querySelector("button.edit");
deleteButton = taskListItem.querySelector("button.delete");
listItem = taskListItem.querySelector("label");
addButton = taskListItem.querySelector("button.add");
listItem.ondblclick = editTask;
editButton.onclick = editTask;
deleteButton.onclick = deleteTask;
};
for (let i = 0; i < incompleteTaskHolder.children.length; i++) {
bindTaskEvents(incompleteTaskHolder.children[i]);
}
Assuming you are getting a list of items as a response like ['todo1', 'todo2' 'todo3']. Let me know if your response is different in structure.
You can create a function that renders your todo list to HTML.
function createTodoList(todoArr) {
const container = document.createElement('div');
const todoList = document.createElement('ul');
document.getElementsByTagName('body')[0].appendChild(container);
container.appendChild(todoList);
todoArr.forEach(function(todo) {
const listItem = document.createElement('li');
listItem.textContent = todo;
todoList.appendChild(listItem);
});
}
// finally call the function with you response.data
createTodoList(res.data)
If you are getting a different response, you might have to
working on simple to-do-list and I'm stuck with searching tasks on list feature. User is typing in input field and I want to compare it with positions on list, if somethings fits, give it class .active (it's just font-weigth: bold;) and if it doesn't fit anymore, remove the class. For now, it is removing class only when the input field is empty and I have no idea how to edit code so it works as I want. Any ideas? https://github.com/eryk-slowinski/to-do-list
const inputAdd = document.querySelector('div.add input');
const tasksList = document.querySelector('.taskslist');
const addTask = () => {
if (inputAdd.value) {
const li = document.createElement('li');
const deleteButton = document.createElement('button');
li.textContent = inputAdd.value;
deleteButton.textContent = '\xD7';
tasksList.appendChild(li).appendChild(deleteButton);
} else return;
}
tasksList.onclick = (e) => {
if (e.target.tagName !== 'BUTTON') return;
else e.target.parentNode.remove();
}
const searchInList = (e) => {
const searchValue = e.target.value.toLowerCase();
const liList = [...document.querySelectorAll('ul.taskslist li')].filter(li => li.textContent.toLowerCase().includes(searchValue));
if (searchValue === "") liList.forEach(index => index.classList.remove('active'));
else liList.forEach(index => index.classList.add('active'));
}
document.querySelector('div.add button').addEventListener('click', addTask);
document.querySelector('div.search input').addEventListener('input', searchInList);
Right now you only remove .active if searchValue === "". You need to remove .active whenever state changes. You can iterate the items, and decide for each item to remove or add the class:
const searchInList = (e) => {
const searchValue = e.target.value.trim().toLowerCase();
const liList = document.querySelectorAll('ul.taskslist li')
.forEach(li => { // iterate all items
if (searchValue === '' || !li.textContent.toLowerCase().includes(searchValue)) { // if search term is empty or not included in li text
li.classList.remove('active');
} else {
li.classList.add('active');
}
});
}
In this code, you're only removing the class if searchValue is empty.
if (searchValue === "") liList.forEach(index => index.classList.remove('active'));
else liList.forEach(index => index.classList.add('active'));
You want to move this remove() line to the top of the function, and simply remove all active classes each a character is typed into the search box, then add it back in for the new matches each time:
const searchInList = (e) => {
[...document.querySelectorAll('ul.taskslist li')].forEach(li => li.classList.remove('active'));
.....
liList.forEach(index => index.classList.add('active'));
}
const inputAdd = document.querySelector('div.add input');
const tasksList = document.querySelector('.taskslist');
const addTask = () => {
if (inputAdd.value) {
const li = document.createElement('li');
const deleteButton = document.createElement('button');
li.textContent = inputAdd.value;
deleteButton.textContent = '\xD7';
tasksList.appendChild(li).appendChild(deleteButton);
} else return;
}
tasksList.onclick = (e) => {
if (e.target.tagName !== 'BUTTON') return;
else e.target.parentNode.remove();
}
const searchInList = (e) => {
[...document.querySelectorAll('ul.taskslist li')].forEach(li => li.classList.remove('active'));
const searchValue = e.target.value.toLowerCase();
const liList = [...document.querySelectorAll('ul.taskslist li')].filter(li => li.textContent.toLowerCase().includes(searchValue));
liList.forEach(index => index.classList.add('active'));
}
document.querySelector('div.add button').addEventListener('click', addTask);
document.querySelector('div.search input').addEventListener('input', searchInList);
li.active{
color: red;
}
<div class="wrap">
<h1>Things to be done</h1>
<div class="inputs">
<div class="add">
<label>Add task<input type="text"></label>
<button type="submit">Add</button>
</div>
<div class="search">
<label>Search for task<input type="text"></label>
</div>
<div class="counter"></div>
</div>
<div class="list">
<ul class="taskslist">
</ul>
</div>
</div>