感谢内容提供者:金牛区吴迪软件开发工作室
学习此内容需要事先掌握基本的 html、css、js 以及简单的 SQL。
SQL的学习:SQL与数据库的基本操作
/** * 打开一个数据库,没有的话会进行创建,然后返回一个数据库对象进行操作 * openDatabase的参数介绍: * 第一个参数:数据库名称 * 第二个参数:版本号 * 第三个参数:描述文本 * 第四个参数:数据库大小 * 第五个参数:创建回调 */ const db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
// 执行事务【事务是后端的一个概念,有兴趣了解的同学可以自行百度下】 db.transaction((tx) => { // 下面都是去写sql进行数据库的操作 // 当不存在 USERS 表的时候创建一个USERS表,并定义 id【unique 用来声明唯一的】name,age,time 字段 // 约束数据类型的写法笔者暂时没发现,有会的同学可以下方留言 tx.executeSql('CREATE TABLE IF NOT EXISTS USERS (id unique, name, age, time)'); // 对 USERS 表执行插入数据的操作 tx.executeSql(`INSERT INTO USERS (id, name, age, time) VALUES (1, "吴小迪", 18, "${new Date()}")`); tx.executeSql(`INSERT INTO USERS (id, name, age, time) VALUES (2, "刘小珊", 16, "${new Date()}")`); });
// 执行事务 db.transaction((tx) => { /** * executeSql 执行sql * executeSql的参数介绍 * 第一个参数:要执行的sql * 第二个参数:动态参数,可以给第一参数的sql使用 * 第三个参数:回调函数,接收俩个参数 tx: 数据库对象,res,执行完sql的response */ tx.executeSql('SELECT * FROM USERS', [], (tx, res) => { // 通过输出表格的方式,直接输出这个表的内容 console.table(res.rows); }); })
db.transaction((tx) => { tx.executeSql('UPDATE USERS SET age = 24 WHERE id = 1'); });
这样我们就把 USERS 表里的 id = 1的那条数据的age变成了24:
db.transaction((tx) => { tx.executeSql('DELETE FROM USERS WHERE id = 2'); })
这样我们就把 USERS 表里的 id = 2 的那条数据删掉了:
其实做一个 TODO List 还是很简单的,就是把我们上面所学的东西运用起来而已~
首先我们搭一个简单的 html 页面 和 简单的 css 样式。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #form-box { position: fixed; top: -100vh; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.6); display: flex; justify-content: center; align-items: center; } </style> </head> <body> <button id="add-btn">新增代办事项</button> <!-- 展示给用户看的表格 --> <table border="1"> <thead> <tr> <td>id</td> <td>待办事项</td> <td>状态</td> <td>操作</td> </tr> </thead> <tbody id="list"> <tr> <td>1</td> <td>我要学习html</td> <td>未完成</td> <td> <button>更改状态</button> <button>修改</button> </td> </tr> <tr> <td>2</td> <td>我要学习css</td> <td>完成</td> <td> <button>更改状态</button> <button>删除</button> </td> </tr> </tbody> </table> <!-- 新增和编辑用的表单Modal --> <div id="form-box"> <form> <label for="todo-text"> 代办事项: <input id="todo-text" type="text" /> </label> <button id="confirm-btn">确认</button> </form> </div> </body> </html>
效果如下:
然后这是一个简单的新增和编辑的modal
首先我们了解一下我们的功能点:
// 当前修改的数据项id【做修改的时候用】 let currentEditId = null; // 获取 新增代办事项按钮 元素 const addBtnEle = document.querySelector('#add-btn'); // 获取 modal 元素 const modalEle = document.querySelector('#form-box'); // 给 新增代办事项元素 按钮增加点击事件 addBtnEle.onclick = () => { // 将 modal 显示出来 modalEle.style.top = 0; }
// 创建一个数据库对象 const db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); // 输入框元素,下面编辑和新增都会用到,所以提到最上面进行复用 const todoTextEle = document.querySelector('#todo-text'); // 获取确认按钮元素 const confirmBtnEle = document.querySelector('#confirm-btn'); // 点击 确认按钮的时候 将数据插入到表里并且将弹窗关闭 confirmBtnEle.onclick = () => { // 执行事务 db.transaction((tx) => { // 如果不存在的话就创建一个 TODOLIST 表 tx.executeSql('CREATE TABLE IF NOT EXISTS TODOLIST (id unique, desc, status)'); if (currentEditId) { // 编辑 // 做一个简单的非空处理 if (!!todoTextEle.value) { // 更改当前编辑项的desc和将status变为未完成 tx.executeSql(`UPDATE TODOLIST SET status = 0, desc = ? WHERE id = ?`, [todoTextEle.value, currentEditId]); } else { alert('代办事项不能为空!'); } // 编辑完成之后将 currentEditId 置为 null currentEditId = null; } else { // 新增 // 查询 TODOLIST 表,找到最后一条的 id tx.executeSql('SELECT * FROM TODOLIST', [], (selectTx, res) => { // 最后一条记录的id 然后 + 1 const currentId = ((res.rows[res.rows.length - 1] && res.rows[res.rows.length - 1].id) || 0) + 1; // 做一个简单的非空处理 if (!!todoTextEle.value) { // 对 TODOLIST 表执行插入数据的操作 tx.executeSql(`INSERT INTO TODOLIST (id, desc, status) VALUES (?, ?, 0)`, [currentId, todoTextEle.value]); } else { alert('代办事项不能为空!'); } }); } // 将 modal 隐藏 modalEle.style.top = '-100vh'; // 从新渲染列表 refreshTodoList(); }); }
// 获取 table body const listEle = document.querySelector('#list'); // 获取数据,将此方法变成一个公用函数 function refreshTodoList() { db.transaction((tx) => { tx.executeSql('SELECT * FROM TODOLIST', [], (tx, res) => { // 因为 res.rows 是一个伪数组,所以我们先给他变成数组再进行遍历,当然你先获取他的length然后写for循环也可以 const resInnerHtml = [...res.rows].reduce((res, ite) => { return res += ` <tr> <td>${ite.id}</td> <td>${ite.desc}</td> <td>${ite.status == 0 ? '未完成' : '完成'}</td> <td> <button class="change-status-btn" data-id="${ite.id}" data-status="${ite.status}">更改状态</button> <button class="edit-btn" data-id="${ite.id}" data-desc="${ite.desc}">修改</button> <button class="delete-btn" data-id="${ite.id}">删除</button> </td> </tr> `; }, ''); listEle.innerHTML = resInnerHtml; }); }); } refreshTodoList();
// 需要使用事件委派.我们这里选择把事件委派到 tbody身上 listEle.onclick = (e) => { const currentEle = e.target; const currentBtnClass = currentEle.getAttribute('class'); const currentDataId = Number(currentEle.getAttribute('data-id')); const currentDataStatus = currentEle.getAttribute('data-status'); const currentDataDesc = currentEle.getAttribute('data-desc'); if (currentBtnClass === 'change-status-btn') { // 更改状态按钮 db.transaction((tx) => { const newStatus = currentDataStatus == 0 ? 1 : 0; tx.executeSql(`UPDATE TODOLIST SET status = ? WHERE id = ?`, [newStatus, currentDataId]); }); } else if (currentBtnClass === 'delete-btn') { // 删除按钮 db.transaction((tx) => { tx.executeSql('DELETE FROM TODOLIST WHERE id = ?', [currentDataId]); }); } else if (currentBtnClass === 'edit-btn') { // 修改 // 更改currentEditId的值, 告诉确认按钮这是编辑; currentEditId = currentDataId; // 给输入框赋初始值 todoTextEle.value = currentDataDesc; // 显示 modal modalEle.style.top = '0'; } // 重新渲染列表 refreshTodoList(); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #form-box { position: fixed; top: -100vh; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.6); display: flex; justify-content: center; align-items: center; } </style> </head> <body> <button id="add-btn">新增代办事项</button> <!-- 展示给用户看的表格 --> <table border="1"> <thead> <tr> <td>id</td> <td>待办事项</td> <td>状态</td> <td>操作</td> </tr> </thead> <tbody id="list"></tbody> </table> <!-- 新增和编辑用的表单Modal --> <div id="form-box"> <form> <label for="todo-text"> 代办事项: <input id="todo-text" type="text" /> </label> <span id="confirm-btn">确认</span> <span id="cancel-btn">取消</span> </form> </div> <script> // --------------- 公共部分start: --------------- // 当前修改的数据项id let currentEditId = null; // 创建一个数据库对象 const db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); // 获取 modal 元素 const modalEle = document.querySelector('#form-box'); // 新增和编辑的desc输入框元素 const todoTextEle = document.querySelector('#todo-text'); // 获取 table body const listEle = document.querySelector('#list'); // 控制 modal 的显示隐藏,传true则显示,false则隐藏 function changeModalVisibility(visibility) { modalEle.style.top = visibility ? 0 : '-100vh'; } // 校验表格的内容是否符合要求 function validateForm() { if (!todoTextEle.value) { // 做一个简单的非空处理 alert('代办事项不能为空!'); return false; } return true; } // 获取数据并重绘table body function refreshTodoList() { db.transaction((tx) => { tx.executeSql('SELECT * FROM TODOLIST', [], (tx, res) => { // 因为 res.rows 是一个伪数组,所以我们先给他变成数组再进行遍历,当然你先获取他的length然后写for循环也可以 const resInnerHtml = [...res.rows].reduce((res, ite) => { return res += ` <tr> <td>${ite.id}</td> <td>${ite.desc}</td> <td>${ite.status == 0 ? '未完成' : '完成'}</td> <td> <button class="change-status-btn" data-id="${ite.id}" data-status="${ite.status}">更改状态</button> <button class="edit-btn" data-id="${ite.id}" data-desc="${ite.desc}">修改</button> <button class="delete-btn" data-id="${ite.id}">删除</button> </td> </tr> `; }, ''); listEle.innerHTML = resInnerHtml; }); }); } // 重置输入框的值以及currentEditId function resetVal() { currentEditId = null; todoTextEle.value = ''; } // --------------- 公共部分 end --------------- // --------------- 功能1:点击新增代办事项的时候出现modal --------------- document.querySelector('#add-btn').onclick = () => changeModalVisibility(true); // 点击取消的时候关闭 modal 并且 重置输入框的值以及currentEditId document.querySelector('#cancel-btn').onclick = () => { changeModalVisibility(false); resetVal(); }; // --------------- 功能2:新增与编辑代办事项 --------------- // 点击 确认按钮的时候 将数据插入到表里并且将弹窗关闭 document.querySelector('#confirm-btn').onclick = () => { db.transaction((tx) => { // 执行事务 // 如果不存在的话就创建一个 TODOLIST 表 tx.executeSql('CREATE TABLE IF NOT EXISTS TODOLIST (id unique, desc, status)'); if (currentEditId) { // 有 currentEditId 的话就是编辑 if (validateForm()) { // 校验表格 // 更改当前编辑项的desc 和 将status变为0 tx.executeSql(`UPDATE TODOLIST SET status = 0, desc = ? WHERE id = ?`, [todoTextEle.value, currentEditId]); resetVal(); } } else { // 新增 if (validateForm()) { // 校验表格 // 查询 TODOLIST 表,找到最后一条的 id tx.executeSql('SELECT * FROM TODOLIST', [], (selectTx, res) => { // 最后一条记录的id 然后 + 1 const currentId = ((res.rows[res.rows.length - 1] && res.rows[res.rows.length - 1].id) || 0) + 1; // 对 TODOLIST 表执行插入数据的操作 tx.executeSql(`INSERT INTO TODOLIST (id, desc, status) VALUES (?, ?, 0)`, [currentId, todoTextEle.value]); tresetVal() }); } } changeModalVisibility(false); // 将 modal 隐藏 refreshTodoList(); // 从新渲染列表 }); } // --------------- 功能3:渲染代办事项列表 --------------- refreshTodoList(); // --------------- 功能4:修改代办事项【包括更改描述以及更改状态】+ 删除代办事项 --------------- // 需要使用事件委派.我们这里选择把事件委派到 tbody身上 listEle.onclick = (e) => { const currentEle = e.target; const currentBtnClass = currentEle.getAttribute('class'); const currentDataId = Number(currentEle.getAttribute('data-id')); const currentDataStatus = currentEle.getAttribute('data-status'); const currentDataDesc = currentEle.getAttribute('data-desc'); if (currentBtnClass === 'change-status-btn') { // 更改状态按钮 const newStatus = currentDataStatus == 0 ? 1 : 0; db.transaction((tx) => tx.executeSql(`UPDATE TODOLIST SET status = ? WHERE id = ?`, [newStatus, currentDataId])); } else if (currentBtnClass === 'delete-btn') { // 删除按钮 db.transaction((tx) => tx.executeSql('DELETE FROM TODOLIST WHERE id = ?', [currentDataId])); } else if (currentBtnClass === 'edit-btn') { // 修改 // 更改currentEditId的值, 告诉确认按钮这是编辑; currentEditId = currentDataId; // 给输入框赋初始值 todoTextEle.value = currentDataDesc; changeModalVisibility(true); // 显示 modal return ; // 阻止它去重绘表格 } if (['change-status-btn', 'delete-btn'].includes(currentBtnClass)) { // 只有删除和更改状态才重新渲染列表 refreshTodoList(); } } </script> </body> </html>
这个东西笔者个人感觉还是很牛逼的,直接在浏览器开辟数据库、表进行开发。
不过和正常项目的数据库的区别还是蛮大的。
比如:
这个技术笔者感觉可以在项目中作为一个渐进增强的功能【如果浏览器支持,那么我们这个功能就会使用户更爽,如果浏览器不支持,那么也应该不会影响到用户的操作】。