10分钟做一个简单任务管理

179 阅读1分钟

image.png

相关技术

  • html
  • css
  • JavaScript
  • 数据驱动视图

数据驱动视图是指,页面随着数据的变化而变化

需求

  1. 有一个输入框,用于给用户填写具体任务
  2. 任务的状态可以切换(已完成/未完成)
  3. 可以删除已经输入的任务
  4. 分别统计已完成的任务和未完成的任务
  5. 可以把已完成的任务清理掉
  6. 页面打开后保持和页面关闭前前状态一致

实现

搭建静态结构

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
    />
    <title>04-待办列表.html</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        background-color: #ccc;
      }

      ul {
        list-style: none;
      }

      li {
        padding: 20px;
        text-align: left;
        font-size: 30px;
        border-bottom: 1px dashed #ccc;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }

      li input {
        margin-right: 10px;
      }

      li button {
        display: none;
        padding: 5px;
      }

      li:hover button {
        display: inline-block;
        cursor: pointer;
      }

      .chk:checked + span {
        text-decoration: line-through;
      }

      h1 {
        margin-bottom: 10px;
      }

      .chk + span {
        width: 100%;
      }

      .box {
        background-color: #fff;
        width: 60vw;
        padding: 20px 20px 0;
        margin: 50px auto;
      }

      .box .tool input {
        width: 100%;
        height: 50px;
        text-indent: 20px;
        font-size: 20px;
        font-style: italic;
        color: #666;
        font-weight: 700;
      }

      section {
        height: 50px;
        display: flex;
        justify-content: space-between;
        align-items: center;
      }

      a {
        text-decoration-color: #666;
        color: inherit;
      }
    </style>
  </head>

  <body>
    <div id="app" data-v-app="">
      <div class="box">
        <h1>任务列表</h1>
        <div class="tool">
          <input
            id="word"
            autofocus=""
            type="text"
            placeholder="请输入代办事项"
          />
        </div>
        <ul></ul>
        <section>
          <span><i>1</i> 未完成</span><a href="#">清理 <b>0</b> 已完成</a>
        </section>
      </div>
    </div>
  </body>
</html>

基本结构效果图.png

封装函数

统计数量的函数

function total() {
  const inputs = document.querySelectorAll(".item");
  const i = document.querySelector("i"); // 未完成
  const b = document.querySelector("b"); // 已完成
  const unfinished = [];
  const finished = [];
  for (let i = 0; i < inputs.length; i++) {
    inputs[i].checked
      ? finished.push(inputs[i])
      : unfinished.push(inputs[i]);
  }
  i.innerText = unfinished.length;
  b.innerText = finished.length;
}

渲染函数,把数据渲染到页面上

const ul = document.querySelector("ul"); // 任务列表

function render() {
  const lis = item.map((ele, i) => {
    return `
    <li data-id=${i}>
      <input type="checkbox" class="item chk" 
      ${ele.isChecked ? "checked" : ""}
       />
      <span>${ele.msg}</span>
      <button>❌</button>
    </li>
  `;
  });
  
  ul.innerHTML = lis.join("");
  total();
}

实现功能

声明一个数组用来存放数据

const item = JSON.parse(localStorage.getItem("item")) || [];

由于要使页面打开后和关闭前状态一致,这里我们使用到了本地存储

实现添加任务功能

const word = document.querySelector("#word"); // 输入框

word.addEventListener("keyup", function (e) {
  if (e.key === "Enter") {
    item.unshift({
      msg: this.value,
    });
    localStorage.setItem("item", JSON.stringify(item));
    render();
    this.value = "";
  }
});

用户按下回车键进行添加任务

实现删除任务功能

const ul = document.querySelector("ul"); // 任务列表

ul.addEventListener("click", function (e) {
  if (e.target.tagName === "BUTTON") {
    if (confirm("是否确认删除该事项?")) {
      item.splice(e.target.dataset.id, 1);
      localStorage.setItem("item", JSON.stringify(item));
      render();
    }
  }
});

使用了事件委托

实现清理已完成功能

const a = document.querySelector("a"); // 清理按钮

a.addEventListener("click", function (e) {
  item.forEach((ele, i) => {
    if (ele.isChecked) {
      item.splice(i, 1);
      localStorage.setItem("item", JSON.stringify(item));
      render();
    }
  });
});

实时统计已完成数量和未完成数量

this.addEventListener("change", function (e) {
  const inputs = document.querySelectorAll(".item");
  total();
  item.forEach((ele, i) => {
    ele.isChecked = inputs[i].checked;
  });
  localStorage.setItem("item", JSON.stringify(item));
});

当页面发生变化时重新统计数量

完整JavaScript代码

<script>
  // 获取元素
  const word = document.querySelector("#word"); // 输入框
  const ul = document.querySelector("ul"); // 任务列表
  const a = document.querySelector("a"); // 清理按钮
  const item = JSON.parse(localStorage.getItem("item")) || []; // 存放事项
  render();
  // 添加
  word.addEventListener("keyup", function (e) {
    if (e.key === "Enter") {
      item.unshift({
        msg: this.value,
      });
      localStorage.setItem("item", JSON.stringify(item));
      render();
      this.value = "";
    }
  });
  // 删除
  ul.addEventListener("click", function (e) {
    if (e.target.tagName === "BUTTON") {
      if (confirm("是否确认删除该事项?")) {
        item.splice(e.target.dataset.id, 1);
        localStorage.setItem("item", JSON.stringify(item));
        render();
      }
    }
  });
  // 统计数据变换
  this.addEventListener("change", function (e) {
    const inputs = document.querySelectorAll(".item");
    total();
    item.forEach((ele, i) => {
      ele.isChecked = inputs[i].checked;
    });
    localStorage.setItem("item", JSON.stringify(item));
  });
  // 清理已完成功能
  a.addEventListener("click", function (e) {
    item.forEach((ele, i) => {
      if (ele.isChecked) {
        item.splice(i, 1);
        localStorage.setItem("item", JSON.stringify(item));
        render();
      }
    });
  });
  // 渲染函数
  function render() {
    const lis = item.map((ele, i) => {
      return `
      <li data-id=${i}>
        <input type="checkbox" class="item chk" 
        ${ele.isChecked ? "checked" : ""}
         />
        <span>${ele.msg}</span>
        <button>❌</button>
      </li>
    `;
    });
    ul.innerHTML = lis.join("");
    total();
  }
  // 统计函数
  function total() {
    const inputs = document.querySelectorAll(".item");
    const i = document.querySelector("i"); // 未完成
    const b = document.querySelector("b"); // 已完成
    const unfinished = [];
    const finished = [];
    for (let i = 0; i < inputs.length; i++) {
      inputs[i].checked
        ? finished.push(inputs[i])
        : unfinished.push(inputs[i]);
    }
    i.innerText = unfinished.length;
    b.innerText = finished.length;
  }
</script>

总结

  • 使用数据操控页面变化比直接用DOM元素反复调整页面变化方便
  • 需要反复使用的一段代码可以封装到一个函数里
  • 要实现每次打开页面保持和关闭前一致的话,每当数据发生变化时就要存一次数据到本地存储里