前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用 HTML、CSS 和 Vanilla JavaScript 以及本地存储创建待办事项应用程序

如何使用 HTML、CSS 和 Vanilla JavaScript 以及本地存储创建待办事项应用程序

作者头像
哈德森sir
发布2024-05-16 16:36:04
570
发布2024-05-16 16:36:04
举报
文章被收录于专栏:哈德森博客哈德森博客

互联网建立在与数据交互的基础上:从用户获取数据、存储数据、更新和删除数据。待办事项应用程序是练习这些基本技能的最佳工具。

在本教程中,我们将介绍如何使用 HTML、CSS 和 JavaScript 创建功能齐全的待办事项应用程序。用户将能够执行以下操作:

  • 添加任务
  • 编辑任务,
  • 删除任务和
  • 将任务标记为已完成

HTML结构

我们的 HTML 将包含三个部分:

  • 留言区
  • 搜索框部分
  • 任务部分
代码语言:javascript
复制
<div class="container">
  <section class="message">

  </section>
  <section class="search-box">
    <input type="text" placeholder="Add Task" id="addTaskInput" />
    <button class="add-btn"><i class="fa-solid fa-plus"></i></button>
  </section>
  <section class="tasks">
    <ul>
      <!-- <li>
            <input type="radio" class="complete" checked />
            <span class="content complete">Create a Todo App with JavaScript</span>
            <div class="buttons">
              <button class="edit-btn">
                <i class="fa-solid fa-pen-to-square"></i>
              </button>
              <button class="delete-btn">
                <i class="fa-solid fa-trash"></i>
              </button>
            </div>
          </li> -->
    </ul>
  </section>
</div>

ul 元素是空的,因为我们将在其中使用 JavaScript 动态添加任务。每个任务将包含以下元素:

  • 用于将任务标记为完成的单选按钮
  • 用于显示任务的 span 元素
  • 一个编辑按钮和一个删除按钮

使用 CSS 设计样式

我们将从主体样式开始,以确保所有元素水平居中:

代码语言:javascript
复制
body {
    background: #000;
    height: 100vh;
    display: flex;
    justify-content: center;
    color: #fff;
  }

包含所有部分的容器元素将具有以下样式:

代码语言:javascript
复制
.container {
      padding: 60px 50px;
      margin-top: 100px;
      width: 500px;
      height: 500px;
      position: relative;
    }

接下来设置消息部分的样式,以确保它始终位于容器元素的中心。

代码语言:javascript
复制
.message{
      text-align: center;
      position: absolute;
      top: 50%;
      left: 30%;
    }

对于搜索框部分,使用 Flexbox 确保子元素居中对齐且间隔均匀。

代码语言:javascript
复制
.search-box {
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: space-around;
    } 

对于输入,添加以下样式:

代码语言:javascript
复制
.search-box input {
    width: 100%;
    height: 30px;
    border-radius: 20px;
    padding: 10px 10px;
    background: rgb(41, 39, 39);
    border: none;
    color: #fff;
    margin-left: 30px;

  }

为了确保我们的任务垂直堆叠,请将 flex-direction 设置为 column,并添加一些填充和边距以确保各个任务之间的空间。

代码语言:javascript
复制
.tasks {
      margin-top: 40px;
      display: flex;
      flex-direction: column;
    }

    .tasks li {
      margin-top: 10px;
      padding: 20px 10px;
      background: rgb(28, 25, 28);
      display: flex;
      justify-content: space-between;
      width: 100%;
      margin-bottom: 5px;
      border-radius: 10px;
      position: relative;

    }

使用 flex-basis 确保用于显示任务的 span 元素占据宽度的 60%,而按钮仅占据 20%。

代码语言:javascript
复制
.tasks li span {
    display: flex;
    flex-basis: 60%;
  }
  .tasks li .buttons {
    display: flex;
    flex-basis: 20%;

  }

去掉默认的按钮样式,并在编辑和删除按钮上添加透明背景,以确保图标可见:

代码语言:javascript
复制
.tasks li .buttons button {

      background: transparent;
      border: none;
    }

将以下样式添加到图标中

代码语言:javascript
复制
.buttons button i {
      color: rgb(175, 16, 16);
      font-size: 20px;
    }

对于单选按钮,我们将具有以下自定义样式:

代码语言:javascript
复制
.tasks input[type="radio"] {
      appearance: none;
      width: 20px;
      height: 20px;
      border: 1px solid #999;
      border-radius: 50%;
      outline: none;
      box-shadow: 0 0 5px #999;
      transition: box-shadow 0.3s ease;
    }
.tasks input[type="radio"]:checked {
      background-color: #bfb9b9;
    }

