JavaScript 本地存储与动态数据渲染实战案例

JavaScript 本地存储与动态数据渲染实战案例

一、案例概述

在前端开发中,本地存储(localStorage) 是无需后端数据库即可实现数据持久化的核心技术,动态数据渲染则是前端页面展示数据的基础能力。本案例通过一个轻量化的「待办事项(TODO)管理工具」,结合两者实现完整功能:用户添加、删除、标记完成待办事项,所有数据自动保存到浏览器本地存储,刷新页面后数据不丢失,同时通过JavaScript动态将数据渲染到页面。

本案例适合前端初学者,覆盖JavaScript DOM操作、本地存储API、数据逻辑处理、事件绑定等核心知识点,无需依赖任何框架,纯原生代码即可运行,可直接部署到本地浏览器使用。

二、核心技术知识点

  1. localStorage本地存储:浏览器提供的持久化存储方案,存储键值对数据,数据永久生效(除非手动清除),存储容量约5MB;
  2. DOM操作:通过JavaScript获取、创建、修改、删除HTML元素,实现页面动态更新;
  3. 事件监听:绑定点击、提交等事件,实现用户交互响应;
  4. 数据处理:数组增删改查、JSON序列化与反序列化(localStorage仅支持字符串存储);
  5. 页面初始化:刷新页面时自动读取本地数据并渲染。

三、实现环境

  • 编辑器:VS Code(任意文本编辑器均可)
  • 运行环境:Chrome/Firefox等现代浏览器
  • 技术栈:HTML5 + CSS3 + 原生JavaScript(无第三方依赖)

