每次提交时发生类似事件时都会附加旧事件

Old events getting appended every time similar event occurs on submit

我试图在原版 JS 中制作待办事项列表。有一个添加按钮可以在列表中添加项目。当为特定项目选择编辑选项时,添加按钮变为编辑并且输入框接受编辑的值。

当我编辑第一个项目时,它工作正常。但是当我编辑第二个项目时,第一个和第二个项目的值都会发生变化。编辑第三项时也是如此,第二项和第一项的值也会发生变化。我不确定为什么会这样。

const todoForm = document.querySelector('.todo-form');
// select the input box

const todoInput = document.querySelector('.todo-input');
// select the <ul> with class="todo-items"

const todoItemsList = document.querySelector('.todo-items');
const butt = document.querySelector('.add-button');

let todos = [];
todoForm.addEventListener('submit', (e) => {
    if(butt.innerHTML === "Add"){
        console.log("Add");
    e.preventDefault();
    AddTo(todoInput.value);
    console.log(todoInput.value);}
});

function AddTo(item){
    console.log(item);
    if(item!= ''){
        const todo = {
            id : Date.now(),
            name : item,
            completed : false
        }

        todos.push(todo);
        addToLocal();
        todoInput.value = '';
    }
}

function renderTo(){
    todoItemsList.innerHTML = '';
    todos.forEach((item) => {
        const checked = item.completed ? 'checked' : null;

        const li = document.createElement('li');
        li.setAttribute('class','item');
        li.setAttribute('data-key', item.id);

        if(item.completed == true){
            li.classList.add('checked');
        }

        li.innerHTML = `<input type = "checkbox" class = "checkbox" ${checked}> ${item.name} <button class = "delete-button">X</button> <button class = "edit-button">E</button>`;
        todoItemsList.append(li);

    })

}

function addToLocal(){
    localStorage.setItem('todos', JSON.stringify(todos));
    renderTo();
}

function getFromLocal(){
    const reference = localStorage.getItem('todos');

    if(reference){
        todos = JSON.parse(reference);
        renderTo()
    }


}

getFromLocal();

todoItemsList.addEventListener('click', (e) => {
    if(e.target.type === 'checkbox'){
        toggle(e.target.parentElement.getAttribute('data-key'));
    }

    if(e.target.classList.contains('delete-button')){
        del(e.target.parentElement.getAttribute('data-key'));
    }
    if(e.target.classList.contains('edit-button')){
        edit(e.target.parentElement.getAttribute('data-key'));
    }
})

function toggle(id){
    todos.forEach((item) => {
        if(item.id == id){
            item.completed = !item.completed;
        }
    });
    addToLocal();
}

function del(id){
    todos = todos.filter((item) => {
        return item.id != id;
    });
    addToLocal();
}