最后,添加这些样式,这些样式将使用 JavaScript 动态添加。

代码语言:javascript
复制
   .strike-through {
      -webkit-text-stroke: 2px rgb(179, 186, 179);
      text-decoration: line-through;
    }
    .complete {
      background-color: #bfb9b9;
    }

现在我们的应用程序看起来像这样:

JavaScript 功能

为了让用户能够添加任务,我们将使用 JavaScript。让我们首先使用 DOM(文档对象模型)获取以下 HTML 元素:

代码语言:javascript
复制
const message =  document.querySelector(".message");
const tasksContainer = document.querySelector(".tasks");
const ulElement = tasksContainer.querySelector("ul");
const taskBtn = document.querySelector(".add-btn");

接下来,让我们初始化一些变量:

代码语言:javascript
复制
let html = "";
let allTasks = [];

该变量html将存储包含代表每个任务的 HTML 标记的 html 字符串。 该allTasks数组将存储所有任务,每个任务都有一个 id(时间戳)、一个名称和一个完成值,该值可以是 true 或 false。示例任务如下所示:

代码语言:javascript
复制
{
id:1700000,
name: "Name of task",
completed:false
}

添加新任务

好吧,首先向添加任务按钮添加单击事件侦听器。在事件侦听器函数中,我们将从用户获取输入值,将其传递给函数addTask(),并将输入值设置为空字符串。

如果用户没有输入值,我们将返回:这将防止在用户没有输入任何值时向列表中添加空任务或执行不必要的操作

代码语言:javascript
复制
  const taskBtn = document.querySelector(".add-btn");
  taskBtn.addEventListener("click", function () {
    let newTaskInput = document.getElementById("addTaskInput");
    const newTask =newTaskInput.value;

    console.log(newTask);
    if (!newTask) return;

    addTask(newTask);
    newTaskInput.value = "";

  });

定义addTask()函数。

代码语言:javascript
复制
function addTask(task) {

}

在函数内部,我们想要执行以下操作:

  • 使用当前时间戳定义任务 ID
  • 将任务对象添加到allTasks数组中
  • 将 html 变量分配给任务 HTML 标记
  • 将 附加htmlulElement

更新函数如下。

代码语言:javascript
复制
function addTask(task) {
  taskId = new Date().getTime();
  allTasks.push({ id: taskId, task: task, completed: false });
  html = ` <li data-id =${taskId}>
    <input type="radio" class="complete-btn"  />
              <span class="content">${task}</span>
              <div class="buttons">

              <button class = "edit-btn" ><i class="fa-solid fa-pen-to-square"></i>Edit</button>
              <button class="delete-btn"><i class="fa-solid fa-trash"></i>Delete</button>

          </div>

          </li>`;
  ulElement.innerHTML += html;
  editTask();

}

正如您所看到的,代表任务的每个 li 元素都有一个作为数据属性值添加的唯一 id ( data-id = ${taskId}):这将允许我们在编辑或删除任务时检索 id。

删除任务

定义一个函数,名为removeTask()

代码语言:javascript
复制
function removeTask(){

}

在函数内部removeTask(),我们想要获取 li 元素的 data 属性并从 DOM 中删除任务。

代码语言:javascript
复制
    function removeTask(){
      deleteBtn = document.querySelectorAll(".delete-btn");
    deleteBtn.forEach((element) => {
      element.addEventListener("click", function (e) {
        const liElement = this.closest("li");
        const taskId = liElement.getAttribute("data-id");
        liElement.remove();
      });
    });

    }

