过滤香草javascript待办事项列表中的待办事项

时间:2020-09-16 13:28:03

标签: javascript html-select

我正在构建一个待办事项列表,现在我被困在尝试实现HTML选择下拉列表以在已完成和未完成的待办事项之间进行过滤。 我正在使用switch语句选择要执行的各个块,但是我不断收到以下错误“ Uncaught TypeError:无法设置未定义的属性'display'”,但我不知道未定义的元素。

const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')

addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);

// Add todo
function addTodo(e) {
    e.preventDefault()
    const todoText = input.value
    const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
    input.value = ""
    input.focus()

    if (!todoText) {
        alert('You must type a todo')
    } else {
        todoUL.insertAdjacentHTML("beforeend", todoEl)
    }
}

// Remove/Complete todo
function remove(e) {
    if (e.target.id == 'deleteTodoButton') {
        e.target.parentElement.remove()
        input.focus()
    } else {
        e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
        input.focus()
    }
}

function filterTodos(e) {
    const todos = todoUL.childNodes
    todos.forEach(function (todoEl) {
        switch (e.target.value) {
            case "all":
                todoEl.style.display = "flex"
                break;

            case "completed":
                if (todoEl.classList.contains("completed")) {
                    todoEl.style.display = "flex"
                } else {
                    todoEl.style.display = "none"
                }
                break;
        }
    })
}
ul {
  list-style: none
}

.completed {
  text-decoration: line-through
}
<div class="form-container">
    <h1>Todo List App</h1>
    <form id="form">
      <input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
      <button type="submit" class="add-todo" id="addTodoButton">Add</button>
      <select name="todos" class="filter-todos">
        <option value="all">All</option>
        <option value="completed">Completed</option>
        <option value="uncompleted">Uncompleted</option>
      </select>
    </form>
    <ul id="todoUL">
    </ul>
  </div>

2 个答案:

答案 0 :(得分:1)

const input = document.getElementById('input')
const addTodoButton = document.getElementById('addTodoButton')
const todoUL = document.getElementById('todoUL')
const filterOptions = document.querySelector('.filter-todos')

addTodoButton.addEventListener('click', addTodo)
todoUL.addEventListener('click', remove);
filterOptions.addEventListener('click', filterTodos);

// Add todo
function addTodo(e) {
    e.preventDefault()
    const todoText = input.value
    const todoEl = `<li><span>${todoText}</span> <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button> <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`
    input.value = ""
    input.focus()

    if (!todoText) {
        alert('You must type a todo')
    } else {
        todoUL.insertAdjacentHTML("beforeend", todoEl)
    }
}

// Remove/Complete todo
function remove(e) {
    if (e.target.id == 'deleteTodoButton') {
        e.target.parentElement.remove()
        input.focus()
    } else {
        e.target.previousElementSibling.previousElementSibling.classList.toggle('completed')
        input.focus()
    }
}

function filterTodos(e) {
    const todos = todoUL.querySelectorAll('li > span');
    todos.forEach(function (todoEl) {
        const tgt = todoEl.parentElement;
        switch (e.target.value) {
            case "all":
                tgt.style.display = "flex"
                break;

            case "completed":
                if (todoEl.classList.contains("completed")) {
                    tgt.style.display = "flex"
                } else {
                    tgt.style.display = "none"
                }
                break;
            case "uncompleted":
                if (todoEl.classList.contains("completed")) {
                    tgt.style.display = "none"
                } else {
                    tgt.style.display = "flex"
                }
                break;
        }
    })
}
ul {
  list-style: none
}

.completed {
  text-decoration: line-through
}
<div class="form-container">
    <h1>Todo List App</h1>
    <form id="form">
      <input type="text" id="input" autocomplete="off" placeholder="Enter your todo">
      <button type="submit" class="add-todo" id="addTodoButton">Add</button>
      <select name="todos" class="filter-todos">
        <option value="all">All</option>
        <option value="completed">Completed</option>
        <option value="uncompleted">Uncompleted</option>
      </select>
    </form>
    <ul id="todoUL">
    </ul>
  </div>

答案 1 :(得分:1)

我已经按照解释的方式进行了这项工作。希望这会对您有所帮助。

我发现您编写的代码存在一些问题

将选择的click事件更改为change事件

第二种情况下,如果使用remove功能,则必须先检查e.target.id == "completeTodoButton",然后再将其标记为完成,否则我们的节点选择将无法正常工作。

第三,在使用filterTodos函数的情况下,必须在切换显示之前确保节点类型为li。我为此使用了todoEl.nodeName === "LI"比较。

在切换情况下,您不能使用todoEl.classList.contains("completed"),因为完成的类未绑定到li,而是绑定到span内部的li标记。您可以将该类移至li节点并相应地拆分元素,或者如果要继续使用当前结构,则应使用todoEl.children[0].classList.contains("completed"),因为li的第一个子节点是类completed的跨度。

您还错过了交换机中的uncompleted情况。

ul {
    list-style: none;
}

.completed {
    text-decoration: line-through;
}
<div class="form-container">
    <h1>Todo List App</h1>
    <form id="form">
    <input
        type="text"
        id="input"
        autocomplete="off"
        placeholder="Enter your todo"
    />
    <button type="submit" class="add-todo" id="addTodoButton">Add</button>
    <select name="todos" class="filter-todos">
        <option value="all">All</option>
        <option value="completed">Completed</option>
        <option value="uncompleted">Uncompleted</option>
    </select>
    </form>
    <ul id="todoUL"></ul>
</div>

<script>
    const input = document.getElementById("input");
    const addTodoButton = document.getElementById("addTodoButton");
    const todoUL = document.getElementById("todoUL");
    const filterOptions = document.querySelector(".filter-todos");

    addTodoButton.addEventListener("click", addTodo);
    todoUL.addEventListener("click", remove);
    filterOptions.addEventListener("change", filterTodos);

    // Add todo
    function addTodo(e) {
      e.preventDefault();
      const todoText = input.value;
      const todoEl = `<li><span>${todoText}</span>
        <button class="delete" id="deleteTodoButton"><i class="far fa-trash-alt"></i>Delete</button>
        <button class="complete" id="completeTodoButton"><i class="fas fa-check"></i>Completed</button></li>`;
      input.value = "";
      input.focus();

      if (!todoText) {
        alert("You must type a todo");
      } else {
        todoUL.insertAdjacentHTML("beforeend", todoEl);
      }
    }

    // Remove/Complete todo
    function remove(e) {
      if (e.target.id == "deleteTodoButton") {
        e.target.parentElement.remove();
        input.focus();
      } else if (e.target.id == "completeTodoButton") {
        e.target.previousElementSibling.previousElementSibling.classList.toggle(
          "completed"
        );
        input.focus();
      }
    }

    function filterTodos(e) {
      const todos = todoUL.childNodes;
      todos.forEach(function(todoEl) {
        if (todoEl.nodeName === "LI") {
          switch (e.target.value) {
            case "all":
              todoEl.style.display = "flex";
              break;

            case "completed":
              if (todoEl.children[0].classList.contains("completed")) {
                todoEl.style.display = "flex";
              } else {
                todoEl.style.display = "none";
              }
              break;

            case "uncompleted":
              if (todoEl.children[0].classList.contains("completed")) {
                todoEl.style.display = "none";
              } else {
                todoEl.style.display = "flex";
              }
              break;
          }
        }
      });
    }
  </script>