function edit(id){
    butt.innerHTML = "Edit";
    todos.forEach((item) => {
        if(item.id == id){
            todoInput.value = item.name;
            console.log(item);
            todoForm.addEventListener('submit', (e) => {
                e.preventDefault();
                console.log(item);
                item.name = todoInput.value;
                addToLocal();
            })
        }
    })
}
* {
    padding: 0;
    margin: 0;
  }
  
  body {
    width: 100vw;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    background: linear-gradient(#F00000, #DC281E);
    font-family: sans-serif;
  }
  
  button:hover {
    cursor: pointer;
    background-color: #73E831;
  }
  
  ul {
    list-style-type: none; 
  /* get rid of bullet points on side of list items */
  
  }
  
  /* common style ends */
  
  
  /* container */
  
  .container {
    min-width: 700px;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20px;
  }
  
  h1 {
    color: #fff;
    font-size: 3rem;
  }
  
  /* todo-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;
  }
  
  .add-button {
    background-color: #0000ff;
    color: #fff;
    border: none;
    outline: none;
    border-radius: 5px;
    padding: 7px;
    font-size: 1.2rem;
  }
  
  /* todo-form style ends */
  
  
  /* todo-items */
  
  .todo-items {
    min-width: 350px;
  }
  
  /* each li with class="item" */
  
  .item {
    background-color: #fff;
    padding: 10px;
    font-size: 1.1rem;
  }
  
  .item:first-child {
    border-top-left-radius: 7px;
    border-top-right-radius: 7px;
  }
  
  .item:last-child {
    border-bottom-left-radius: 7px;
    border-bottom-right-radius: 7px;
  }
  
  /* item style end */
  
  
  .checkbox {
    margin-right: 10px;
  }
  
  .delete-button {
    float: right;
    background-color: #dc143c;
    border: none;
    outline: none;
    border-radius: 7px;
    padding: 2px 5px;
    margin-left: 10px;
    font-size: 1.1rem;
    font-weight: 550;
  }
  .edit-button {
    float: right;
    background-color: blue;
    border: none;
    outline: none;
    border-radius: 7px;
    padding: 2px 5px;
    margin-left: 3px;
    font-size: 1.1rem;
    font-weight: 550;
  }
  
  /* applied when the todo item is checked */
  
  .checked { 
    text-decoration: line-through;
  }
  
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="style.css">
  <title>Todo List</title>
</head>
<body>

  <div class="container">
    <h1>Todo</h1>

    <form class="todo-form">
      <input type="text" class="todo-input" placeholder="Add a Todo...">
      <button type="submit" class="add-button">Add</button>
    </form>

    <ul class="todo-items">
      
<!-- dummy item -->

      <li class="item" data-key="1594003133171">
        <input class="checkbox" type="checkbox">
        Go to Gym
        <button class="delete-button">X</button>
      </li>
    </ul>
  </div>
  
  <script type="text/javascript" src="script.js"></script>
</body>
</html>

您正在为每个选择创建一个新的 'submit' 事件侦听器并且从不清除它们。

每次选择都会向现有事件侦听器添加一个新的事件侦听器

查看编辑方法的变化,或查看jsfiddle

const todoForm = document.querySelector('.todo-form');
// select the input box

const todoInput = document.querySelector('.todo-input');
// select the <ul> with class="todo-items"

const todoItemsList = document.querySelector('.todo-items');
const butt = document.querySelector('.add-button');

let todos = [];
todoForm.addEventListener('submit', (e) => {
    if(butt.innerHTML === "Add"){
        console.log("Add");
    e.preventDefault();
    AddTo(todoInput.value);
    console.log(todoInput.value);}
});

function AddTo(item){
    console.log(item);
    if(item!= ''){
        const todo = {
            id : Date.now(),
            name : item,
            completed : false
        }

        todos.push(todo);
        addToLocal();
        todoInput.value = '';
    }
}

function renderTo(){
    todoItemsList.innerHTML = '';
    todos.forEach((item) => {
        const checked = item.completed ? 'checked' : null;

        const li = document.createElement('li');
        li.setAttribute('class','item');
        li.setAttribute('data-key', item.id);

        if(item.completed == true){
            li.classList.add('checked');
        }

        li.innerHTML = `<input type = "checkbox" class = "checkbox" ${checked}> ${item.name} <button class = "delete-button">X</button> <button class = "edit-button">E</button>`;
        todoItemsList.append(li);

    })

}

function addToLocal(){
    localStorage.setItem('todos', JSON.stringify(todos));
    renderTo();
}

function getFromLocal(){
    const reference = localStorage.getItem('todos');

    if(reference){
        todos = JSON.parse(reference);
        renderTo()
    }


}

getFromLocal();

todoItemsList.addEventListener('click', (e) => {
    if(e.target.type === 'checkbox'){
        toggle(e.target.parentElement.getAttribute('data-key'));
    }

    if(e.target.classList.contains('delete-button')){
        del(e.target.parentElement.getAttribute('data-key'));
    }
    if(e.target.classList.contains('edit-button')){
        edit(e.target.parentElement.getAttribute('data-key'));
    }
})

function toggle(id){
    todos.forEach((item) => {
        if(item.id == id){
            item.completed = !item.completed;
        }
    });
    addToLocal();
}

function del(id){
    todos = todos.filter((item) => {
        return item.id != id;
    });
    addToLocal();
}

function edit(id){
        console.log(id)
    butt.innerHTML = "Edit";
    todos.forEach((item) => {
        if(item.id == id){
                
            todoInput.value = item.name;
            console.log('in for', item);
            const listener = (e) => {
                e.preventDefault();
                console.log('submit', item);
                item.name = todoInput.value;
                addToLocal();
                todoForm.removeEventListener('submit', listener) //<-- clear listener when its done
            }
            todoForm.addEventListener('submit', listener)
        }
    })
}
* {
    padding: 0;
    margin: 0;
  }
  
  body {
    width: 100vw;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    background: linear-gradient(#F00000, #DC281E);
    font-family: sans-serif;
  }
  
  button:hover {
    cursor: pointer;
    background-color: #73E831;
  }
  
  ul {
    list-style-type: none; 
  /* get rid of bullet points on side of list items */
  
  }
  
  /* common style ends */
  
  
  /* container */
  
  .container {
    min-width: 700px;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20px;
  }
  
  h1 {
    color: #fff;
    font-size: 3rem;
  }
  
  /* todo-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;
  }
  
  .add-button {
    background-color: #0000ff;
    color: #fff;
    border: none;
    outline: none;
    border-radius: 5px;
    padding: 7px;
    font-size: 1.2rem;
  }
  
  /* todo-form style ends */
  
  
  /* todo-items */
  
  .todo-items {
    min-width: 350px;
  }
  
  /* each li with class="item" */
  
  .item {
    background-color: #fff;
    padding: 10px;
    font-size: 1.1rem;
  }
  
  .item:first-child {
    border-top-left-radius: 7px;
    border-top-right-radius: 7px;
  }
  
  .item:last-child {
    border-bottom-left-radius: 7px;
    border-bottom-right-radius: 7px;
  }
  
  /* item style end */
  
  
  .checkbox {
    margin-right: 10px;
  }
  
  .delete-button {
    float: right;
    background-color: #dc143c;
    border: none;
    outline: none;
    border-radius: 7px;
    padding: 2px 5px;
    margin-left: 10px;
    font-size: 1.1rem;
    font-weight: 550;
  }
  .edit-button {
    float: right;
    background-color: blue;
    border: none;
    outline: none;
    border-radius: 7px;
    padding: 2px 5px;
    margin-left: 3px;
    font-size: 1.1rem;
    font-weight: 550;
  }
  
  /* applied when the todo item is checked */
  
  .checked { 
    text-decoration: line-through;
  }
  
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" type="text/css" href="style.css">
  <title>Todo List</title>
</head>
<body>

  <div class="container">
    <h1>Todo</h1>

    <form class="todo-form">
      <input type="text" class="todo-input" placeholder="Add a Todo...">
      <button type="submit" class="add-button">Add</button>
    </form>

    <ul class="todo-items">
      
<!-- dummy item -->

      <li class="item" data-key="1594003133171">
        <input class="checkbox" type="checkbox">
        Go to Gym
        <button class="delete-button">X</button>
      </li>
    </ul>
  </div>
  
  <script type="text/javascript" src="script.js"></script>
</body>
</html>