打造可爱手绘风的待办事项记事本:完整实现指南!

167 阅读8分钟

> 当指尖划过冷硬的电子屏幕,你是否也曾怀念作业本上铅笔勾勒的温暖线条?在这个万物皆可数字化的时代,为你带来一款会呼吸的待办事项本——它用CSS绘制出水彩笔触,让JavaScript跳动出童趣韵律,在代码世界复现手账本般的治愈体验。这不是普通的效率工具,而是一封写给数字原住民的复古情书,现在,让我们揭开手绘风待办应用的魔法秘籍! 快来实现你的一个的待办记事本吧!

待办事项记事本JavaScript代码:

// 获取DOM元素
const form = document.querySelector('.form'); // 获取表单元素
const input = document.querySelector('.form_input'); // 获取输入框
const ul = document.querySelector('.todo-list'); // 获取待办事项列表容器

// 数据存储
const toDoListArray = [];  // 创建空数组存储待办事项(数据层)

// 表单提交事件监听
form.addEventListener('submit', function (e) {  // 监听表单提交事件
  e.preventDefault();  // 阻止表单默认提交行为(避免页面刷新)
  
  // 生成唯一ID(使用时间戳确保每个任务有唯一标识)
  let itemId = String(Date.now());  
  // 获取用户输入内容
  let toDoItem = input.value;  
  
  // 添加任务到数据层和视图层
  addItemToArray(itemId, toDoItem); // 存储到数组
  addItemToDom(itemId, toDoItem);   // 添加到DOM
  
  input.value = ''; // 清空输入框(准备接收新输入)
})

// 添加任务到数据数组
function addItemToArray (id, item) {
  toDoListArray.push({  // 将新任务对象推入数组
    itemId: id,        // 存储唯一ID
    todoItem: item     // 存储任务内容
  })
}

// 添加任务到DOM视图
function addItemToDom (id, item) {
  const li = document.createElement('li'); // 创建新的列表项元素
  li.textContent = item;                  // 设置列表项文本内容
  li.setAttribute('data-id', id);         // 设置自定义属性存储ID(用于后续操作)
  ul.appendChild(li);                     // 将新项添加到列表末尾
}

// 任务删除功能(事件委托)
ul.addEventListener('click', function(e) {
  // 获取被点击元素的data-id属性(事件委托的关键)
  const id = e.target.getAttribute('data-id');
  
  // 检查是否有效ID(避免误点击空白区域)
  if (id) {
    removeItemFromArray(id); // 从数据数组中移除
    removeItemFromDom(id);   // 从DOM中移除
  }
})

// 从数据数组中移除任务
function removeItemFromArray (id) {
  // 使用filter方法创建新数组(排除指定ID的任务)
  toDoListArray = toDoListArray.filter(item => item.itemId !== id);
}

// 从DOM中移除任务元素
function removeItemFromDom (id) {
  // 通过data-id选择器找到对应元素
  let li = document.querySelector(`li[data-id="${id}"]`);
  // 从父元素中移除该节点
  ul.removeChild(li);
}

关键代码段解析

事件委托模式

ul.addEventListener('click', function(e) {
  const id = e.target.getAttribute('data-id');
  // ...
})
  • 原理:在父元素(ul)上设置事件监听器,而非每个子元素(li)
  • 优势:处理动态添加元素,减少内存占用,提高性能
  • 工作流程:点击事件冒泡到ul → 通过e.target定位实际点击元素 → 通过data-id获取关联数据

数据-DOM绑定

li.setAttribute('data-id', id);
  • 作用:在DOM元素上存储对应数据的唯一标识
  • 用途:实现数据层与视图层的双向关联
  • 优势:删除操作时能准确定位数据和DOM元素 函数职责分离
// 数据操作
function addItemToArray(id, item) { ... }

// 视图操作
function addItemToDom(id, item) { ... }

删除逻辑实现

toDoListArray.filter(item => item.itemId !== id);
  • 原理:使用filter创建新数组,排除指定ID的项目
  • 替代方案:findIndex + splice(直接修改原数组)
  • 选择原因:更函数式,避免直接修改原数组

待办事项记事本html代码:

<!DOCTYPE html> <!-- 声明文档类型为HTML5 -->
<html lang="en"> <!-- HTML文档根元素,设置语言为英语 -->
<head>
  <!-- 字符编码设置:确保正确显示各种字符 -->
  <meta charset="UTF-8">
  
  <!-- 视口设置:使页面在移动设备上正确缩放 -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <!-- 页面标题:显示在浏览器标签页上 -->
  <title>我的便签</title>
  
  <!-- 外部样式表链接:引入样式文件 -->
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <!-- 主容器:包裹整个应用内容 -->
  <div class="container">
    
    <!-- 标题区域:应用标题 -->
    <div class="heading">
      <!-- 主标题:应用名称 -->
      <h1 class="heading_title">To-Do List</h1>
    </div>

    <!-- 表单区域:用于添加新待办事项 -->
    <form class="form">
      <div>
        <!-- 输入框标签:描述输入框用途 -->
        <label for="todo" class="form_label">~ 今天干些什么呢? ~</label>
        
        <!-- 文本输入框:用户输入待办事项内容 -->
        <input 
          type="text" 
          class="form_input"
          id="todo"          <!-- 与label的for属性关联 -->
          size="30"          <!-- 初始宽度 -->
          required           <!-- 必填字段验证 -->
        >
        
        <!-- 提交按钮:添加新待办事项 -->
        <button class="button">
          <span>Submit</span>
        </button>
      </div>
    </form>

    <!-- 待办事项列表区域 -->
    <div>
      <!-- 无序列表:显示所有待办事项 -->
      <ul class="todo-list">
        <!-- 示例待办事项 -->
        <li>玩游戏</li>
      </ul>
    </div>
  </div>
  
  <!-- 外部JavaScript文件链接:引入交互功能 -->
  <script src="./index.js"></script>
</body>
</html>

关键部分详细说明:

<meta name="viewport">

  • 确保页面在各种设备上正确显示
  • width=device-width:使页面宽度等于设备宽度
  • initial-scale=1.0:设置初始缩放级别为100%

<link rel="stylesheet">

  • 引入外部CSS文件style.css
  • 分离样式和结构,提高可维护性

<form class="form">

  • 表单容器,用于收集用户输入
  • 包含输入框和提交按钮
  • required属性确保用户必须输入内容

<label for="todo">

  • 关联标签和输入框,提高可访问性
  • 点击标签可自动聚焦到输入框

<ul class="todo-list">

  • 无序列表容器,用于动态显示待办事项
  • JavaScript将在此添加/删除列表项

<script src="./index.js">

  • 引入JavaScript文件,添加交互功能
  • 位置在</body>前确保DOM加载完成
  • 处理表单提交和列表操作

这个HTML结构清晰划分了三个主要区域:

  1. 标题区:展示应用名称
  2. 输入区:添加新待办事项
  3. 列表区:展示所有待办事项

待办事项记事本css代码:

/* 引入Google Fonts的手写字体 */
@import url(https://fonts.googleapis.com/css?family=Gochi+Hand);



/* 页面整体样式 */
body {
  background-color: pink; /* 设置粉色背景 */
  min-height: 100vh; /* 最小高度为视口高度 */
  display: flex; /* 使用Flex布局 */
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
  font-family: "Gochi Hand", cursive; /* 使用手写字体 */
  color: #494a4b; /* 文字颜色 */
  text-align: center; /* 文本居中 */
  font-size: 130%; /* 增大基础字体大小 */
}

/* 主容器样式 */
.container {
  width: 100%; /* 宽度100% */
  min-width: 250px; /* 最小宽度 */
  max-width: 500px; /* 最大宽度 */
  min-height: 500px; /* 最小高度 */
  background-color: #f1f5f8; /* 浅灰色背景 */
  box-shadow: 4px 3px 7px 2px #000; /* 添加阴影效果 */
  border-radius: 20px; /* 圆角边框 */
  padding: 1rem; /* 内边距 */
  box-sizing: border-box; /* 盒模型计算方式 */
  
  /* 背景点状纹理 */
  background-image: radial-gradient(#bfc0c1 7.2%, transparent 0);
  background-size: 25px 25px; /* 点阵大小 */
}

/* 标题区域样式 */
.heading {
  margin-bottom: 1rem; /* 底部外边距 */
}

/* 主标题样式 */
.heading_title {
  padding: 0.2rem 1.2rem; /* 内边距 */
  background-color: #95a9ea; /* 浅蓝色背景 */
  border-radius: 20% 5% 20% 5%/5% 20% 25% 20%; /* 不规则圆角 */
  transform: rotate(3deg); /* 轻微旋转 */
  display: inline-block; /* 行内块元素 */
}

/* 表单标签样式 */
.form_label {
  display: block; /* 块级元素 */
  margin-bottom: 0.5rem; /* 底部外边距 */
}

/* 输入框样式 */
.form_input {
  box-sizing: border-box; /* 盒模型计算方式 */
  background-color: transparent; /* 透明背景 */
  padding: 0.7rem; /* 内边距 */
  border: 3px solid transparent; /* 透明边框 */
  
  /* 特殊的手绘风格边框 */
  border-bottom-right-radius: 15px 3px; /* 右下角不规则圆角 */
  border-bottom-left-radius: 3px 15px; /* 左下角不规则圆角 */
  border-bottom: dashed 3px #95a9ea; /* 底部虚线边框 */
  
  font-family: "Gochi Hand", cursive; /* 手写字体 */
  font-size: 1rem; /* 字体大小 */
  color: rgba(63, 62, 65, 0.7); /* 半透明文字颜色 */
  width: 70%; /* 宽度 */
  margin-bottom: 20px; /* 底部外边距 */
}

/* 输入框聚焦状态 */
.form_input:focus {
  outline: none; /* 移除默认轮廓 */
  border: 3px solid #95a9ea; /* 实线边框 */
}

/* 按钮基础样式 */
.button {
  padding: 0; /* 无内边距 */
  border: none; /* 无边框 */
  font-family: "Gochi Hand", cursive; /* 手写字体 */
  padding-bottom: 3px; /* 底部内边距 */
  
  /* 背景纹理(base64图片数据) */
  background-image: url("data:image/gif;base64,R0lGODlhBAAEAIAB...");
  
  border-radius: 5px; /* 圆角 */
  box-shadow: 0 2px 0 #494a4b; /* 阴影效果 */
  background-color: rgba(0, 119, 255, 0.7); /* 半透明蓝色背景 */
  
  /* 动画效果 */
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); 
  transform: rotate(4deg); /* 轻微旋转 */
}

/* 按钮内部span样式 */
.button span {
  background-color: #f1f5f8; /* 浅灰色背景 */
  display: block; /* 块级元素 */
  padding: 0.5rem 1rem; /* 内边距 */
  border-radius: 5px; /* 圆角 */
  border: 2px solid #494a4b; /* 边框 */
}

/* 按钮激活/聚焦状态 */
.button:active,
.button:focus {
  padding-bottom: 0; /* 减少底部内边距 */
  outline: 0; /* 移除轮廓 */
  transform: rotate(0deg); /* 旋转回正 */
}

/* 待办事项列表 */
.todo-list {
  text-align: left; /* 左对齐文本 */
}

/* 列表项样式 */
.todo-list li {
  padding: 0.5rem; /* 内边距 */
}

/* 列表项悬停效果 */
.todo-list li:hover {
  text-decoration: line-through wavy red; /* 波浪形删除线 */
}

关键样式解析:

背景点阵效果

background-image: radial-gradient(#bfc0c1 7.2%, transparent 0);
background-size: 25px 25px;
  • 使用径向渐变创建点状背景
  • 模拟真实便签纸的印刷纹理

手绘风格边框

border-bottom-right-radius: 15px 3px;
border-bottom-left-radius: 3px 15px;
border-bottom: dashed 3px #95a9ea;
  • 不对称圆角设计模仿手绘不规则性
  • 虚线边框模拟手绘线条

按钮动画效果

transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transform: rotate(4deg);
  • 自定义贝塞尔曲线实现弹性动画
  • 轻微旋转创造手绘随意感

波浪形删除线

text-decoration: line-through wavy red;
  • 悬停时显示红色波浪线
  • 视觉暗示任务完成状态

响应式设计

min-width: 250px;
max-width: 500px;
  • 确保在手机和桌面设备上都能良好显示
  • 限制最大宽度避免在大屏幕上过宽

3D按钮效果

box-shadow: 0 2px 0 #494a4b;
padding-bottom: 3px;
  • 创建按钮的立体感
  • 点击时通过减少padding实现按下效果

> 当最后一行代码化作屏幕上的温柔笔触,我们完成的不仅是一个应用,更是一场数字时代的温柔革命。那些跃动的便签纸、会呼吸的虚线框、带着体温的挤压动画,都在诉说着同一个故事:科技从未远离手作的温度。此刻,这个会画画、懂心跳的待办本正静静躺在云端,等待与你的创意灵魂相遇。不如现在就打开编辑器,让指尖在键盘上跳一支圆舞曲——毕竟,最动人的代码,永远是写给生活的情书