让我们分解一下removeTask()函数中发生了什么。

  • 由于所有删除按钮都具有相同的类,因此我们使用该querySelectorAll属性来选择所有按钮。
  • 使用 forEach 迭代每个按钮
  • 对于每个按钮,我们使用最接近按钮的 li 元素this.closest("li)(其中 this 指的是单击的按钮)。
  • liElement然后我们从 DOM 中删除。
  • 最后,我们获取 li 元素的 data 属性值并将其存储在名为 的变量中taskId。我们在实现本地存储时会用到这个值

编辑任务

定义一个名为 的函数editTask()。在这个函数中,我们想要执行与删除按钮相同的步骤:即:

  • 获取所有编辑按钮
  • 使用forEach()方法迭代并获取最接近的li元素
  • 获取 data-id 属性
  • allTasks使用 id 在数组中查找任务
  • 更新 DOM 中的任务名称

更新editTask()函数如下:

代码语言:javascript
复制
      function editTask() {
        editBtn = document.querySelectorAll(".edit-btn");
        editBtn.forEach((element) => {
          element.addEventListener("click", function (event) {
            const liElement = event.target.closest("li");
            const taskId = liElement.getAttribute("data-id");

            const taskIdIndex = allTasks.findIndex(
              (task) => task.id.toString() === taskId
            );

            if (taskIdIndex !== -1) {
            const currentTask = allTasks[taskIdIndex].task;
              const newTask = prompt("Edit Task:", currentTask);
              if (
                newTask !== null &&
                newTask !== "" &&
                newTask !== currentTask
              ) {
                allTasks[taskIdIndex].task = newTask;

                const contentElement = liElement.querySelector(".content");
                contentElement.textContent = newTask;

              }
            }
          });
        });
      }

让我们分解一下editTask()上面函数中发生的事情:

  • 从 data 属性获取任务 id 后,我们使用该findIndex()方法检查该 id 是否存在于allTaksks数组中。
  • 当传递给数组时,该findIndex()方法查找满足指定条件的第一个元素的索引。如果没有找到元素,则返回-1
  • 如果taskIndex不是-1,我们使用该taskIndex值来获取当前任务,代码如下allTasks[taskIndex].task const newTask = prompt("Edit Task", currentTask);:显示一个提示对话框,其中包含消息“编辑任务:”,并将输入值设置为当前任务内容(currentTask)。
  • 然后新值存储在newTask变量中。
  • if 语句验证用户输入的新值。
  • allTasks[taskIndex].task = newTask:更新数组中的新任务名称。
  • 最后,我们使用以下代码更新当前 li 元素的 span 内容:contentElement.textContent = new Task;

现在,如果您单击任何任务的编辑按钮,您应该会看到此提示。

将任务标记为完成

要将任务标记为完成,我们将以下 CSS 类应用于单选按钮和 li 元素中的内容。

代码语言:javascript
复制
  .tasks input[type="radio"]:checked {
    background-color: #bfb9b9;
  }

  .strike-through {
    -webkit-text-stroke: 2px rgb(179, 186, 179);
    text-decoration: line-through;
  }
  .complete {
    background-color: #bfb9b9;
  }

创建completeTask()函数,如下所示:

代码语言:javascript
复制
function completeTask() {
  completeBtn = document.querySelectorAll(".complete-btn");
  completeBtn.forEach((element) => {
    element.addEventListener("click", function (e) {
      const liElement = event.target.closest("li");
      console.log(liElement);
      const contentSpan = liElement.querySelector(".content");

      contentSpan.classList.add("strike-through");

      const taskId = liElement.getAttribute("data-id");
      const taskIndex = allTasks.findIndex(
        (task) => task.id.toString() === taskId
      );
      if (taskIndex !== -1) {
        allTasks[taskIndex].completed = this.checked;

      }
    });
  });
}

在该completeTask()函数中,我们执行以下操作:

  • 将事件侦听器附加到单选按钮,对于每个按钮,我们从最近的 li 元素的 data 属性中获取任务 id。
  • 将删除线 CSS 类添加到当前 li 元素的范围
  • 使用该findIndex()方法从数组中获取当前任务的索引allTasks,然后将按钮的状态更新为选中。

本地存储功能

即使添加任务后,刷新页面后它们也会消失。为了持久存储,我们将添加本地存储功能。

本地存储是一个允许您在浏览器中存储数据的对象。数据以键值对的字符串形式存储。即使关闭浏览器后,存储在浏览器中的数据仍然存在。只有清除缓存后,它才会被删除。

将此功能添加到我们的项目中将允许添加的数据即使在刷新或关闭页面后也能保留。 要将数据存储在本地存储中,可以使用 setItem,如下所示。

代码语言:javascript
复制
localStorage.setItem("task", "New task");

存储此数据后,使用 Chrome 开发工具,您可以在“应用程序”选项卡下看到这些数据。

要获取存储在本地存储中的项目,请使用以下密钥:

代码语言:javascript
复制
localStorage.getItem("tasks")

从本地存储中删除项目

代码语言:javascript
复制
localStorage.clear();

添加任务到本地存储

让我们实现在本地存储中添加任务的功能。由于我们已经拥有数组中的所有任务allTasks,因此我们需要做的就是将数据添加到本地存储中,如下所示:

代码语言:javascript
复制
  localStorage.setItem("tasks", JSON.stringify(allTasks));

由于本地存储中存储的数据是字符串格式,因此我们习惯JSON.stringify将任务对象转换为字符串进行存储。

更新addTask()函数如下:

代码语言:javascript
复制
function addTask(task) {
  // the rest of the code
  localStorage.setItem("tasks", JSON.stringify(allTasks));

}

现在返回并添加一些任务,您应该在浏览器中看到它们。

从本地存储加载

我们还需要从本地存储加载任务。创建一个名为 的函数loadFromStorage()。该函数将检查本地存储中是否有任务,如果找到,任务将使用该函数呈现在页面上renderTasks()

代码语言:javascript
复制
function loadFromStorage() {
    const storedTasks = localStorage.getItem("tasks");
    if (storedTasks) {
      allTasks = JSON.parse(storedTasks);
      renderTasks();
    }
  }

创建该renderTasks()函数并添加以下代码。

代码语言:javascript
复制
function renderTasks() {
  ulElement.innerHTML = ""; // Clear existing tasks
  allTasks.forEach((task) => {
    const completedClass = task.completed
      ? "complete strike-through"
      : "";
    const html = `
      <li data-id="${task.id}" class="${completedClass}">
          <input type="radio" class="complete-btn" ${
            task.completed ? "checked" : ""
          } />
          <span class="content">${task.task}</span>
          <div class="buttons">
              <button class="edit-btn"><i class="fa-solid fa-pen-to-square"></i>Edit</button>
              <button class="delete-btn"><i class="fa-solid fa-trash"></i>Delete</button>
          </div>
      </li>`;
    ulElement.innerHTML += html;
  });

  editTask();
  completeTask();
  removeTask();
}

让我们分解一下该renderTasks()函数的作用: `

  • ulElement.innerHTML = "" `:清除页面上任何现有任务
  • 然后,我们使用该forEach()方法迭代allTasks数组并将每个任务的 HTML 标记添加到ulElement.
  • const completedClass=task.completed? "complete strike-through": "":是一个条件,用于检查是否task.completed为 true 并添加“完整删除线”CSS 类。如果task.completed为 false,则不会应用 CSS 类。
  • 最后,我们将附加 editTask、completeTask 和 removeTask 事件侦听器。

更新本地存储中的任务

要更新本地存储中的任务,请更新editTask()函数如下:

代码语言:javascript
复制
function editTask() {
  editBtn = document.querySelectorAll(".edit-btn");
  editBtn.forEach((element) => {
    element.addEventListener("click", function (event) {
      const liElement = event.target.closest("li");
      const taskId = liElement.getAttribute("data-id");

      const taskIdIndex = allTasks.findIndex(
        (task) => task.id.toString() === taskId
      );

      if (taskIdIndex !== -1) {
        console.log(allTasks[taskIdIndex]);
        console.log(allTasks[taskIdIndex].task);

        const currentTask = allTasks[taskIdIndex].task;
        const newTask = prompt("Edit Task:", currentTask);
        if (
          newTask !== null &&
          newTask !== "" &&
          newTask !== currentTask
        ) {
          allTasks[taskIdIndex].task = newTask;

          const contentElement = liElement.querySelector(".content");
          contentElement.textContent = newTask;

          localStorage.setItem("tasks", JSON.stringify(allTasks)); //update this line
        }
      }
    });
  });
}

该行将localStorage.setItem("tasks",JSON.stringify(allTasks); 确保任务更新后更新任务的当前状态。 要从本地存储中删除任务,请创建一个deleteTask()函数并添加以下代码;

代码语言:javascript
复制
function deleteTask(id) {
  const taskIdIndex = allTasks.findIndex(
    (task) => task.id.toString() === id
  );
  if (taskIdIndex !== -1) {
    allTasks.splice(taskIdIndex, 1);
    localStorage.setItem("tasks", JSON.stringify(allTasks));
  }
}

deleteTask()上面的函数中,我们使用任务的 id 来检查它是否存在于数组中allTasks。如果找到,我们使用该splice()方法从数组中删除该任务allTasks

更新removeTasks()函数如下:

代码语言:javascript
复制
function removeTask() {
  deleteBtn = document.querySelectorAll(".delete-btn");
  deleteBtn.forEach((element) => {
    element.addEventListener("click", function (e) {
      const liElement = this.closest("li");
      console.log(this);

      const taskId = liElement.getAttribute("data-id");
      liElement.remove();
      deleteTask(taskId); //add this line 

    });
  });
}

最后要做的事情是,如果用户没有待处理的任务,则向用户显示一条消息:

代码语言:javascript
复制
function updateMessage() {
  if (ulElement.children.length === 0) {
    message.innerHTML = "You are all caught up";
  } else {
    message.innerHTML = "";
  }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • HTML结构
  • 使用 CSS 设计样式
  • JavaScript 功能
    • 添加新任务
      • 删除任务
        • 编辑任务
          • 将任务标记为完成
          • 本地存储功能
            • 添加任务到本地存储
              • 从本地存储加载
                • 更新本地存储中的任务
                相关产品与服务
                对象存储
                对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档