SOURCE

console 命令行工具 X clear

                    
>
console
const todoFormEl = document.querySelector("#todo-form");
const todoInputEl = document.querySelector("#todo-input");
const todoListEl = document.querySelector("#todo-list");

function buildUniqueId(prefix = "prefix") {
  return prefix + "-" + Math.floor(Math.random() * Date.now());
}

function createTask(name) {
  return {
    name,
    id: buildUniqueId("todo")
  };
}

const state = {
  taskName: "",
  tasks: [
    {
      id: "todo-0",
      name: "Learn some frameworks!"
    }
  ]
};

function init() {
  todoInputEl.addEventListener("change", handleInputChange);
  todoFormEl.addEventListener("submit", handleFormSubmit);
  renderInput();
  renderTodoList();
}

function renderInput() {
  todoInputEl.value = state.taskName;
}

function renderTodoList() {
  const frag = document.createDocumentFragment();

  state.tasks.forEach((task) => {
    const item = buildTodoItemEl(task.id, task.name);
    frag.appendChild(item);
  });

  while (todoListEl.lastChild) {
    todoListEl.removeChild(todoListEl.lastChild);
  }
  todoListEl.appendChild(frag);
}

function buildTodoItemEl(id, name) {
  const item = document.createElement("li");
  const span = document.createElement("span");
  const textContent = document.createTextNode(name);

  span.appendChild(textContent);

  item.id = id;
  item.appendChild(span);
  item.appendChild(buildDeleteButtonEl(id));

  return item;
}

function buildDeleteButtonEl(id) {
  const button = document.createElement("button");
  const textContent = document.createTextNode("Delete");

  button.setAttribute("type", "button");
  button.addEventListener("click", handleTodoDeleteButtonClick.bind(null, id));
  button.appendChild(textContent);

  return button;
}

function handleInputChange(e) {
  state.taskName = e.target.value;
}

function handleFormSubmit(e) {
  e.preventDefault();
  state.tasks = [...state.tasks, createTask(state.taskName)];
  state.taskName = "";
  renderInput();
  renderTodoList();
}

function handleTodoDeleteButtonClick(id) {
  state.tasks = state.tasks.filter((t) => t.id !== id);
  renderTodoList();
}

document.addEventListener("DOMContentLoaded", init);
  <h1>TodoMatic</h1>
  <form id="todo-form">
      <label for="todo-input">
        What needs to be done?
      </label>
    <input type="text" id="todo-input" name="mozTodoDemoItemName" autocomplete="on" />
    <button type="submit">
      Add
    </button>
  </form>
  <ul id="todo-list"></ul>
  <ul>
  <li v-for="task in tasks" v-bind:key="task.id">
    <span>{{task.name}}</span>
    <button type="button">Delete</button>
  </li>
</ul>
*,
*::before,
*::after {
  box-sizing: border-box;
}

* + * {
  margin-top: 0.4rem;
}

::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

:focus {
  outline: 3px solid hsl(215, 100%, 34%);
  outline-offset: .2rem;
}

html {
  font-size: 62.5%;
}

body {
  font-size: 2rem;
  line-height: 1.25;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  color: hsl(0, 0, 13%);
  
  width: 95%;
  max-width: 30em;
  padding-bottom: 2em;
  margin: 0 auto;
}

button,
input[type="text"] {
  font-size: 100%;
  line-height: 1.15;
  font-family: inherit;
  margin: 0;
  
  padding: 0.5rem;
  border: 1px solid #707070;
  border-radius: 2px;
}

* + button {
  margin-left: 0.4rem;
}

label {
  display: table;
}

ul {
  margin-top: 1.6rem;
  padding-left: 2em;
}

label + input[type="text"] {
  margin-top: 0.4rem;
}