感谢内容提供者:金牛区吴迪软件开发工作室
文章目录
前言:
学习此内容需要事先掌握基本的 html、css、js 以及简单的 SQL。
SQL的学习:SQL与数据库的基本操作
一、初步了解 Web SQL的使用
1. 创建数据库
/**
* 打开一个数据库,没有的话会进行创建,然后返回一个数据库对象进行操作
* openDatabase的参数介绍:
* 第一个参数:数据库名称
* 第二个参数:版本号
* 第三个参数:描述文本
* 第四个参数:数据库大小
* 第五个参数:创建回调
*/
const db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);
2. 创建表并向表里新增数据
// 执行事务【事务是后端的一个概念,有兴趣了解的同学可以自行百度下】
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()}")`);
});
3. 查询数据
// 执行事务
db.transaction((tx) => {
/**
* executeSql 执行sql
* executeSql的参数介绍
* 第一个参数:要执行的sql
* 第二个参数:动态参数,可以给第一参数的sql使用
* 第三个参数:回调函数,接收俩个参数 tx: 数据库对象,res,执行完sql的response
*/
tx.executeSql('SELECT * FROM USERS', [], (tx, res) => {
// 通过输出表格的方式,直接输出这个表的内容
console.table(res.rows);
});
})
4. 修改数据
db.transaction((tx) => {
tx.executeSql('UPDATE USERS SET age = 24 WHERE id = 1');
});
这样我们就把 USERS 表里的 id = 1的那条数据的age变成了24:
5. 删除数据
db.transaction((tx) => {
tx.executeSql('DELETE FROM USERS WHERE id = 2');
})
这样我们就把 USERS 表里的 id = 2 的那条数据删掉了:
二、做一个 Todo List
1. 创建一个简单的html页面
其实做一个 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
2. 写js逻辑
首先我们了解一下我们的功能点:
- 功能1:点击新增代办事项的时候出现modal
- 功能2:新增代办事项
- 功能3:渲染代办事项列表
- 功能4:修改代办事项【包括更改描述以及更改状态】+ 删除代办事项
① 功能1:点击新增代办事项的时候出现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();
}
3. 功能集成并做代码优化【html + css + js全部代码】
<!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>
三、兼容性调查
四、笔者个人想法
这个东西笔者个人感觉还是很牛逼的,直接在浏览器开辟数据库、表进行开发。
不过和正常项目的数据库的区别还是蛮大的。
比如:
- 无法约定数据类型【可能只是笔者没找到约束的方法】
- 数据无法共享【这个是肯定的。。。因为这个只是搞到你的本地了】
- 兼容问题【这个本地数据库是依赖浏览器是否支持的,服务器上的数据库不需要考虑兼容性问题】
- 应该还有很多SQL不支持,由于时间有限笔者没做调研,欢迎大家一起做调查然后搞到评论区我们一起学习【比如View,left join等等】
这个技术笔者感觉可以在项目中作为一个渐进增强的功能【如果浏览器支持,那么我们这个功能就会使用户更爽,如果浏览器不支持,那么也应该不会影响到用户的操作】。