HTML5新特性 之 Web SQL

感谢内容提供者:金牛区吴迪软件开发工作室

前言:

学习此内容需要事先掌握基本的 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>

三、兼容性调查

在这里插入图片描述

四、笔者个人想法

这个东西笔者个人感觉还是很牛逼的,直接在浏览器开辟数据库、表进行开发。

不过和正常项目的数据库的区别还是蛮大的。
比如:

  1. 无法约定数据类型【可能只是笔者没找到约束的方法】
  2. 数据无法共享【这个是肯定的。。。因为这个只是搞到你的本地了】
  3. 兼容问题【这个本地数据库是依赖浏览器是否支持的,服务器上的数据库不需要考虑兼容性问题】
  4. 应该还有很多SQL不支持,由于时间有限笔者没做调研,欢迎大家一起做调查然后搞到评论区我们一起学习【比如View,left join等等】

这个技术笔者感觉可以在项目中作为一个渐进增强的功能【如果浏览器支持,那么我们这个功能就会使用户更爽,如果浏览器不支持,那么也应该不会影响到用户的操作】。

猜你喜欢

转载自blog.csdn.net/weixin_43606158/article/details/121564984