四、完整代码实现

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>TODO本地存储管理工具</title>
    <style>
        * {margin: 0; padding: 0; box-sizing: border-box; font-family: Arial, sans-serif;}
        .todo-container {width: 500px; margin: 50px auto; padding: 20px; border: 1px solid #eee; border-radius: 8px;}
        .input-box {display: flex; gap: 10px; margin-bottom: 20px;}
        #todo-input {flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px;}
        #add-btn {padding: 8px 16px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer;}
        #add-btn:hover {background: #337ecc;}
        .todo-list {list-style: none;}
        .todo-item {display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #eee;}
        .todo-text {cursor: pointer;}
        .completed {text-decoration: line-through; color: #999;}
        .del-btn {color: #f56c6c; cursor: pointer; border: none; background: none;}
    </style>
</head>
<body>
    <div class="todo-container">
        <h2>待办事项管理</h2>
        <div class="input-box">
            <input type="text" id="todo-input" placeholder="请输入待办事项">
            <button id="add-btn">添加</button>
        </div>
        <ul class="todo-list" id="todo-list"></ul>
    </div>

    <script>
        // 获取DOM元素
        const todoInput = document.getElementById('todo-input');
        const addBtn = document.getElementById('add-btn');
        const todoList = document.getElementById('todo-list');

        // 初始化:读取本地存储数据
        let todos = JSON.parse(localStorage.getItem('todos')) || [];

        // 页面加载时渲染数据
        window.addEventListener('DOMContentLoaded', renderTodoList);

        // 添加按钮点击事件
        addBtn.addEventListener('click', addTodo);
        // 回车快捷添加
        todoInput.addEventListener('keydown', (e) => e.key === 'Enter' && addTodo());

        /**
         * 添加待办事项
         */
        function addTodo() {
            const text = todoInput.value.trim();
            if (!text) return alert('请输入内容!');
            
            // 创建新待办对象
            const newTodo = {
                id: Date.now(), // 唯一ID
                text: text,
                completed: false // 完成状态
            };

            todos.push(newTodo); // 追加到数组
            saveToLocal(); // 保存到本地
            renderTodoList(); // 重新渲染页面
            todoInput.value = ''; // 清空输入框
        }

        /**
         * 渲染待办列表到页面
         */
        function renderTodoList() {
            todoList.innerHTML = ''; // 清空原有内容

            todos.forEach(todo => {
                const li = document.createElement('li');
                li.className = `todo-item ${todo.completed ? 'completed' : ''}`;
                li.innerHTML = `
                    <span class="todo-text" data-id="${todo.id}">${todo.text}</span>
                    <button class="del-btn" data-id="${todo.id}">删除</button>
                `;
                todoList.appendChild(li);
            });

            // 绑定状态切换和删除事件
            bindTodoEvents();
        }

        /**
         * 绑定待办事项交互事件
         */
        function bindTodoEvents() {
            // 切换完成状态
            document.querySelectorAll('.todo-text').forEach(item => {
                item.addEventListener('click', toggleComplete);
            });
            // 删除事项
            document.querySelectorAll('.del-btn').forEach(btn => {
                btn.addEventListener('click', deleteTodo);
            });
        }

        /**
         * 切换待办事项完成状态
         */
        function toggleComplete(e) {
            const id = parseInt(e.target.dataset.id);
            todos = todos.map(todo => {
                if (todo.id === id) todo.completed = !todo.completed;
                return todo;
            });
            saveToLocal();
            renderTodoList();
        }

        /**
         * 删除待办事项
         */
        function deleteTodo(e) {
            const id = parseInt(e.target.dataset.id);
            todos = todos.filter(todo => todo.id !== id);
            saveToLocal();
            renderTodoList();
        }

        /**
         * 保存数据到localStorage
         */
        function saveToLocal() {
            localStorage.setItem('todos', JSON.stringify(todos));
        }
    </script>
</body>
</html>

五、代码核心解析

1. 本地存储数据处理

  • 读取数据:JSON.parse(localStorage.getItem('todos')) || [],本地无数据时返回空数组,避免报错;
  • 保存数据:localStorage.setItem('todos', JSON.stringify(todos)),将数组转为JSON字符串存储(localStorage不支持直接存储对象/数组)。

2. 动态渲染逻辑

  • 每次数据变更(添加、删除、状态切换)后,清空列表容器,遍历数据数组重新创建DOM元素;
  • 通过自定义data-id属性绑定数据唯一标识,实现精准操作对应事项。

3. 交互功能实现

  • 添加功能:校验输入内容,生成唯一ID,追加到数组并保存;
  • 状态切换:点击文本切换completed状态,添加删除线样式;
  • 删除功能:过滤掉对应ID的数据,更新本地存储。

六、功能测试步骤

  1. 将代码复制到文本编辑器,保存为.html文件;
  2. 用浏览器打开该文件,输入待办内容点击「添加」,或按回车快速添加;
  3. 点击待办文本,标记为已完成(显示删除线);
  4. 点击「删除」按钮,移除对应事项;
  5. 刷新浏览器页面,所有数据保持不变,验证本地存储生效。

七、案例扩展方向

  1. 增加编辑功能:支持修改已添加的待办事项;
  2. 增加筛选功能:区分显示全部、未完成、已完成事项;
  3. 增加清空全部功能:一键清除所有待办数据;
  4. 样式优化:添加动画效果、响应式布局适配移动端。

八、总结

本案例通过原生JavaScript实现了本地存储与动态数据渲染的完整闭环,核心是数据驱动视图:所有交互基于数据数组操作,视图根据数据自动更新,同时通过localStorage完成数据持久化。该模式是前端开发的基础逻辑,掌握后可快速上手Vue、React等主流框架,也能独立开发轻量化前端工具。

相关推荐
小小19921 小时前
vue 单页面请求
开发语言·前端·javascript
淀粉肠kk1 小时前
【C++11】智能指针详解
开发语言·c++
无心使然1 小时前
Openlayers调用ArcGis要素服务之一 ——要素查询 (/query)
前端·javascript·数据可视化
kyriewen111 小时前
Next.js部署:从本地跑得欢,到线上飞得稳
开发语言·前端·javascript·科技·react.js·前端框架·ecmascript
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题】【Java基础篇】第21题:HashMap和Hashtable的区别是什么
java·开发语言·面试·哈希算法·散列表·hash table
不想写代码的星星1 小时前
COW(Copy-on-Write):开抄开抄,哎嘿,我装的
开发语言·c++
慕容卡卡1 小时前
Claude 使用神器(web页面)--CloudCLI UI
java·开发语言·前端·人工智能·ui·spring cloud
咬_咬1 小时前
go语言学习(函数)
开发语言·学习·golang
froginwe111 小时前
PHP MySQL Delete 操作指南
开发语言