如何在事件监听器中传递项目 ID 来更新列表输入项?
2021-07-14
194
我已经设法自行构建了待办事项列表,并实现了 addTodo 和添加到本地存储/从本地存储获取的功能。我现在需要添加更新功能来更新待办事项列表项。我知道我首先需要将事件侦听器附加到 ul 并找到最接近的 li 项 id 或类似的东西。然后我需要将更新函数传递给事件侦听器以更新列表中的输入值。这是我目前所拥有的。我将如何创建事件侦听器和更新函数来更新列表项?
所需输出:用户单击文本并更改项目的文本后,添加项目的文本需要更新(到本地存储)。
注意:该代码片段不起作用,因为不允许本地存储(据我所知)。您需要查看 codepen
// select DOM elements
const todoForm = document.querySelector('.todo-form');
const todoInput = document.querySelector('.todo-input');
const todoItemsList = document.querySelector('.todo-items');
// array which stores every todo item
// each item will be an object with id, name, completed boollean
let todos = [];
// add an event listener on form
todoForm.addEventListener('submit', function(e) {
e.preventDefault();
// input param from addTodo is todoInput.value
addTodo(todoInput.value);
});
document.addEventListener('click', (e) => {
if (e.target.classList.contains('checkbox')) {
if (e.target.checked) e.target.closest('.item').classList.add('checked');
else e.target.closest('.item').classList.remove('checked');
}
});
// todoItemsList.addEventListener('input', (e) => {
// const taskId = e.target.item.id;
// console.log(taskId);
// });
// todoItemsList.addEventListener('input', (e) => {
// const taskId = e.target.closest('li').id
// console.log(taskId)
// // updateTask(taskId, e.target)
// })
function addTodo(input) {
if (input !== '') {
const todo = {
id: Date.now(),
name: input,
completed: false,
};
todos.push(todo);
addToLocalStorage(todos);
todoInput.value = '';
}
}
// todos is passed as parameter because it exists outside function environment
function renderTodos(todos) {
// clear list
todoItemsList.innerHTML = '';
// run through each item inside todos
todos.forEach((item) => {
// checkbox
let cb = document.createElement('input');
cb.type = 'checkbox';
cb.classList.add('checkbox');
cb.checked = false;
// delete button
const deleteButton = document.createElement('button');
deleteButton.className = 'delete-button';
deleteButton.innerText = 'x';
// lists
const li = document.createElement('li');
li.classList.add('item');
li.setAttribute('data-key', item.id);
li.appendChild(cb);
li.append(item.name);
li.append(deleteButton);
todoItemsList.append(li);
});
}
function addToLocalStorage(todos) {
// name key 'todos" and value todos array
// convert array to string and store it
localStorage.setItem('todos', JSON.stringify(todos));
renderTodos(todos);
}
function getFromLocalStorage() {
const reference = localStorage.getItem('todos');
if (reference) {
// converts string back to an array and store in todos array
todos = JSON.parse(reference);
renderTodos(todos);
}
}
getFromLocalStorage();
* {
padding: 0;
margin: 0;
}
body {
width: 100vw;
min-height: 100vh;
display: flex;
justify-content: center;
background: hsl(194, 100%, 70%);
font-family: 'Roboto', sans-serif;
}
button {
display: inline-block;
padding: 0.35em 1.2em;
border: 0.1em solid hsl(0, 0%, 0%);
margin: 0 0.3em 0.3em 0;
background: hsl(0, 0%, 0%);
border-radius: 0.12em;
box-sizing: border-box;
text-decoration: none;
font-family: 'Roboto', sans-serif;
font-weight: 300;
color: hsl(0, 0%, 100%);
text-align: center;
transition: all 0.2s;
}
button:hover {
cursor: pointer;
background-color: hsl(0, 0%, 100%);
color: hsl(214, 11%, 13%);
}
ul {
list-style-type: none;
}
.container {
min-width: 700px;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
h1 {
color: hsl(0, 0%, 100%);
font-size: 3rem;
}
/* To Do Form */
.todo-form {
margin: 40px 0px;
}
.todo-input {
width: 250px;
border: none;
outline: none;
border-radius: 5px;
padding: 10px;
margin-right: 10px;
font-size: 1rem;
}
.todo-items {
min-width: 350px;
height: auto;
}
/* Item style */
.item {
height: auto;
background-color: #fff;
margin: 1em;
padding: 10px;
font-size: 1.2rem;
vertical-align: middle;
border-radius: 5px;
}
.checkbox {
width: 1.5em;
height: 1.5em;
margin-right: 20px;
vertical-align: middle;
}
.delete-button {
display: inline-flex;
justify-content: center;
align-items: center;
float: right;
width: 25px;
height: 25px;
background-color: hsl(348, 93%, 56%);
border: none;
outline: none;
/* border-radius: 7px; */
/* padding: 2px 5px; */
/* margin-left: 10px; */
font-size: 0.8rem;
font-weight: 550;
text-align: center;
vertical-align: middle;
}
.delete-button:hover {
background: pink;
}
.checked {
text-decoration: line-through;
}
.text-container {
min-width: 100px;
height: 50px;
background: red;
}
<div class="container">
<h1>To Do</h1>
<form class="todo-form">
<input type="text" class="todo-input" placeholder="add a todo item">
<button type="submit" class="add=button">Add</button>
</form>
<ul class="todo-items">
</ul>
</div>
2个回答
实际上,这里不需要 ID 。您可以继续使用元素的索引来确定它属于哪个待办事项。查看以下代码
todoItemsList.addEventListener('click', (e) => {
// Anywhere inside UL clicked
let clickedElement = e.target;
if (clickedElement.classList.contains('delete-button') || clickedElement.classList.contains('checkbox')) {
// Either checkbox or delete button is clicked
let listElement = clickedElement.parentNode;
let listElementsArray = todoItemsList.querySelectorAll('li');
let index = Array.prototype.indexOf.call(listElementsArray, listElement);
if (clickedElement.classList.contains('delete-button')) {
//Delete button clicked
listElement.remove();
todos.splice(index, 1);
} else if (clickedElement.classList.contains('checkbox')) {
//Checkbox clicked
todos[index].completed = e.target.checked;
}
console.log(todos);
// Update in localstorage
localStorage.setItem('todos', JSON.stringify(todos));
}
});
注意:这里我使用了
Array.prototype.indexOf.call
而不是
listElementsArray.indexOf
,因为
listElementsArray
是一个节点列表,它本身不支持
indexOf
。
Caleb Hillary
2021-07-14
我重新编写了你的代码,因为我在其中迷失了方向...
你感兴趣的部分在最后,其中:
todoList.onclick = ({target}) =>
{
if (!target.matches('li input[type=checkbox], button.delete-button')) return
let li = target.closest('li')
, idx = todos.findIndex(x=>x.id==li.dataset.key)
;
switch (target.tagName.toLowerCase())
{
case 'input':
todos[idx].completed = target.checked
break;
case 'button':
todos.splice(idx, 1)
todoList.removeChild(li)
break;
}
todos.update_LocalStorage()
}
它是一个事件委托,用于管理复选框或删除按钮
完整代码
const
todoForm = document.querySelector('#todo-form')
, todoList = document.querySelector('#todo-items')
, todos = JSON.parse( localStorage.getItem('todos') || '[]')
;
todos.UI_add = item =>
{
let li = document.createElement('li')
li.dataset.key = item.id
li.innerHTML = `
<input type="checkbox" ${item.completed ? 'checked':''} >
<span>${item.name}</span>
<button class="delete-button">x</button>`
todoList.appendChild(li)
}
todos.update_LocalStorage = _ =>
{
localStorage.setItem('todos', JSON.stringify(todos))
}
todoForm.onsubmit = e =>
{
e.preventDefault()
let str = todoForm['todo-text'].value.trim()
if (str !== '')
{
let item = { id: Date.now(), name:str, completed: false }
todos.push( item )
todos.UI_add( item )
todos.update_LocalStorage()
todoForm['todo-text'].value = ''
todoForm['todo-text'].focus()
}
}
todoList.onclick = ({target}) =>
{
if (!target.matches('li input[type=checkbox], button.delete-button')) return
let li = target.closest('li')
, idx = todos.findIndex(x=>x.id==li.dataset.key)
;
switch (target.tagName.toLowerCase())
{
case 'input':
todos[idx].completed = target.checked
break;
case 'button':
todos.splice(idx, 1)
todoList.removeChild(li)
break;
}
todos.update_LocalStorage()
}
// init user interface list
todos.forEach(todos.UI_add)
* {
padding: 0;
margin: 0;
}
body {
width: 100vw;
min-height: 100vh;
display: flex;
justify-content: center;
background: hsl(194, 100%, 70%);
font-family: 'Roboto', sans-serif;
}
button {
display: inline-block;
padding: 0.35em 1.2em;
border: 0.1em solid hsl(0, 0%, 0%);
margin: 0 0.3em 0.3em 0;
background: hsl(0, 0%, 0%);
border-radius: 0.12em;
box-sizing: border-box;
text-decoration: none;
font-family: 'Roboto', sans-serif;
font-weight: 300;
color: hsl(0, 0%, 100%);
text-align: center;
transition: all 0.2s;
}
button:hover {
cursor: pointer;
background-color: hsl(0, 0%, 100%);
color: hsl(214, 11%, 13%);
}
.container {
min-width : 700px;
display : flex;
flex-direction : column;
align-items : center;
padding : 20px;
}
h1 {
color : hsl(0, 0%, 100%);
font-size : 3rem;
}
form#todo-form {
margin : 40px 0;
}
form#todo-form input {
width : 250px;
border : none;
outline : none;
border-radius : 5px;
padding : 10px;
margin-right : 10px;
font-size : 1rem;
}
ul#todo-items {
min-width : 350px;
height : auto;
list-style : none;
}
ul#todo-items li {
height : auto;
background-color : #fff;
margin : 1em;
padding : 10px;
font-size : 1.2rem;
vertical-align : middle;
border-radius : 5px;
}
ul#todo-items li input[type=checkbox] {
width : 1.5em;
height : 1.5em;
margin-right : 20px;
vertical-align : middle;
}
.delete-button {
display : inline-flex;
justify-content : center;
align-items : center;
float : right;
width : 25px;
height : 25px;
background-color : hsl(348, 93%, 56%);
border : none;
outline : none;
font-size : 0.8rem;
font-weight : 550;
text-align : center;
vertical-align : middle;
}
.delete-button:hover {
background: pink;
}
ul#todo-items li input:checked + span {
text-decoration: line-through;
}
<div class="container">
<h1>To Do</h1>
<form id="todo-form">
<input type="text" name="todo-text" placeholder="add a todo item">
<button type="submit"> Add </button>
</form>
<ul id="todo-items"> </ul>
</div>
Mister Jojo
2021-07-14