HTML5 学习总结(三)——本地存储(localStorage、sessionStorage、WebSqlDataBase、IndexedDB)
HTML5问世以后,前端加入了一个重要的功能,便是本地存储,本地存储可分为4类:
Local Storage:总的存储量有所限制,并不能提供真正的检索API,数据的生命期比窗口或浏览器的生命期长,数据可被同源的每个窗口或者标签页共享,兼容性很好,使用最多的存储方式。
Session Storage:只要浏览器窗口不关闭就会一直存在,不应该把真正有价值的东西放在里面,数据会保存到存储它的窗口或者标签页关闭时,数据只在构建他们的窗口或者标签页可见
Indexed Database:在Indexed Database中,objectstore代替了传统的表的概念,每个objectstore相当于一个key和value的集合,IndexedDB并不像传统的如SQL Server那样需要额外安装。Indexed是存在于浏览器端的并且能被用户所访问控制。是保存不敏感用户数据的最佳方案,也可以用于创建本地应用,NOSql。
Web SQL Database:实际上未包含在HTML5规范中。和Indexed Database都是在客户端存储大量结构化数据的解决方案。web sql database实现了传统的基于sql语句的数据库操作,而indexed database实现了nosql的存储方式,关系数据库。
Indexed Database通过直接执行同步或者异步的函数调用来检索树状的对象存储引擎。索引数据库API避开了查询字符串,它使用的底层API支持将值直接存储在javascript对象中。存储在数据库中的值可以通过键或使用索引获取到,并且可以使用同步或异步方式访问API。索引数据库也限定在同源范围内。
一、HTML4客户端存储
B/S架构的应用大量的信息存储在服务器端,客户端通过请求响应的方式从服务器获得数据,这样集中存储也会给服务器带来相应的压力,有些数据可以直接存储在客户端,传统的Web技术中会使用Cookie,但Cookie有一些缺点,为了说明这个缺点我们先看看当提交表单时会有那些信息会被浏览器收集后发送到服务器。
Cookies 客户端 独享 4K
Session 服务端 独享 少量
Application 服务端 共享 小量
1.1、提交表单发送到服务器的信息
1)、带name的可用表单元素
2)、url
3)、客户端请求头部信息
4)、cookie
5)、客户端请求正文信息(有些请求没有正文)
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <% //定义一个cookie对象 Cookie cookie=new Cookie("color", "blue"); //设置过期时间为365小时,以秒为单位 cookie.setMaxAge(60*60*365); //添加cookie response.addCookie(cookie); %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title>提交表单测试</title> </head> <body> <form action="target.do?id=1" method="post"> <h2>提交表单测试</h2> <p> 名称:<input type="text" name="txtName" id="txtName1" /> </p> <p> 价格:<input type="text" name="txtPrice" id="txtPrice1" value="888" readonly="readonly"/> </p> <p> 数量:<input type="text" name="txtAmount" id="txtAmount1" value="999" disabled="disabled"/> </p> <input type="hidden" id="key" value="123445"> <input type="submit" value="提交" /> </form> </body> </html>
运行结果:
服务器在响应头部中声明要求客户端浏览器指定设置cookie color=blue的工作,且指定了过期时间,会将cookie信息记录在本地,查看结果如下:
当提交信息给服务器时cookie将收集后返回服务器,同时也会将url、带name可用的表单及请求头部信息如user-agent等,结果如下:
1.2、客户端本地存储概要
顾名思义客户端本地存储就是将信息存储在客户端电脑上,cookie就是一种典型的传统客户端存储,长期以来本地存储能力一直是桌面应用区别于Web应用的一个主要优势,作为Web应用程序而言,新一代的HTML标准对数据的本地存储提出了更高的要求。传统的Web数据存储方式一直来使用的是Cookie,但Cookie有以下缺陷:
a)、cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
b)、由于在HTTP请求中的cookie是明文传递的,所以安全性成问题。
c)、Cookie的大小限制在4 KB左右,容量达不到要求。
HTML5中的Web Storage,称为Web本地存储,在Web客户端储存数据的功能,用键值对的形式保存数据,曾经属于HTML5的规范,目前已经被独立出来形成单独的规范体系。本地存储优势:
a)、统一的标准,兼容性高(IE8、各大浏览器支持)
b)、数据存储量大
c)、无需安装插件
d)、减少网络流量
e)、更加适合移动端
HTML5 提供了四种在客户端存储数据的新方法,即localStorage 、sessionStorage、globalStorage、Web Sql Database。 前面三个适用于存储较少的数据,而Web Sql Database适用于存储大型的,复杂的数据,我习惯把前面的三个称之为小存储。 IE8、Firefox3.6、Chrome5、Safari4、Opera10,事实证明各个浏览器在API方面的实现基本上一致,存在一定的兼容性问题,但不影响正常使用。
在chrome浏览器中可以使用开发者工具查看到各种不同的本地存储方式,如下图所示:
Web SQL Database 和 Indexed Database 都是在客户端存储大量结构化数据的解决方案。Web SQL Database 实现了传统的基于 SQL 语句的数据库操作,而 Indexed Database 实现了 NoSQL 的存储方式。
Web Storage 这种用于存储 (key, value),一般两者都是字符串;
IndexDB 是增强型的 Web Storage,也是存储 (key, value);
Web SQL 则是 SQLite,一个完整的关系型数据库,可以执行 SQL。
WebSQL是SQLite在浏览器中的实现,所以它是一种关系型数据库。由于W3C对其规范定义不够理想,各家浏览器有各自实现,有浏览器兼容问题;
IndexDB是一种key-value类型的非关系数据库(NoSQL)
1.3、客户端操作Cookies
1.3.1、获得操作cookies的库
https://github.com/js-cookie/js-cookie
官方帮助说明:
A simple, lightweight JavaScript API for handling cookies Works in all browsers Accepts any character Heavily tested No dependency Unobtrusive JSON support Supports AMD/CommonJS RFC 6265 compliant Useful Wiki Enable custom encoding/decoding ~900 bytes gzipped! If you're viewing this at https://github.com/js-cookie/js-cookie, you're reading the documentation for the master branch. View documentation for the latest release. Build Status Matrix (including active Pull Requests) Selenium Test Status Installation Direct download Download the script here and include it (unless you are packaging scripts somehow else): <script src="/path/to/js.cookie.js"></script> Or include it via jsDelivr CDN: <script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script> Do not include the script directly from GitHub (http://raw.github.com/...). The file is being served as text/plain and as such being blocked in Internet Explorer on Windows 7 for instance (because of the wrong MIME type). Bottom line: GitHub is not a CDN. Package Managers JavaScript Cookie supports npm and Bower under the name js-cookie. NPM $ npm install js-cookie --save Module Loaders JavaScript Cookie can also be loaded as an AMD or CommonJS module. Basic Usage Create a cookie, valid across the entire site: Cookies.set('name', 'value'); Create a cookie that expires 7 days from now, valid across the entire site: Cookies.set('name', 'value', { expires: 7 }); Create an expiring cookie, valid to the path of the current page: Cookies.set('name', 'value', { expires: 7, path: '' }); Read cookie: Cookies.get('name'); // => 'value' Cookies.get('nothing'); // => undefined Read all visible cookies: Cookies.get(); // => { name: 'value' } Note: It is not possible to read a particular cookie by passing one of the cookie attributes (which may or may not have been used when writing the cookie in question): Cookies.get('foo', { domain: 'sub.example.com' }); // `domain` won't have any effect...! The cookie with the name foo will only be available on .get() if it's visible from where the code is called; the domain and/or path attribute will not have an effect when reading. Delete cookie: Cookies.remove('name'); Delete a cookie valid to the path of the current page: Cookies.set('name', 'value', { path: '' }); Cookies.remove('name'); // fail! Cookies.remove('name', { path: '' }); // removed! IMPORTANT! When deleting a cookie, you must pass the exact same path and domain attributes that were used to set the cookie, unless you're relying on the default attributes. Note: Removing a nonexistent cookie does not raise any exception nor return any value. Namespace conflicts If there is any danger of a conflict with the namespace Cookies, the noConflict method will allow you to define a new namespace and preserve the original one. This is especially useful when running the script on third party sites e.g. as part of a widget or SDK. // Assign the js-cookie api to a different variable and restore the original "window.Cookies" var Cookies2 = Cookies.noConflict(); Cookies2.set('name', 'value'); Note: The .noConflict method is not necessary when using AMD or CommonJS, thus it is not exposed in those environments. JSON js-cookie provides unobtrusive JSON storage for cookies. When creating a cookie you can pass an Array or Object Literal instead of a string in the value. If you do so, js-cookie will store the string representation of the object according to JSON.stringify: Cookies.set('name', { foo: 'bar' }); When reading a cookie with the default Cookies.get api, you receive the string representation stored in the cookie: Cookies.get('name'); // => '{"foo":"bar"}' Cookies.get(); // => { name: '{"foo":"bar"}' } When reading a cookie with the Cookies.getJSON api, you receive the parsed representation of the string stored in the cookie according to JSON.parse: Cookies.getJSON('name'); // => { foo: 'bar' } Cookies.getJSON(); // => { name: { foo: 'bar' } } Note: To support IE6-7 (and IE 8 compatibility mode) you need to include the JSON-js polyfill: https://github.com/douglascrockford/JSON-js Encoding This project is RFC 6265 compliant. All special characters that are not allowed in the cookie-name or cookie-value are encoded with each one's UTF-8 Hex equivalent using percent-encoding. The only character in cookie-name or cookie-value that is allowed and still encoded is the percent % character, it is escaped in order to interpret percent input as literal. Please note that the default encoding/decoding strategy is meant to be interoperable only between cookies that are read/written by js-cookie. To override the default encoding/decoding strategy you need to use a converter. Cookie Attributes Cookie attributes defaults can be set globally by setting properties of the Cookies.defaults object or individually for each call to Cookies.set(...) by passing a plain object in the last argument. Per-call attributes override the default attributes. expires Define when the cookie will be removed. Value can be a Number which will be interpreted as days from time of creation or a Date instance. If omitted, the cookie becomes a session cookie. To create a cookie that expires in less than a day, you can check the FAQ on the Wiki. Default: Cookie is removed when the user closes the browser. Examples: Cookies.set('name', 'value', { expires: 365 }); Cookies.get('name'); // => 'value' Cookies.remove('name'); path A String indicating the path where the cookie is visible. Default: / Examples: Cookies.set('name', 'value', { path: '' }); Cookies.get('name'); // => 'value' Cookies.remove('name', { path: '' }); Note regarding Internet Explorer: Due to an obscure bug in the underlying WinINET InternetGetCookie implementation, IE’s document.cookie will not return a cookie if it was set with a path attribute containing a filename. (From Internet Explorer Cookie Internals (FAQ)) This means one cannot set a path using window.location.pathname in case such pathname contains a filename like so: /check.html (or at least, such cookie cannot be read correctly). In fact, you should never allow untrusted input to set the cookie attributes or you might be exposed to a XSS attack. domain A String indicating a valid domain where the cookie should be visible. The cookie will also be visible to all subdomains. Default: Cookie is visible only to the domain or subdomain of the page where the cookie was created, except for Internet Explorer (see below). Examples: Assuming a cookie that is being created on site.com: Cookies.set('name', 'value', { domain: 'subdomain.site.com' }); Cookies.get('name'); // => undefined (need to read at 'subdomain.site.com') Note regarding Internet Explorer default behavior: Q3: If I don’t specify a DOMAIN attribute (for) a cookie, IE sends it to all nested subdomains anyway? A: Yes, a cookie set on example.com will be sent to sub2.sub1.example.com. Internet Explorer differs from other browsers in this regard. (From Internet Explorer Cookie Internals (FAQ)) This means that if you omit the domain attribute, it will be visible for a subdomain in IE. secure Either true or false, indicating if the cookie transmission requires a secure protocol (https). Default: No secure protocol requirement. Examples: Cookies.set('name', 'value', { secure: true }); Cookies.get('name'); // => 'value' Cookies.remove('name'); Converters Read Create a new instance of the api that overrides the default decoding implementation. All get methods that rely in a proper decoding to work, such as Cookies.get() and Cookies.get('name'), will run the converter first for each cookie. The returning String will be used as the cookie value. Example from reading one of the cookies that can only be decoded using the escape function: document.cookie = 'escaped=%u5317'; document.cookie = 'default=%E5%8C%97'; var cookies = Cookies.withConverter(function (value, name) { if ( name === 'escaped' ) { return unescape(value); } }); cookies.get('escaped'); // 北 cookies.get('default'); // 北 cookies.get(); // { escaped: '北', default: '北' } Write Create a new instance of the api that overrides the default encoding implementation: Cookies.withConverter({ read: function (value, name) { // Read converter }, write: function (value, name) { // Write converter } }); Server-side integration Check out the Servers Docs Contributing Check out the Contributing Guidelines Security For vulnerability reports, send an e-mail to jscookieproject at gmail dot com Manual release steps Increment the "version" attribute of package.json Increment the version number in the src/js.cookie.js file If major bump, update jsDelivr CDN major version link on README Commit with the message "Release version x.x.x" Create version tag in git Create a github release and upload the minified file Change the latest tag pointer to the latest commit git tag -f latest git push <remote> :refs/tags/latest git push origin master --tags Release on npm Authors Klaus Hartl Fagner Brack And awesome contributors
1.3.2、操作cookies
1、写
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>客户端读写Cookie</title> </head> <body> <button type="button" onclick="writeCookie();">写</button> <button type="button" onclick="readCookie();">读</button> <button type="button" onclick="removeCookie();">清除</button> <script src="js.cookie-2.2.0.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> function writeCookie(){ //在当前域下写入cookie Cookies.set('price', '998', { expires: 7 }); } </script> </body> </html>
运行结果:
2、读
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>客户端读写Cookie</title> </head> <body> <button type="button" onclick="writeCookie();">写</button> <button type="button" onclick="readCookie();">读</button> <button type="button" onclick="removeCookie();">清除</button> <script src="js.cookie-2.2.0.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> function writeCookie(){ //在当前域下写入cookie Cookies.set('price', '998', { expires: 7 }); } function readCookie(){ //在当前域根据key读取cookie var price=Cookies.get("price"); alert("价格:"+price); } </script> </body> </html>
结果:
3、清除
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>客户端读写Cookie</title> </head> <body> <button type="button" onclick="writeCookie();">写</button> <button type="button" onclick="readCookie();">读</button> <button type="button" onclick="removeCookie();">清除</button> <script src="js.cookie-2.2.0.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> function writeCookie(){ //在当前域下写入cookie Cookies.set('price', '998', { expires: 7 }); //过期时间为7天 Cookies.set('color', 'blue', { expires: 7 }); } function readCookie(){ //在当前域根据key读取cookie var price=Cookies.get("price"); alert("价格:"+price); } function removeCookie(){ //在当前域指定的cookie Cookies.remove("price"); } console.log(document.cookie); </script> </body> </html>
结果:
二、localStorage
localStorage:将数据保存在客户端本地的硬件设备(通常指硬盘,但也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。
2.1、添加
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>localStorage 本地存储</title> </head> <body> <h2>localStorage 本地存储</h2> <button onclick="add_click()">添加</button> <script type="text/javascript"> function add_click() { //向本地存储中添加一个名为name,值为"手机"的key-value对象 localStorage.setItem("name","手机"); //办法2 localStorage["price"]=999.5; //办法3 localStorage.amount=1788; } </script> </body> </html>
运行结果:
2.2、取值
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>localStorage 本地存储</title> </head> <body> <h2>localStorage 本地存储</h2> <button onclick="add_click()">添加</button> <button onclick="get_click()">取值</button> <h3 id="msg"></h3> <a href="d02.html">d02.html</a> <script type="text/javascript"> var msg=document.getElementById("msg"); //添加 function add_click() { //向本地存储中添加一个名为name,值为"手机"的key-value对象 localStorage.setItem("name","手机"); //办法2 localStorage["price"]=999.5; //办法3 localStorage.amount=1788; } //取值 function get_click() { msg.innerHTML+=localStorage.getItem("name")+"<br/>"; msg.innerHTML+=localStorage["price"]+"<br/>"; msg.innerHTML+=localStorage.amount+"<br/>"; } </script> </body> </html>
运行结果:
2.3、修改
//修改 function update_click() { //如果不存在就添加,如果存在就修改 localStorage.setItem("name","iPhone 8 plus手机"); //修改办法2 localStorage["price"]=899.5; //修改办法3 localStorage.amount=100; }
运行结果:
2.4、删除
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>localStorage 本地存储</title> </head> <body> <h2>localStorage 本地存储</h2> <button onclick="add_click()">添加</button> <button onclick="get_click()">取值</button> <button onclick="update_click()">修改</button> <button onclick="delete_click()">删除</button> <h3 id="msg"></h3> <a href="d02.html">d02.html</a> <script type="text/javascript"> var msg=document.getElementById("msg"); //添加 function add_click() { //向本地存储中添加一个名为name,值为"手机"的key-value对象 localStorage.setItem("name","手机"); //添加办法2 localStorage["price"]=999.5; //添加办法3 localStorage.amount=1788; } //取值 function get_click() { msg.innerHTML+=localStorage.getItem("name")+"<br/>"; msg.innerHTML+=localStorage["price"]+"<br/>"; msg.innerHTML+=localStorage.amount+"<br/>"; } //修改 function update_click() { //如果不存在就添加,如果存在就修改 localStorage.setItem("name","iPhone 8 plus手机"); //修改办法2 localStorage["price"]=899.5; //修改办法3 localStorage.amount=100; } //删除 function delete_click() { //根据键删除 //localStorage.removeItem("price"); //删除所有 localStorage.clear(); } </script> </body> </html>
运行结果:
2.5、跨页面与跨域
当关闭浏览器,下次再打开时,值仍然存在。可以跨页面,不能跨域。我们在d01页面中添加了值,在d02页面中仍然可以访问,在整个同域下都可以访问。
2.6、存储位置与SQLite
localStorage与cookie不一样,它存储在一个数据库文件中,默认位置在:C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\databases\http_localhost_*
或者
C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Profile 1\Local Storage
Administrator是当前登录用户名
使用SQLite数据库管理工具,打开后看到的结果,这里以taobao存储客户端的localStorage为例:
提示:SQLite,是一款轻型的免费开源的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来讲,它的处理速度比他们都快。SQLite第一个Alpha版本诞生于2000年5月。 至2015年已经有15个年头,SQLite也迎来了一个版本 SQLite 3已经发布。
SQLiteSpy管理工具下载: http://pan.baidu.com/s/1i5JQtBf
2.7、用途、练习与兼容性
所有需要将少量(不超过4M)数据存储在客户端的需求都适用,如密码,用户偏好(profile)等
登录成功后记录用户访问次数。
在IE8中测试通过
Firefox测试通过
三、sessionStorage
将数据临时保存在客户端session对象中。session对象就是会话对象,session中存储的数据独立于每个客户,该数据会随着浏览器的关闭而消失。
sessionStorage的操作api与localStorage基本一样,在不手动清除的情况下localStorage永久保存,而sessionStorage只是临时暂存。
3.1、sessionStorage使用
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>sessionStorage 本地存储</title> </head> <body> <h2>sessionStorage 本地存储</h2> <button onclick="add_click()">添加</button> <button onclick="get_click()">取值</button> <button onclick="update_click()">修改</button> <button onclick="delete_click()">删除</button> <h3 id="msg"></h3> <a href="d04.html">d04.html</a> <script type="text/javascript"> var msg=document.getElementById("msg"); //添加 function add_click() { //向本地存储中添加一个名为name,值为"手机"的key-value对象 sessionStorage.setItem("name","手机"); //添加办法2 sessionStorage["price"]=999.5; //添加办法3 sessionStorage.amount=1788; } //取值 function get_click() { msg.innerHTML+=sessionStorage.getItem("name")+"<br/>"; msg.innerHTML+=sessionStorage["price"]+"<br/>"; msg.innerHTML+=sessionStorage.amount+"<br/>"; } //修改 function update_click() { //如果不存在就添加,如果存在就修改 sessionStorage.setItem("name","iPhone 8 plus手机"); //修改办法2 sessionStorage["price"]=899.5; //修改办法3 sessionStorage.amount=100; } //删除 function delete_click() { //根据键删除 //sessionStorage.removeItem("price"); //删除所有 sessionStorage.clear(); } </script> </body> </html>
运行结果:
可以实现在页面间传值,比如可以临时存储用户信息。
3.2、Web本地存储事件监听
当程序修改localStorage与sessionStorage时将触发全局事件。
当setItem(),removeItem()或者clear() 方法被调用,并且数据真的发生了改变时,就会触发storage事件,如果需要进行监听数据处理,通过以下方法:
window.addEventListener(event,handleEvent, capture)
event:设置成storage
handleEvent:事件处理函数
capture:事件处理顺序,一般设置成false,表示采用冒泡方式处理
handleEvent处理事件的函数会接收到一个StorageEvent对象,该对象有以下属性:
key:被修改的键。
oldValue:修改前的值(如果是增加新的键值,则该属性为null)
newValue:修改后的值(如果是删除键值,则该属性为null)
url/uri:触发当前存储事件的页面的url
注意:storage改变的时候,触发这个事件会调用所有同域下其他窗口的storage事件,不过它本身触发storage即当前窗口是不会触发这个事件的(当然ie这个特例除外,它包含自己本事也会触发storage事件)
修改d02页面,监听值的变化。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>获得localStorage的值</title> </head> <body> <script type="text/javascript"> console.log(localStorage.price); window.addEventListener("storage",function(obj){ alert(obj.oldValue+","+obj.newValue+","+obj.url); },true); </script> </body> </html>
运行结果如下:
StorageEvent对象
//StorageEvent对象 { bubbles: false, cancelBubble: false, cancelable: false, composed: false, currentTarget: {...}, //当前Window对象 defaultPrevented: false, eventPhase: 0, isTrusted: true, key: "tabs",//更新的item键名 newValue: "1",//更新后的item键值 oldValue: "2",//更新前的item键值 path: [Window],//一个数组,数组中有一个元素,为当前Window对象 returnValue: true, srcElement: {...}, //当前Window对象 storageArea: {...}, //Storage对象 target:{...}, //当前Window对象 timeStamp: 101647.2000000067, type: "storage", url: "http://www.localhost.com/list.html",//更新localStorage的页面 __proto__: StorageEvent }
3.3、cookie、sessionStorage、localStorage比较
四、Web SQL Database
Web SQL Database 引入了一套使用 SQL 来操纵客户端数据库(client-side database)的 API,这些 API 是异步的(asynchronous),规范中所使用的 SQL 语言为 SQLite。Web SQL Database API 实际上未包含在 HTML 5 规范之中,它是一个独立的规范,它引入了一套使用 SQL 操作客户端数据库的 API,这些 API 有同步的,也有异步的, 同步版本的 API 只在工作线程(Worker Threads)上有用,由于并不是所有的浏览器都支持工作线程,一般情况下,都会使用异步 API。兼容情况如下:
Web SQL Database可以让开发人员使用SQL语句操作客户端浏览器中嵌入的SQLite数据库 ,给开发人员提供了方便。对于简单的数据,使用sessionStorage和localStorage能够很好地完成存取,但是对于处理复杂的关系型数据,它就力不从心了。这也是 HTML 5 的“Web SQLDatabase”API 接口的应用所在。我把它理解成一个Html5环境下可以用Js执行CRUD的Web数据库
三个核心方法
openDatabase:这个方法使用现有数据库或创建新数据库创建数据库对象。
transaction:这个方法允许我们根据情况控制事务提交或回滚。
executeSql:这个方法用于执行真实的SQL查询。
4.1、创建数据库
使用openDatabase创建或打开数据库,如果存在就打开,如果不存在就创建,语法如下:
openDatabase(a,b,c,d,e);
a).数据库名称。
b).版本号 目前为1.0
c).对数据库的描述
d).设置数据的大小,以Byte为单位
e).回调函数(可省略)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //数据库 var db; //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 db=openDatabase("products",1.0,"产品数据库",1024*1024*3,function(){ log("创建或打开数据库完成"); }); //显示消息 function log(info){ $("#msg")[0].innerHTML+=info+"<br/>"; } </script> </body> </html>
运行结果:
4.2、创建表
执行所有的SQL语句都将使用到transaction、executeSql两个方法,基本语法格式如下:
数据库对象.transaction(function(tx) { tx.executeSql( "要执行的sql", [参数], function(tx, result) { alert('成功时的回调方法'); }, function(tx, error) { alert('失败时的回调方法' + error.message); }); });
页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button onclick="createTable()">创建表</button> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //创建或打开名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 var db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { log("创建数据库完成"); }); //创建表 function createTable() { db.transaction(function(tx) { tx.executeSql( "create table if not exists goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { log('创建表成功'); }, function(tx, error) { log('创建表失败' + error.message); }); }); } //显示消息 function log(info) { $("#msg")[0].innerHTML += info + "<br/>"; } </script> </body> </html>
运行结果:
当创建成功时,可以发现在出现了两个表,其中名为sqlite_sequence为自动增长用的序列。
4.3、添加数据
页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button onclick="createTable()">创建表</button> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <button onclick="insert()">添加</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 var db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { log("创建或打开数据库完成"); }); //创建表 function createTable() { db.transaction(function(tx) { tx.executeSql( "create table if not exists goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { log('创建表成功'); }, function(tx, error) { log('创建表失败' + error.message); }); }); } //插入数据 function insert() { db.transaction(function(tx) { tx.executeSql( "insert into goods(name,price) values(?,?)", [$("#name").val(),$("#price").val()], function(tx, result) { log('添加数据成功'); }, function(tx, error) { log('添加数据失败' + error.message); }); }); } //显示消息 function log(info) { $("#msg")[0].innerHTML += info + "<br/>"; } </script> </body> </html>
运行结果:
4.4、展示数据
页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button onclick="createTable()">创建表</button> <table border="1" width="80%" id="tabGoods"> <tr><th>编号</th><th>名称</th><th>价格</th><th>删除</th></tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <button onclick="insert()">添加</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 var db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { log("创建或打开数据库完成"); }); //展示,加载数据 function select() { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods", [], function(tx, result) { for (var i=0;i<result.rows.length;i++) { var tr=$("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del=$("<a href='#' onclick='del("+result.rows.item(i)["id"]+")' >删除</a>") $("<td/>").append(del).appendTo(tr); tr.appendTo("#tabGoods"); } }, function(tx, error) { log('创建表失败' + error.message); }); }); } select(); //创建表 function createTable() { db.transaction(function(tx) { tx.executeSql( "create table if not exists goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { log('创建表成功'); }, function(tx, error) { log('创建表失败' + error.message); }); }); } //插入数据 function insert() { db.transaction(function(tx) { tx.executeSql( "insert into goods(name,price) values(?,?)", [$("#name").val(),$("#price").val()], function(tx, result) { log('添加数据成功'); select(); }, function(tx, error) { log('添加数据失败' + error.message); }); }); } //显示消息 function log(info) { $("#msg")[0].innerHTML += info + "<br/>"; } </script> </body> </html>
运行结果如下:
4.5、编辑数据
页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button onclick="createTable()">创建表</button> <table border="1" width="80%" id="tabGoods"> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <input type="hidden" id="goodsId" /> <button onclick="insert()">添加</button> <button onclick="update()">更新</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 var db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { log("创建或打开数据库完成"); }); //展示,加载数据 function select() { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods", [], function(tx, result) { for(var i = 0; i < result.rows.length; i++) { var tr = $("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del = $("<a href='#' onclick='del(" + result.rows.item(i)["id"] + ",this)' >删除 | </a>") var edit = $("<a href='#' onclick='edit(" + result.rows.item(i)["id"] + ",this)' >修改</a>") $("<td/>").append(del).append(edit).appendTo(tr); tr.appendTo("#tabGoods"); } }, function(tx, error) { log('创建表失败' + error.message); }); }); } select(); //创建表 function createTable() { db.transaction(function(tx) { tx.executeSql( "create table if not exists goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { log('创建表成功'); }, function(tx, error) { log('创建表失败' + error.message); }); }); } //插入数据 function insert() { db.transaction(function(tx) { tx.executeSql( "insert into goods(name,price) values(?,?)", [$("#name").val(), $("#price").val()], function(tx, result) { log('添加数据成功'); select(); }, function(tx, error) { log('添加数据失败' + error.message); }); }); } //删除 function del(id, link) { db.transaction(function(tx) { tx.executeSql( "delete from goods where id=?", [id], function(tx, result) { log('删除成功'); //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }, function(tx, error) { log('删除失败' + error.message); }); }); } //编辑 function edit(id) { db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods where id=?", [id], function(tx, result) { $("#name").val(result.rows.item(0)["name"]); $("#price").val(result.rows.item(0)["price"]); $("#goodsId").val(result.rows.item(0)["id"]); log("修改后请保存"); }, function(tx, error) { log('编辑失败' + error.message); }); }); } //更新 function update() { if($("#goodsId").val()) { db.transaction(function(tx) { tx.executeSql( "update goods set name=?,price=? where id=?", [$("#name").val(), $("#price").val(), $("#goodsId").val()], function(tx, result) { log('更新成功'); select(); $("#goodsId").val(""); }, function(tx, error) { log('更新失败' + error.message); }); }); } else { log("请选择要更新的记录 "); } } //显示消息 function log(info) { $("#msg")[0].innerHTML += info + "<br/>"; } </script> </body> </html>
运行结果如下:
4.6、删除数据
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button onclick="createTable()">创建表</button> <table border="1" width="80%" id="tabGoods"> <tr><th>编号</th><th>名称</th><th>价格</th><th>删除</th></tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <button onclick="insert()">添加</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 var db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { log("创建或打开数据库完成"); }); //展示,加载数据 function select() { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods", [], function(tx, result) { for (var i=0;i<result.rows.length;i++) { var tr=$("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del=$("<a href='#' onclick='del("+result.rows.item(i)["id"]+",this)' >删除</a>") $("<td/>").append(del).appendTo(tr); tr.appendTo("#tabGoods"); } }, function(tx, error) { log('创建表失败' + error.message); }); }); } select(); //创建表 function createTable() { db.transaction(function(tx) { tx.executeSql( "create table if not exists goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { log('创建表成功'); }, function(tx, error) { log('创建表失败' + error.message); }); }); } //插入数据 function insert() { db.transaction(function(tx) { tx.executeSql( "insert into goods(name,price) values(?,?)", [$("#name").val(),$("#price").val()], function(tx, result) { log('添加数据成功'); select(); }, function(tx, error) { log('添加数据失败' + error.message); }); }); } //删除 function del(id,link) { db.transaction(function(tx) { tx.executeSql( "delete from goods where id=?", [id], function(tx, result) { log('删除成功'); //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }, function(tx, error) { log('删除失败' + error.message); }); }); } //显示消息 function log(info) { $("#msg")[0].innerHTML += info + "<br/>"; } </script> </body> </html>
运行结果:
4.7、删除表
删除表
//创建表 function dropTable() { db.transaction(function(tx) { tx.executeSql( "drop table IF EXISTS goods", [], function(tx, result) { log('删除表成功'); }, function(tx, error) { log('删除表失败' + error.message); }); }); }
运行结果:
4.8、数据库位置
D:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\databases
4.9、封装JavaScript
前面的示例中javascript方法都直接暴露在window下,有可能与别的js冲突,可以进行简单封装。
简单对象封装示例:
var ticker={ n:0, add:function() { this.n++; }, show:function() { alert(this.n); } } ticker.add(); ticker.add(); ticker.show();
运行结果:2
回调方法:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script type="text/javascript"> var ticker={ n:0, add:function(){ this.n++; }, show:function(){ alert(ticker.n); } }; ticker.add(); ticker.add(); ticker.show(); function add(a,b){ var sum=a+b; alert(a+"+"+b+"="+sum); } calc(1,2,add); calc(5,8,function(c,d){ alert(c+"-"+d+"="+(c-d)); }); function calc(n1,n2,fun){ if(n1>0&&n2>0&&fun){ fun(n1,n2); }else{ alert("参数缺失"); } } </script> </body> </html>
封装结果:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>WebSqlDataBase</title> </head> <body> <h2>WebSqlDataBase</h2> <button onclick="app.openDb()">创建/打开数据库</button> <button onclick="app.createTable()">创建数据表</button> <button onclick="app.insert()">添加数据</button> <button onclick="app.update()">修改数据</button> <button onclick="app.remove()">删除数据</button> <button onclick="app.select()">查询数据</button> <button onclick="app.dropTable()">删除表</button> <p> 编号:<input type="text" id="id" /> </p> <p> 名称:<input type="text" id="name" /> </p> <p> <h2 id="h2Msg"></h2> </p> <script type="text/javascript"> var app = { db: {}, data: { id: document.querySelector("#id").value, name: document.querySelector("#name").value }, init: function() { window.log = function(msg) { console.log(msg); document.getElementById("h2Msg").innerHTML += msg + "<br/>"; } app.openDb(); }, openDb: function() { //创建或打开数据库 app.db = openDatabase("gomallDb", "1.0", "天狗商城", 1024 * 1024 * 3, function() { log("创建或打开数据库成功"); }); }, executeSql: function(sql, params, success, error) { if(app.db) { app.db.transaction(function(tx) { tx.executeSql(sql, params || [], function(tx, result) { log("执行成功!"); if(success) { success(tx, result); } }, function(tx, result) { log("执行失败!"); if(error) { error(tx, result); } }); }); } else { log("请打开数据库"); } }, createTable: function() { //创建表 var sql = "create table if not exists goods(id integer primary key autoincrement,name text not null);"; app.executeSql(sql); }, insert: function() { //添加数据 app.executeSql("insert into goods(name) values(?)", [app.data.name]); }, update: function() { //修改数据 app.executeSql("update goods set name=? where id=?", [app.data.name, app.data.id]); }, remove: function() { //删除数据 app.executeSql("delete from goods where name like ? or id=?", [app.data.name, app.data.id]); }, select: function() { //查询 app.executeSql("select id,name from goods", [], function(tx, result) { var r = result.rows; for(var i = 0; i < r.length; i++) { log(r[i].id + "," + r[i].name); } }); }, dropTable: function() { //删除表 app.executeSql("drop table if exists goods", [], function() { alert("删除成功!") }, function() { alert("删除失败!"); } ) } }; app.init(); </script> </body> </html>
第一次封装后的代码,在整个window对象中只暴露dbApp对象,代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button id="btnCreateTable">创建表</button> <button id="btnDropTable">删除表</button> <table border="1" width="80%" id="tabGoods"> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <input type="hidden" id="goodsId" /> <button id="btnInsert">添加</button> <button id="btnUpdate">更新</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //定义当前应用的对象 var dbApp={ //打开数据库 openDb:function() { //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 this.db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { this.log("创建或打开数据库完成"); }); }, //初始化 init:function() { //打开或创建数据库 this.openDb(); //绑定事件 this.bindEvent(); //展示数据 this.select(); this.log("初始化完成"); }, //绑定事件 bindEvent:function() { //添加事件 $("#btnInsert").click(this.insert); $("#btnUpdate").click(this.update); $("#btnCreateTable").click(this.createTable); $("#btnDropTable").click(this.dropTable); }, //显示消息 log:function(info) { $("#msg")[0].innerHTML += info + "<br/>"; }, //创建表 createTable:function() { this.db.transaction(function(tx) { tx.executeSql( "create table IF not EXISTS goods(id integer primary key autoincrement,name text not null,price double)", [], function(tx, result) { this.log('创建表成功'); }, function(tx, error) { this.log('创建表失败' + error.message); }); }); }, //删除表 dropTable:function() { this.db.transaction(function(tx) { tx.executeSql( "drop table IF EXISTS goods", [], function(tx, result) { this.log('删除表成功'); }, function(tx, error) { this.log('删除表失败' + error.message); }); }); }, //展示,加载数据 select:function() { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); this.db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods", [], function(tx, result) { for(var i = 0; i < result.rows.length; i++) { var tr = $("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del = $("<a href='#' onclick='dbApp.del(" + result.rows.item(i)["id"] + ",this)' >删除 | </a>") var edit = $("<a href='#' onclick='dbApp.edit(" + result.rows.item(i)["id"] + ",this)' >修改</a>") $("<td/>").append(del).append(edit).appendTo(tr); tr.appendTo("#tabGoods"); } }, function(tx, error) { this.log('创建表失败' + error.message); }); }); }, //插入数据 insert:function() { //如果insert方法被绑定为事件,则this表示事件发生的对象 dbApp.db.transaction(function(tx) { tx.executeSql( "insert into goods(name,price) values(?,?)", [$("#name").val(), $("#price").val()], function(tx, result) { dbApp.log('添加数据成功'); //刷新 dbApp.select(); }, function(tx, error) { dbApp.log('添加数据失败' + error.message); }); }); }, //删除 del:function(id, link) { dbApp.db.transaction(function(tx) { tx.executeSql( "delete from goods where id=?", [id], function(tx, result) { dbApp.log('删除成功'); //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }, function(tx, error) { dbApp.log('删除失败' + error.message); }); }); }, //编辑 edit:function(id) { dbApp.db.transaction(function(tx) { tx.executeSql( "select id,name,price from goods where id=?", [id], function(tx, result) { $("#name").val(result.rows.item(0)["name"]); $("#price").val(result.rows.item(0)["price"]); $("#goodsId").val(result.rows.item(0)["id"]); dbApp.log("修改后请保存"); }, function(tx, error) { dbApp.log('编辑失败' + error.message); }); }); }, //更新 update:function() { if($("#goodsId").val()) { dbApp.db.transaction(function(tx) { tx.executeSql( "update goods set name=?,price=? where id=?", [$("#name").val(), $("#price").val(), $("#goodsId").val()], function(tx, result) { dbApp.log('更新成功'); dbApp.select(); $("#goodsId").val(""); }, function(tx, error) { dbApp.log('更新失败' + error.message); }); }); } else { dbApp.log("请选择要更新的记录 "); } } }; dbApp.init(); </script> </body> </html>
运行结果:
从上面的代码可以发现操作数据库,执行sql的方法存在大量的冗余,可以优化,优化后的代码如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> </head> <body> <h2>Web SQL Database</h2> <div> <button id="btnCreateTable">创建表</button> <button id="btnDropTable">删除表</button> <table border="1" width="80%" id="tabGoods"> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <input type="hidden" id="goodsId" /> <button id="btnInsert">添加</button> <button id="btnUpdate">更新</button> </p> </fieldset> </div> <h2 id="msg"></h2> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //定义当前应用的对象 var dbApp={ //打开数据库 openDb:function() { //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 this.db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { this.log("创建或打开数据库完成"); }); }, //初始化 init:function() { //打开或创建数据库 this.openDb(); //绑定事件 this.bindEvent(); //展示数据 this.select(); this.log("初始化完成"); }, //绑定事件 bindEvent:function() { //添加事件 $("#btnInsert").click(this.insert); $("#btnUpdate").click(this.update); $("#btnCreateTable").click(this.createTable); $("#btnDropTable").click(this.dropTable); }, //显示消息 log:function(info) { $("#msg")[0].innerHTML += info + "<br/>"; }, //执行sql的通用方法 result.rowsAffected 影响行数 //callback执行成功时的回调方法 exeSql:function(sql,title,param,callback) { title=title||"操作"; this.db.transaction(function(tx) { tx.executeSql( sql, param||[], function(tx, result) { dbApp.log(title+'成功'); if(callback){ //如果有参数 callback(result); } }, function(tx, error) { dbApp.log(title+'失败' + error.message); }); }); }, //创建表 createTable:function() { dbApp.exeSql("create table IF not EXISTS goods(id integer primary key autoincrement,name text not null,price double)","创建表"); }, //删除表 dropTable:function() { dbApp.exeSql("drop table IF EXISTS goods","删除表"); }, //展示,加载数据 select:function() { dbApp.exeSql("select id,name,price from goods","查询",[],function(result) { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); for(var i = 0; i < result.rows.length; i++) { var tr = $("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del = $("<a href='#' onclick='dbApp.del(" + result.rows.item(i)["id"] + ",this)' >删除 | </a>") var edit = $("<a href='#' onclick='dbApp.edit(" + result.rows.item(i)["id"] + ",this)' >修改</a>") $("<td/>").append(del).append(edit).appendTo(tr); tr.appendTo("#tabGoods"); } }); }, //插入数据 insert:function() { //如果insert方法被绑定为事件,则this表示事件发生的对象 dbApp.exeSql("insert into goods(name,price) values(?,?)","添加",[$("#name").val(), $("#price").val()],function(){ dbApp.select(); }); }, //删除 del:function(id, link) { dbApp.exeSql("delete from goods where id=?","删除",[id],function(result){ //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }); }, //编辑 edit:function(id) { dbApp.exeSql("select id,name,price from goods where id=?","编辑",[id],function(result) { $("#name").val(result.rows.item(0)["name"]); $("#price").val(result.rows.item(0)["price"]); $("#goodsId").val(result.rows.item(0)["id"]); dbApp.log("修改后请保存"); }); }, //更新 update:function() { if($("#goodsId").val()) { dbApp.exeSql("update goods set name=?,price=? where id=?","更新",[$("#name").val(), $("#price").val(), $("#goodsId").val()],function(result) { dbApp.select(); $("#goodsId").val(""); }); } else { dbApp.log("请选择要更新的记录 "); } } }; dbApp.init(); </script> </body> </html>
运行结果:
4.10、美化页面
在原页面的基础上增加css样式,添加样式后的页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Web SQL Database</title> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <style type="text/css"> * { margin: 0; padding: 0; font-family: "microsoft yahei"; } #container{ padding: 10px; font-size: 14px; } #container a{ color: #fff; text-decoration: none; margin-right: 5px; } #container a:hover{ color:orangered; } button,a{ border: 0; height: 22px; line-height: 22px; border-radius: 3px; padding:0 10px; background: dodgerblue; color: white; } button:hover{ background: orangered; } #container h2{ height: 60px; } html #tabGoods{ width:100%; margin: 15px 0; border: 2px solid #0062CC; } #tabGoods,#tabGoods td,#tabGoods th { border: 1px solid #0062CC; border-collapse: collapse; } #tabGoods td,#tabGoods th{ padding: 5px 0 5px 5px; } #fieldsetForm{ width: 400px; padding: 10px; border-radius: 10px; border: 2px solid #0062CC; } #fieldsetForm p{ padding: 10px; } #msg{ font-size: 16px; font-weight: normal; height: 100px; overflow: auto; margin-top: 10px; } </style> </head> <body> <div id="container"> <h2>Web SQL Database</h2> <button id="btnCreateTable">创建表</button> <button id="btnDropTable">删除表</button> <table id="tabGoods"> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>删除</th> </tr> </table> <fieldset id="fieldsetForm"> <legend>商品信息</legend> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <input type="hidden" id="goodsId" /> <button id="btnInsert">添加</button> <button id="btnUpdate">更新</button> </p> </fieldset> <h2 id="msg"></h2> </div> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //定义当前应用的对象 var dbApp = { //打开数据库 openDb: function() { //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 this.db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { this.log("创建或打开数据库完成"); }); }, //初始化 init: function() { //打开或创建数据库 this.openDb(); //绑定事件 this.bindEvent(); //展示数据 this.select(); this.log("初始化完成"); }, //绑定事件 bindEvent: function() { //添加事件 $("#btnInsert").click(this.insert); $("#btnUpdate").click(this.update); $("#btnCreateTable").click(this.createTable); $("#btnDropTable").click(this.dropTable); }, //显示消息 log: function(info) { $("#msg")[0].innerHTML += info + "<br/>"; }, //执行sql的通用方法 result.rowsAffected 影响行数 //callback执行成功时的回调方法 exeSql: function(sql, title, param, callback) { title = title || "操作"; this.db.transaction(function(tx) { tx.executeSql( sql, param || [], function(tx, result) { dbApp.log(title + '成功'); if(callback) { //如果有参数 callback(result); } }, function(tx, error) { dbApp.log(title + '失败' + error.message); }); }); }, //创建表 createTable: function() { dbApp.exeSql("create table IF not EXISTS goods(id integer primary key autoincrement,name text not null,price double)", "创建表"); }, //删除表 dropTable: function() { dbApp.exeSql("drop table IF EXISTS goods", "删除表"); }, //展示,加载数据 select: function() { dbApp.exeSql("select id,name,price from goods", "查询", [], function(result) { //将表格中tr索引大于0的元素删除 $("#tabGoods tr:gt(0)").remove(); for(var i = 0; i < result.rows.length; i++) { var tr = $("<tr/>"); $("<td/>").text(result.rows.item(i)["id"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["name"]).appendTo(tr); $("<td/>").text(result.rows.item(i)["price"]).appendTo(tr); var del = $("<a href='#' onclick='dbApp.del(" + result.rows.item(i)["id"] + ",this)' >删除</a> ") var edit = $("<a href='#' onclick='dbApp.edit(" + result.rows.item(i)["id"] + ",this)' >修改</a>") $("<td/>").append(del).append(edit).appendTo(tr); tr.appendTo("#tabGoods"); } }); }, //插入数据 insert: function() { //如果insert方法被绑定为事件,则this表示事件发生的对象 dbApp.exeSql("insert into goods(name,price) values(?,?)", "添加", [$("#name").val(), $("#price").val()], function() { dbApp.select(); }); }, //删除 del: function(id, link) { dbApp.exeSql("delete from goods where id=?", "删除", [id], function(result) { //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }); }, //编辑 edit: function(id) { dbApp.exeSql("select id,name,price from goods where id=?", "编辑", [id], function(result) { $("#name").val(result.rows.item(0)["name"]); $("#price").val(result.rows.item(0)["price"]); $("#goodsId").val(result.rows.item(0)["id"]); dbApp.log("修改后请保存"); }); }, //更新 update: function() { if($("#goodsId").val()) { dbApp.exeSql("update goods set name=?,price=? where id=?", "更新", [$("#name").val(), $("#price").val(), $("#goodsId").val()], function(result) { dbApp.select(); $("#goodsId").val(""); }); } else { dbApp.log("请选择要更新的记录 "); } } }; dbApp.init(); </script> </body> </html>
运行结果:
五、IndexedDB
IndexedDB是在浏览器中保存结构化数据的一种数据库,为了替换WebSQL(标准已废弃,但被广泛支持)而出现。IndexedDB使用NoSQL的形式来操作数据库,保存和读取是JavaScript对象,同时还支持查询及搜索。
API: https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
示例代码:
if('indexedDB' in window){ //检测是否支持indexed database }else{ mui.alert('您的手机占不支持'); } //数据库信息 var DB = { name:'admin_users', version:1 } var db; function openDB(name,version){ //第一个参数是数据库名称,如果存在则打开,如果不存在就创建 //第二个参数是数据库版本,用于更新数据库结构 var request = window.indexedDB.open(name,version); request.onerror = function(e){ mui.alert('打开数据库失败'); } request.onsuccess = function(e){ db = request.result; ReadAll(); } //操作数据库(创建删除修改) //首次打开数据库或改变数据库版本的时候触发 request.onupgradeneeded = function(e){ //使用createObjectStore()方法创建一个对象存储 //此方法接受两个参数:存储的名称和参数对象 //keypath是用户数据的唯一标识 或者使用索引 var objectStore = e.target.result.createObjectStore("users",{keyPath:'name'}); } } function Add(name,account,password){ //transaction()方法是用来指定我们想要进行事务处理的对象存储,接受3个参数 //第一个(必选)要处理的对象存储的列表数组 //第二个(可选)指定操作方式 只读/读写 //第三个(可选)版本变化 //add()方法用于添加数据 var request = db.transaction(["users"],"readwrite").objectStore("users").add({ name:name, account:account, password:password }); request.onsuccess = function(e){ mui.toast('成功'); var list = document.querySelector('#list'); var dom = '<li class="mui-table-view-cell mui-collapse">'; dom+= '<a class="mui-navigate-right" href="#">'+name+'</a>'; dom+= '<div class="mui-collapse-content"><p><span>账号:</span>'+account+'</p>'; dom+= '<p><span>密码:</span>'+password+'</p></div></li>'; list.innerHTML += dom; } request.onerror = function(e){ mui.toast('失败'); } } function Read(name){ var objectStore = db.transaction(["users"]).objectStore("users"); //get()方法用于获取数据 var request = objectStore.get(name); request.onerror = function(event){ mui.toast('读取失败'); } request.onsuccess = function(event){ if(request.result){ console.log(request.result); } } } function ReadAll(){ var objectStore = db.transaction("users").objectStore("users"); //openCursor()方法用于获取所有数据 var request = objectStore.openCursor(); request.onsuccess = function(event){ //db.close(); var res = event.target.result; var list = document.querySelector('#list'); if(res){ var dom = '<li class="mui-table-view-cell mui-collapse">'; dom+= '<a class="mui-navigate-right" href="#">'+res.value.name+'</a>'; dom+= '<div class="mui-collapse-content"><p><span>账号:</span>'+res.value.account+'</p>'; dom+= '<p><span>密码:</span>'+res.value.password+'</p></div></li>'; list.innerHTML += dom; //console.log(res.value); res.continue(); } } request.onerror = function(e){ mui.toast('读取失败') } } function Remove(name){ //delete()方法用于删除数据 var request = db.transaction("users","readwrite").objectStore('users').delete(name); request.onsuccess = function(event){ mui.toast('删除成功'); } request.onerror = function(){ mui.toast('删除失败') } } openDB(DB.name,DB.version); var submits = document.querySelector('#submit'); function display(dis){ var bg = document.querySelectorAll('.alert-bg')[0]; var alert = document.querySelectorAll('.alert')[0]; alert.style.display = dis; bg.style.display = dis; } submits.onclick = function(){ var name = document.querySelector('#name').value; var account = document.querySelector('#account').value; var password = document.querySelector('#password').value; if(!name || !account || !password){ return mui.toast('请输入完整信息'); } display('none'); Add(name,account,password) }
5.1、数据库初始化
IndexedDB保存的是对象,而不是使用表保存数据。打开数据库使用indexDB.open方法,这方法有两个参数,第一个是数据库名称,第二个是数据版本号。
IndexedDB的操作完全是异步进行的,每一次IndexedDB操作,都需要注册onerror或onsuccess事件处理程序。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods",{"keyPath":"id"}); log("初始化数据库成功!"); }; } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
这里要注意的是,数据库版本只会有最新一个,不会同时存在两个版本的同名数据库。
运行结果:
5.2、对象存储空间(ObjectStore)
对象存储空间(ObjectStore)可以想象成关系数据库的表,在初始化DB触发onupgradeneeded时,创建ObjectStore。使用createObjectStore方法,第一个参数是对象名,第二个参数是对象属性,一般是设置keyPath(作为键使用)。
因为对新数据的操作都需要在transaction中进行,而transaction又要求指定object store,所以我们只能在创建数据库的时候初始化object store以供后面使用,这正是onupgradeneeded的一个重要作用
有了数据库后我们自然希望创建一个表用来存储数据,但indexedDB中没有表的概念,而是objectStore,一个数据库中可以包含多个objectStore,objectStore是一个灵活的数据结构,可以存放多种类型数据。也就是说一个objectStore相当于一张表,里面存储的每条数据和一个键相关联。
我们可以使用每条记录中的某个指定字段作为键值(keyPath),也可以使用自动生成的递增数字作为键值(keyGenerator),也可以不指定。选择键的类型不同,objectStore可以存储的数据结构也有差异
不使用—>任意值,但是没添加一条数据的时候需要指定键参数
keyPath—>Javascript对象,对象必须有一属性作为键值
keyGenerator—>任意值(db.createObjectStore('students',{autoIncrement: true});)
都使用—>Javascript对象,如果对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性
req.onupgradeneeded = function (evt) { console.debug("initDb.onupgradeneeded"); var db = evt.currentTarget.result; //ObjectStore必须在onupgradeneeded里创建,其他地方将会创建失败 var usersStore = db.createObjectStore("users", { keyPath : "id" }); };
效果如下:
5.3、事务
所有读取或修改数据的操作,都要通过事务来完成。创建事务使用transaction方法,第一个参数是需要访问的ObjectStore,第二个参数是访问模式(readwrite可读可写、readonly只读,默认是只读)。
5.4、添加数据
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <input value="新增数据" type="button" onclick="add()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods",{"keyPath":"id"}); log("初始化数据库成功!"); }; } //新增数据 function add(){ //创建事务对象 var tx=db.transaction("goods","readwrite"); //从数据库中获得存储对象,表 var goods=tx.objectStore("goods"); //javascript中的对象数组 var items=[{"id":1,"name":"iPhone 11","price":19999.5},{"id":2,"name":"华为荣耀V9","price":1997.3}]; for(var i=0;i<items.length;i++){ goods.add(items[i]); } log("添加数据成功!"); } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
结果:
5.5、获取单个对象数据
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <input value="新增数据" type="button" onclick="add()" /> <input value="获得单个对象" type="button" onclick="getSingle()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods", { "keyPath": "id" }); log("初始化数据库成功!"); }; } //新增数据 function add() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var items = [{ "id": 1, "name": "iPhone 11", "price": 19999.5 }, { "id": 2, "name": "华为荣耀V9", "price": 1997.3 }]; for(var i = 0; i < items.length; i++) { goods.add(items[i]); } log("添加数据成功!"); } //获得单个对象 function getSingle() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //请求key为1的对象 var request=goods.get(1); request.onsuccess=function(e){ var obj=e.target.result; //JSON.stringify将obj对象转换成字符 log(JSON.stringify(obj)); log(obj.id+","+obj.name+","+obj.price); }; } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
结果:
5.6、修改数据
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <input value="新增数据" type="button" onclick="add()" /> <input value="获得单个对象" type="button" onclick="getSingle()" /> <input value="更新对象" type="button" onclick="edit()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods", { "keyPath": "id" }); log("初始化数据库成功!"); }; } //新增数据 function add() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var items = [{ "id": 1, "name": "iPhone 11", "price": 19999.5 }, { "id": 2, "name": "华为荣耀V9", "price": 1997.3 }]; for(var i = 0; i < items.length; i++) { goods.add(items[i]); } log("添加数据成功!"); } //获得单个对象 function getSingle() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //请求key为1的对象 var request = goods.get(2); request.onsuccess = function(e) { var obj = e.target.result; //JSON.stringify将obj对象转换成字符 log(JSON.stringify(obj)); log(obj.id + "," + obj.name + "," + obj.price); }; } //更新对象 function edit() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var item = { "id": 2, "name": "华为荣耀P15", "price":1357.9, "os":"android" }; //执行更新 var request=goods.put(item); request.onsuccess=function(e){ log(e.target.result); }; } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
结果:
5.7、删除数据
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <input value="新增数据" type="button" onclick="add()" /> <input value="获得单个对象" type="button" onclick="getSingle()" /> <input value="更新对象" type="button" onclick="edit()" /> <input value="删除对象" type="button" onclick="del()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods", { "keyPath": "id" }); log("初始化数据库成功!"); }; } //新增数据 function add() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var items = [{ "id": 1, "name": "iPhone 11", "price": 19999.5 }, { "id": 2, "name": "华为荣耀V9", "price": 1997.3 }]; for(var i = 0; i < items.length; i++) { goods.add(items[i]); } log("添加数据成功!"); } //获得单个对象 function getSingle() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //请求key为1的对象 var request = goods.get(2); request.onsuccess = function(e) { var obj = e.target.result; //JSON.stringify将obj对象转换成字符 log(JSON.stringify(obj)); log(obj.id + "," + obj.name + "," + obj.price); }; } //更新对象 function edit() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var item = { "id": 2, "name": "华为荣耀P15", "price":1357.9, "os":"android" }; //执行更新 var request=goods.put(item); request.onsuccess=function(e){ log(e.target.result); }; } //删除对象 function del() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //执行删除操作 var request=goods.delete(2); //成功时的回调 request.onsuccess=function(e){ log(e.target.result); }; } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
结果:
5.8、清空数据
function clearData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.clear(); req.onsuccess = function (evt) { console.debug("clearData success"); }; req.onerror = function (evt) { console.error("clearData error:", evt.target.errorCode || evt.target.error); }; }
5.9、游标查询
使用事务可以直接通过键检索单个对象,而需要检索多个对象时候就需要使用游标。游标是指向结果集的指针,不提前收集结果。游标指针会先指向结果中的第一项,在接到查找下一项指令时,才会指向下一项。
function openCursor(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var req = store.openCursor(); req.onsuccess = function (evt) { var cursor = evt.target.result; if(cursor){ //必要检查 var value = cursor.value; console.log(value); if(value.name == '杨幂'){ value.age = 16; cursor.update(value); //修改数据(必须是读写模式) } if(value.name == '柳岩'){ cursor.delete(); //删除当前项 } cursor.continue(); //移动到下一项 } }; req.onerror = function (evt) { console.error("openCursor error:", evt.target.errorCode || evt.target.error); }; }
这里有几点要注意:
1. 如果需要修改或删除数据,就需要打开成读写模式。
2. cursor的非空校验是必要的。
3. 修改或删除的操作也是有onsuccess和onerror的,只是在示例中没有写出来。
4. 调用continue才会移动到下一项
另外可以设置游标的键范围和游标的方向,即打开openCursor方法时可以传这两个参数(openCursor(键范围,方向)),第一个参数是object类型,第二个参数是字符串类型。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB</title> </head> <body> <h2>IndexedDB(NOSQL)</h2> <input value="创建数据库与表" type="button" onclick="create()" /> <input value="新增数据" type="button" onclick="add()" /> <input value="获得单个对象" type="button" onclick="getSingle()" /> <input value="更新对象" type="button" onclick="edit()" /> <input value="删除对象" type="button" onclick="del()" /> <input value="获得多个对象(游标)" type="button" onclick="getAll()" /> <h2 id="msg"></h2> <script> //数据库 var db; //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomall", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库成功'); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods", { "keyPath": "id" }); log("初始化数据库成功!"); }; } //新增数据 function add() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var items = [{ "id": 2, "name": "iPhone 13", "price": 19999.5 }, { "id": 3, "name": "华为荣耀V10", "price": 1997.3 }]; for(var i = 0; i < items.length; i++) { goods.add(items[i]); } log("添加数据成功!"); } //获得单个对象 function getSingle() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //请求key为1的对象 var request = goods.get(2); request.onsuccess = function(e) { var obj = e.target.result; //JSON.stringify将obj对象转换成字符 log(JSON.stringify(obj)); log(obj.id + "," + obj.name + "," + obj.price); }; } //更新对象 function edit() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var item = { "id": 2, "name": "华为荣耀P15", "price": 1357.9, "os": "android" }; //执行更新 var request = goods.put(item); request.onsuccess = function(e) { log(e.target.result); }; } //删除对象 function del() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //执行删除操作 var request = goods.delete(2); //成功时的回调 request.onsuccess = function(e) { log(e.target.result); }; } //获得多个对象(游标) function getAll() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //打开游标 var request = goods.openCursor(); //成功时的回调 request.onsuccess = function(e) { //获得游标 var cursor = e.target.result; if(cursor) { //如果不为空 var obj = cursor.value; log(JSON.stringify(obj)); //下移 cursor.continue(); } }; } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } //用于判断浏览器是否支持indexedDB,0,null,'',undefind false if(window.indexedDB) { } else { alert('请升级浏览器,如chrome'); } </script> </body> </html>
结果:
5.10、游标键范围
键范围由IDBKeyRange的实例表示。
IDBKeyRange.only('001'); //只想要键为001的结果 IDBKeyRange.lowerBound('002'); //从键为002开始,到最后 IDBKeyRange.lowerBound('002', true); //从键为002开始,但忽略002,到最后 IDBKeyRange.upperBound('002'); //从头开始,到键为002为止 IDBKeyRange.upperBound('002', true); //从头开始,到键为002为止,但忽略002 IDBKeyRange.bound('001', '005'); //从001开始,到为005为止 IDBKeyRange.bound('001', '005', true, true); //从001开始,到为005为止,但忽略001、005
5.11、游标方向
next : 从第一项到最后一项(默认)
prev : 从最后一项到第一项
索引
当需要使用其他属性(非主键)获取数据时,就要预先创建索引,然后使用索引获取数据。
创建索引(在数据库初始化onupgradeneeded事件时)
第一个参数是索引名字,第二个参数是索引的属性的名字,第三个是一个options对象。一般是指定unique,设置索引是否唯一。
usersStore.createIndex("name", "name", { unique : false });
索引获取数据
function indexGetData(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var index = store.index("name"); var req = index.get("杨幂") req.onsuccess = function (evt) { console.debug("indexGet success" , evt.target.result); }; req.onerror = function (evt) { console.error("indexGet error:", evt.target.errorCode || evt.target.error); }; } function indexOpenCursor(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var index = store.index("name"); var req = index.openCursor(); req.onsuccess = function (evt) { var cursor = evt.target.result; if(cursor){ //必要检查 var value = cursor.value; console.log(value); cursor.continue(); //移动到下一项 } }; req.onerror = function (evt) { console.error("openCursor error:", evt.target.errorCode || evt.target.error); }; }
PS:索引用法跟普通取值和游标取值一样
对象存储所有索引
function indexNames(){ var tx = db.transaction("users", READ_WRITE); var store = tx.objectStore("users"); var indexNames = store.indexNames; var index, i = 0, len = indexNames.length; while(i < len){ index = store.index(indexNames[i++]); console.log(index); } }
5.12、删除数据库
var request = indexedDB.deleteDatabase(name);
var request = indexedDB.deleteDatabase(name, options);
var DBDeleteRequest = window.indexedDB.deleteDatabase("toDoList"); DBDeleteRequest.onerror = function(event) { console.log("Error deleting database."); }; DBDeleteRequest.onsuccess = function(event) { console.log("Database deleted successfully"); console.log(event.result); // should be undefined };
5.13、删除存储对象
dbInstance.deleteObjectStore(name);
var dbName = "sampleDB"; var dbVersion = 2; var request = indexedDB.open(dbName, dbVersion); request.onupgradeneeded = function(e) { var db = request.result; if (e.oldVersion < 1) { db.createObjectStore("store1"); } if (e.oldVersion < 2) { db.deleteObjectStore("store1"); db.createObjectStore("store2"); } // etc. for version < 3, 4... };
六、移动端打包与运行
6.1、在手机端直接访问Web站点
将手机与电脑连接到同一个网段,比如可以使用wifi
查看本机ip地址,有时需要将本地连接禁用,查看ip地址的指令是ipconfig
在手机端使用浏览器查看结果如下:
6.2、打包成app安装运行
这里使用HBuilder打包成apk的安装包,安装打包结果如下:
6.3、套用移动端UI框架MUI
文档: http://dev.dcloud.net.cn/mui/ui/
源码: https://github.com/dcloudio/mui/
6.3.1、Hello MUI
示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title>Hello MUI</title> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">Hello MUI</h1> </header> <div class="mui-content"> <div class="mui-content-padded"> <button type="button" class="mui-btn">默认</button> <button type="button" class="mui-btn mui-btn-primary" id="btnHello">Hello</button> <button type="button" class="mui-btn mui-btn-success">绿色</button> <button type="button" class="mui-btn mui-btn-warning">黄色</button> <button type="button" class="mui-btn mui-btn-danger">红色</button> <button type="button" class="mui-btn mui-btn-royal">紫色</button> </div> </div> <script src="js/mui.min.js"></script> <script type="text/javascript" charset="utf-8"> mui.init({ }); document.getElementById("btnHello").addEventListener("tap",function(){ mui.alert("Hello MUI!","提示","确认",function(){ mui.toast("关闭完成"); }); },false); </script> </body> </html>
效果:
6.3.2、事件与转场
代码:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> <title>Hello MUI</title> <link href="css/mui.min.css" rel="stylesheet" /> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">Hello MUI</h1> </header> <div class="mui-content"> <div class="mui-content-padded"> <button type="button" class="mui-btn">默认</button> <button type="button" class="mui-btn mui-btn-primary" id="btnHello">Hello</button> <button type="button" class="mui-btn mui-btn-success" id="btnWin">窗口</button> <button type="button" class="mui-btn mui-btn-warning">黄色</button> <button type="button" class="mui-btn mui-btn-danger">红色</button> <button type="button" class="mui-btn mui-btn-royal">紫色</button> </div> </div> <script src="js/mui.min.js"></script> <script type="text/javascript" charset="utf-8"> mui.init({ //侧滑关闭 swipeBack: true //Boolean(默认false)启用右滑关闭功能 }); document.getElementById("btnHello").addEventListener("tap", function() { mui.alert("Hello MUI!", "提示", "确认", function() { mui.toast("关闭完成"); }); }, false); //mui加载完成 mui.ready(function() { mui(".mui-content").on("tap", "#btnWin", function() { mui.openWindow({ url: "sub1.html?id=1", id: "sub1.html", extras: { id: "1" }, createNew: false, //是否重复创建同样id的webview,默认为false:不重复创建,直接显示 show: { autoShow: true //页面loaded事件发生后自动显示,默认为true }, waiting: { autoShow: true, //自动显示等待框,默认为true title: '正在加载...' //等待对话框上显示的提示内容 } }); }); }); </script> </body> </html>
效果:
6.3.3、列表与选项卡
示例:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello List</title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <!--标准mui.css--> <link rel="stylesheet" href="css/mui.min.css"> <style> .title { margin: 20px 15px 10px; color: #6d6d72; font-size: 15px; } .oa-contact-cell.mui-table .mui-table-cell { padding: 11px 0; vertical-align: middle; } .oa-contact-cell { position: relative; margin: -11px 0; } .oa-contact-avatar { width: 75px; } .oa-contact-avatar img { border-radius: 50%; } .oa-contact-content { width: 100%; } .oa-contact-name { margin-right: 20px; } .oa-contact-name, oa-contact-position { float: left; } </style> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">用户列表</h1> </header> <nav class="mui-bar mui-bar-tab"> <a class="mui-tab-item mui-active" href="#tabbar"> <span class="mui-icon mui-icon-home"></span> <span class="mui-tab-label">首页</span> </a> <a class="mui-tab-item" href="#tabbar-with-chat"> <span class="mui-icon mui-icon-email"><span class="mui-badge">9</span></span> <span class="mui-tab-label">添加</span> </a> <a class="mui-tab-item" href="#tabbar-with-contact"> <span class="mui-icon mui-icon-contact"></span> <span class="mui-tab-label">我的</span> </a> <a class="mui-tab-item" href="#tabbar-with-map"> <span class="mui-icon mui-icon-gear"></span> <span class="mui-tab-label">设置</span> </a> </nav> <div class="mui-content"> <div id="slider" class="mui-slider"> <div class="mui-slider-group mui-slider-loop"> <!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="images/yuantiao.jpg"> </a> </div> <!-- 第一张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/shuijiao.jpg"> </a> </div> <!-- 第二张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/muwu.jpg"> </a> </div> <!-- 第三张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/cbd.jpg"> </a> </div> <!-- 第四张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/yuantiao.jpg"> </a> </div> <!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="images/shuijiao.jpg"> </a> </div> </div> <div class="mui-slider-indicator"> <div class="mui-indicator mui-active"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> </div> <!--轮播--> <!--list--> <ul class="mui-table-view mui-table-view-chevron"> <li class="mui-table-view-cell mui-media"> <a class="mui-navigate-right" id="sub1.html"> <img class="mui-media-object mui-pull-left" src="images/cbd.jpg"> <div class="mui-media-body"> CBD <p class='mui-ellipsis'>烤炉模式的城,到黄昏,如同打翻的调色盘一般.</p> </div> </a> </li> <li class="mui-table-view-cell mui-media"> <a class='mui-navigate-right' id="index.html"> <img class="mui-media-object mui-pull-left" src="images/yuantiao.jpg"> <div class="mui-media-body"> 远眺 <p class='mui-ellipsis'>静静的看这个世界,最后终于疯了</p> </div> </a> </li> <li class="mui-table-view-cell mui-media"> <a class="mui-navigate-right"> <img class="mui-media-object mui-pull-left" src="images/shuijiao.jpg"> <div class="mui-media-body"> 幸福 <p class='mui-ellipsis'>能和心爱的人一起睡觉,是件幸福的事情;可是,打呼噜怎么办?</p> </div> </a> </li> </ul> <!--list--> </div> <div id="tabbar-with-chat" class="mui-control-content"> 添加 </div> <div id="tabbar-with-contact" class="mui-control-content"> 我的 </div> <div id="tabbar-with-map" class="mui-control-content"> 设置 </div> </div> </body> <script src="js/mui.min.js"></script> <script> mui.init({ swipeBack: true //启用右滑关闭功能 }); mui("#slider").slider({ interval: 1000 }); mui.ready(function() { mui(".mui-content").on("tap", ".mui-navigate-right", function() { var id =this.id; mui.openWindow({ url: id, id: id, extras: { id: "1" }, createNew: false, //是否重复创建同样id的webview,默认为false:不重复创建,直接显示 show: { autoShow: true //页面loaded事件发生后自动显示,默认为true }, waiting: { autoShow: true, //自动显示等待框,默认为true title: '正在加载...' //等待对话框上显示的提示内容 } }); }); }); </script> </html>
效果:
这里使用HBuilder内置的MUI为例,新增d06.html,页面脚本如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>淘水果</title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <!--标准mui.css--> <link rel="stylesheet" href="css/mui.min.css"> <!--App自定义的css--> <link rel="stylesheet" type="text/css" href="css/app.css" /> <style> .title { margin: 20px 15px 10px; color: #6d6d72; font-size: 15px; } .oa-contact-cell.mui-table .mui-table-cell { padding: 11px 0; vertical-align: middle; } .oa-contact-cell { position: relative; margin: -11px 0; } .oa-contact-avatar { width: 75px; } .oa-contact-avatar img { border-radius: 50%; } .oa-contact-content { width: 100%; } .oa-contact-name { margin-right: 20px; } .oa-contact-name, oa-contact-position { float: left; } </style> </head> <body> <header class="mui-bar mui-bar-nav"> <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 class="mui-title">淘水果</h1> </header> <nav class="mui-bar mui-bar-tab"> <a class="mui-tab-item mui-active" href="#tabbar"> <span class="mui-icon mui-icon-home"></span> <span class="mui-tab-label">畅销水果</span> </a> <a class="mui-tab-item" href="#tabbar-with-chat"> <span class="mui-icon mui-icon-email"><span class="mui-badge">9</span></span> <span class="mui-tab-label">新货上架</span> </a> <a class="mui-tab-item" href="#tabbar-with-contact"> <span class="mui-icon mui-icon-contact"></span> <span class="mui-tab-label">收款</span> </a> <a class="mui-tab-item" href="#tabbar-with-map"> <span class="mui-icon mui-icon-gear"></span> <span class="mui-tab-label">发货</span> </a> </nav> <div class="mui-content"> <div id="tabbar" class="mui-control-content mui-active"> <div id="slider" class="mui-slider"> <div class="mui-slider-group mui-slider-lhttp://127.0.0.1:8020/HTML5_2_1/d06.html#oop"> <!-- 额外增加的一个节点(循环轮播:第一个节点是最后一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="images/yuantiao.jpg"> </a> </div> <!-- 第一张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/shuijiao.jpg"> </a> </div> <!-- 第二张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/muwu.jpg"> </a> </div> <!-- 第三张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/cbd.jpg"> </a> </div> <!-- 第四张 --> <div class="mui-slider-item"> <a href="#"> <img src="images/yuantiao.jpg"> </a> </div> <!-- 额外增加的一个节点(循环轮播:最后一个节点是第一张轮播) --> <div class="mui-slider-item mui-slider-item-duplicate"> <a href="#"> <img src="images/shuijiao.jpg"> </a> </div> </div> <div class="mui-slider-indicator"> <div class="mui-indicator mui-active"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> <div class="mui-indicator"></div> </div> </div> <ul class="mui-table-view mui-table-view-chevron" id="goodsList"> </ul> </div> <div id="tabbar-with-chat" class="mui-control-content"> <p> <label for="name">名称:</label> <input type="text" id="name" value="" /> </p> <p> <label for="price">价格:</label> <input type="text" id="price" value="" /> </p> <p> <input type="hidden" id="goodsId" /> <button id="btnInsert">添加</button> <button id="btnUpdate">更新</button> </p> </div> <div id="tabbar-with-contact" class="mui-control-content"> <div class="title">这是div模式选项卡中的第3个子页面,该页面展示一个通讯录示例.</div> <ul class="mui-table-view mui-table-view-striped mui-table-view-condensed"> <li class="mui-table-view-cell"> <div class="mui-slider-cell"> <div class="oa-contact-cell mui-table"> <div class="oa-contact-avatar mui-table-cell"> <img src="images/60x60.gif" /> </div> <div class="oa-contact-content mui-table-cell"> <div class="mui-clearfix"> <h4 class="oa-contact-name">叶文洁</h4> <span class="oa-contact-position mui-h6">董事长</span> </div> <p class="oa-contact-email mui-h6"> yewenjie@sina.com </p> </div> </div> </div> </li> <li class="mui-table-view-cell"> <div class="mui-slider-cell"> <div class="oa-contact-cell mui-table"> <div class="oa-contact-avatar mui-table-cell"> <img src="images/60x60.gif" /> </div> <div class="oa-contact-content mui-table-cell"> <div class="mui-clearfix"> <h4 class="oa-contact-name">艾AA</h4> <span class="oa-contact-position mui-h6">总经理</span> </div> <p class="oa-contact-email mui-h6"> aaa@163.com </p> </div> </div> </div> </li> <li class="mui-table-view-cell"> <div class="mui-slider-cell"> <div class="oa-contact-cell mui-table"> <div class="oa-contact-avatar mui-table-cell"> <img src="images/60x60.gif" /> </div> <div class="oa-contact-content mui-table-cell"> <div class="mui-clearfix"> <h4 class="oa-contact-name">罗辑</h4> <span class="oa-contact-position mui-h6">员工</span> </div> <p class="oa-contact-email mui-h6"> luoji@126.com </p> </div> </div> </div> </li> <li class="mui-table-view-cell"> <div class="mui-slider-cell"> <div class="oa-contact-cell mui-table"> <div class="oa-contact-avatar mui-table-cell"> <img src="images/60x60.gif" /> </div> <div class="oa-contact-content mui-table-cell"> <div class="mui-clearfix"> <h4 class="oa-contact-name">云天明</h4> <span class="oa-contact-position mui-h6">员工</span> </div> <p class="oa-contact-email mui-h6"> ytm@163.com </p> </div> </div> </div> </li> <li class="mui-table-view-cell"> <div class="mui-slider-cell"> <div class="oa-contact-cell mui-table"> <div class="oa-contact-avatar mui-table-cell"> <img src="images/60x60.gif" /> </div> <div class="oa-contact-content mui-table-cell"> <div class="mui-clearfix"> <h4 class="oa-contact-name">史强</h4> <span class="oa-contact-position mui-h6">员工</span> </div> <p class="oa-contact-email mui-h6"> shiqiang@gmail.com </p> </div> </div> </div> </li> </ul> </div> <div id="tabbar-with-map" class="mui-control-content"> <div class="title">这是div模式选项卡中的第4个子页面,该页面展示一个常见的设置示例.</div> <ul class="mui-table-view"> <li class="mui-table-view-cell"> <a class="mui-navigate-right"> 新消息通知 </a> </li> <li class="mui-table-view-cell"> <a class="mui-navigate-right"> 隐私 </a> </li> <li class="mui-table-view-cell"> <a class="mui-navigate-right"> 通用 </a> </li> </ul> <ul class="mui-table-view" style="margin-top: 25px;"> <li class="mui-table-view-cell"> <a class="mui-navigate-right"> 关于mui </a> </li> </ul> <ul class="mui-table-view" style="margin-top: 25px;"> <li class="mui-table-view-cell"> <a style="text-align: center;color: #FF3B30;"> 退出登录 </a> </li> </ul> </div> </div> </body> <script src="js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/mui.min.js"></script> <script> //定义当前应用的对象 var dbApp = { //打开数据库 openDb: function() { //创建名称为products,版本为1.0,描述为产品数据库,3M大小的数据库 this.db = openDatabase("products", 1.0, "产品数据库", 1024 * 1024 * 3, function() { this.log("创建或打开数据库完成"); }); }, //初始化 init: function() { //打开或创建数据库 this.openDb(); //绑定事件 this.bindEvent(); //展示数据 this.select(); this.log("初始化完成"); }, //绑定事件 bindEvent: function() { //添加事件 $("#btnInsert").click(this.insert); $("#btnUpdate").click(this.update); $("#btnCreateTable").click(this.createTable); $("#btnDropTable").click(this.dropTable); }, //显示消息 log: function(info) { mui.toast(info); }, //执行sql的通用方法 result.rowsAffected 影响行数 //callback执行成功时的回调方法 exeSql: function(sql, title, param, callback) { title = title || "操作"; this.db.transaction(function(tx) { tx.executeSql( sql, param || [], function(tx, result) { dbApp.log(title + '成功'); if(callback) { //如果有参数 callback(result); } }, function(tx, error) { dbApp.log(title + '失败' + error.message); }); }); }, //创建表 createTable: function() { dbApp.exeSql("create table IF not EXISTS goods(id integer primary key autoincrement,name text not null,price double)", "创建表"); }, //删除表 dropTable: function() { dbApp.exeSql("drop table IF EXISTS goods", "删除表"); }, //展示,加载数据 select: function() { dbApp.exeSql("select id,name,price from goods", "查询", [], function(result) { //将表格中tr索引大于0的元素删除 $("#goodsList li").remove(); for(var i = 0; i < result.rows.length; i++) { var tr = $("<li class='mui-table-view-cell mui-media'><a class='mui-navigate-right'><img class='mui-media-object mui-pull-left' src='images/cbd.jpg'><div class='mui-media-body'>" + result.rows.item(i)["name"] + "<p class='mui-ellipsis'>新鲜好吃的水果,仅售:" + result.rows.item(i)["price"] + "/斤</p></div></a></li>") tr.appendTo("#goodsList"); } mui.init({ swipeBack: true //启用右滑关闭功能 }); }); }, //插入数据 insert: function() { //如果insert方法被绑定为事件,则this表示事件发生的对象 dbApp.exeSql("insert into goods(name,price) values(?,?)", "添加", [$("#name").val(), $("#price").val()], function() { dbApp.select(); }); }, //删除 del: function(id, link) { dbApp.exeSql("delete from goods where id=?", "删除", [id], function(result) { //查找a标签最近的一个tr父元素,移除 $(link).closest("tr").remove(); }); }, //编辑 edit: function(id) { dbApp.exeSql("select id,name,price from goods where id=?", "编辑", [id], function(result) { $("#name").val(result.rows.item(0)["name"]); $("#price").val(result.rows.item(0)["price"]); $("#goodsId").val(result.rows.item(0)["id"]); dbApp.log("修改后请保存"); }); }, //更新 update: function() { if($("#goodsId").val()) { dbApp.exeSql("update goods set name=?,price=? where id=?", "更新", [$("#name").val(), $("#price").val(), $("#goodsId").val()], function(result) { dbApp.select(); $("#goodsId").val(""); }); } else { dbApp.log("请选择要更新的记录 "); } } }; dbApp.init(); </script> </html>
打包运行后的结果如下:
七、示例下载
https://git.coding.net/zhangguo5/HTML5_143.git
https://coding.net/u/zhangguo5/p/HTML502/git
github: https://github.com/zhangguo5/HTML5_2_1
mui示例: https://git.coding.net/zhangguo5/GoMallPro.git
八、视频
https://www.bilibili.com/video/av16293468/
九、作业
9.1.1、重现每个上课示例。
9.1.2、用户登录,记住密码,选择颜色,下次打开页面时不需要输入密码,将背景色设置为用户选择的颜色。Login.html,Admin.html
9.1.3、用SessionStorage判断用户是否登录,如果未登录转到页面Login.html
9.1.4、使用IndexedDb完成如下功能:
参考:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>IndexedDB-UI</title> </head> <body> <h2>IndexedDB-UI(NOSQL)</h2> <table border="1" cellspacing="1" cellpadding="1" id="tabGoods" width="100%"> <tr> <th>序号</th> <th>编号</th> <th>名称</th> <th>价格</th> <th>操作</th> </tr> </table> <fieldset> <legend>商品信息</legend> <p> <label for="id">编号</label> <input type="text" name="id" id="id" value="" /> </p> <p> <label for="name">名称</label> <input type="text" name="name" id="name" value="" /> </p> <p> <label for="price">价格</label> <input type="number" name="price" id="price" value="" /> </p> <p> <input type="button" id="btnSubmit" value="添加" /> <input type="button" id="btnUpdate" value="更新" /> </p> </fieldset> <h3 id="msg"></h3> <script src="../js/jQuery/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script> //数据库 var db; function init() { create(); $("body").on("click", "#btnSubmit", {}, function() { add(); }); $("#tabGoods").on("click", ".del", {}, function() { if(confirm('您确定要删除吗?')) { var tr = $(this).closest("tr"); del(tr.data("goods").id, function() { alert('删除成功!'); tr.remove(); }); } }); $("#tabGoods").on("click", ".edit", {}, function() { var tr = $(this).closest("tr"); var obj = tr.data("goods"); $("#id").val(obj.id).prop("disabled", "disabled"); $("#name").val(obj.name); $("#price").val(obj.price); }); $("body").on("click", "#btnUpdate", {}, function() { var obj = { "id": $("#id").val(), "name": $("#name").val(), "price": $("#price").val() }; edit(obj, function() { alert('修改成功!'); getAll(); }); $("#id").val(obj.id).removeProp("disabled"); }); } init(); //创建数据库与表 function create() { //创建一个名称为gomall且版本为2的数据库,返回一个请求 var request = indexedDB.open("gomallPro", 2); //绑定回调事件,成功时 request.onsuccess = function(e) { db = e.target.result; log('创建数据库或打开数据库成功!'); getAll(); }; //失败时 request.onerror = function(e) { log("错误:" + e.target.errorCode || e.target.error); }; //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function(e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore("goods", { "keyPath": "id" }); log("初始化数据库成功!"); }; } //新增数据 function add() { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //javascript中的对象数组 var item = { "id": $("#id").val(), "name": $("#name").val(), "price": $("#price").val() }; goods.add(item); log("添加数据成功!"); getAll(); } //更新对象 function edit(item, callback) { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //执行更新 var request = goods.put(item); request.onsuccess = function(e) { log(e.target.result); if(callback) callback(); }; } //删除对象 function del(key, callback) { //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //执行删除操作 var request = goods.delete(key); //成功时的回调 request.onsuccess = function(e) { log(e.target.result); log("删除成功!"); if(callback) callback(); }; } var index; //获得多个对象(游标) function getAll() { index = 1; //移除表格中除第一行以外的所有行 $("#tabGoods tr:gt(0)").remove(); //创建事务对象 var tx = db.transaction("goods", "readwrite"); //从数据库中获得存储对象,表 var goods = tx.objectStore("goods"); //打开游标 var request = goods.openCursor(); //成功时的回调 request.onsuccess = function(e) { //获得游标 var cursor = e.target.result; if(cursor) { //如果不为空 var obj = cursor.value; genTr(obj); //下移 cursor.continue(); } }; } function genTr(goods) { var tr = $("<tr/>").data("goods", goods); $("<td/>").html(index++).appendTo(tr); $("<td/>").html(goods.id).appendTo(tr); $("<td/>").html(goods.name).appendTo(tr); $("<td/>").html(goods.price).appendTo(tr); var btnDel = $("<input type='button' value='删除' class='del'/>"); var btnEdit = $("<input type='button' value='编辑' class='edit'/>"); $("<td/>").append(btnDel).append(btnEdit).appendTo(tr); tr.appendTo($("#tabGoods")); } //显示消息 var msg = document.getElementById("msg"); function log(m) { msg.innerHTML += m + "<br/>"; } </script> </body> </html>
运行结果:
9.2.1、完成一个密码管理器APP
javascript获取url中的参数
//方法一:采用正则表达式获取地址栏参数 function GetQueryString(name) { var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null)return unescape(r[2]); return null; }
十、参考示例
10.1、添加与展示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>IndexedDb</title> </head> <body> <div> <table border="1" width="100%" id="tabGoods"> <tr> <th>编号</th><th>名称</th><th>价格</th><th>操作</th> </tr> </table> </div> <p> <button onclick="nfapp.insert()" type="button">添加数据</button> <button onclick="nfapp.select()" type="button">获得所有数据</button> </p> <script src="../js/jquery-1.12.4.min.js"></script> <script> ;(function () { var app = { db: null, run: function () { this.createDb(); }, //创建数据库 createDb: function () { //创建或打开数据库gomall,指定版本为1 var request = indexedDB.open("gomall", 1); //当发生错误时的回调事件 request.onerror = function (e) { console.log("错误:" + e.target.errorCode + "," + e.target.error); }; //成功时的回调函数 request.onsuccess = function (e) { app.db = e.target.result; console.log("创建或打开数据库成功!"); app.select(); }; //当数据库版本发生变化时的回调函数,发生在onsuccess之前 request.onupgradeneeded = function (e) { //当前数据库,因为onsuccess还未执行,获得不到数据库 var db = e.currentTarget.result; //创建存储对象,指定名称与参数,keyPath表示主键 db.createObjectStore("goods", {keyPath: "id"}); } }, //添加数据 insert: function () { //创建事务对象 var tx = this.db.transaction("goods", "readwrite"); //获得存储对象 var goods = tx.objectStore("goods"); //添加数据对象 goods.add({id: 103, name: "Meizu 16th", price: 3998}); goods.add({id: 102, name: "Oppo R15", price: 2939}); goods.add({id: 101, name: "Huwei 8X", price: 1398}); }, //获取所有数据 select:function () { //创建事务对象 var tx = this.db.transaction("goods", "readwrite"); //获得存储对象 var goods = tx.objectStore("goods"); //打开游标 var request=goods.openCursor(); //成功时的回调函数 request.onsuccess=function (e) { //获得游标 var cursor=e.target.result; //如果存在,没有到末尾 if(cursor){ console.log(cursor.value); //生成DOM行 app.createRow(cursor.value); //游标下移 cursor.continue(); } }; }, createRow:function (obj) { var tr=$("<tr/>"); $("<td/>").text(obj.id).appendTo(tr); $("<td/>").text(obj.name).appendTo(tr); $("<td/>").text(obj.price).appendTo(tr); $("<td/>").text("").appendTo(tr); $("#tabGoods").append(tr); } }; app.run(); window.nfapp = app; })(); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>商品管理</title> <style> #tabGoods { border: 1px solid #00f; border-collapse: collapse; width: 100%; } #tabGoods td, #tabGoods th { border: 1px solid #00f; border-collapse: collapse; } </style> </head> <body> <h2>商品管理</h2> <table id="tabGoods"> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>操作</th> </tr> </table> <fieldset> <legend>商品</legend> <p> <label for="txtId">编号</label> <input type="number" id="txtId" name="txtId" /> </p> <p> <label for="txtName">名称</label> <input type="text" id="txtName" name="txtName" /> </p> <p> <label for="txtPrice">价格</label> <input type="number" id="txtPrice" name="txtPrice" /> </p> <p> <button id="btnAdd" type="button">添加</button> <button id="btnUpdate" type="button">更新</button> </p> </fieldset> <h3 id="msg"></h3> <script src="../js/jquery-1.12.4.min.js"></script> <script> ;(function () { var db //NoSql数据库 var READ_WRITE = 'readwrite' function log(msg) { console.log(msg); document.getElementById('msg').innerHTML += JSON.stringify(msg) + '<br/>' } var app = {} app.currentObj = null app.init = function () { app.createDb(function () { //app.add(); app.getAll() }) $('#btnAdd').click(function () { //获取事务对象 var tx = db.transaction('goods', 'readwrite') //获取存储对象 var goods = tx.objectStore('goods') var obj = { id: $('#txtId').val(), name: $('#txtName').val(), price: $('#txtPrice').val() } goods.add(obj) log('添加成功!') app.getAll() }) $('#btnUpdate').click(function () { //获取事务对象 var tx = db.transaction('goods', 'readwrite') //获取存储对象 var goods = tx.objectStore('goods') var obj = app.currentObj if (obj) { obj.name = $('#txtName').val() obj.price = $('#txtPrice').val() //执行更新 var request = goods.put(obj) request.onsuccess = function (e) { log(e.target.result) log('更新成功') app.currentObj=null; app.getAll() } } }) } //创建数据库 app.createDb = function (myEvent) { //如果数据库mallDB存在就打开,如果不存在就创建数据库,版本号为1 var request = indexedDB.open('mallDB', 1) request.onsuccess = function (e) { db = e.target.result //获取数据库 log('获取数据库成功!') myEvent() } request.onerror = function (e) { alert('错误:' + e.target.errorCode || e.target.error) } //增加数据库版本号时,会触发onupgradeneeded事件(会在onsuccess之前被调用) request.onupgradeneeded = function (e) { //创建对象存储空间(ObjectStore),类似表,goods是表名,id是主键,存储的是JSON e.target.result.createObjectStore('goods', { keyPath: 'id' }) log('初始化数据库成功!') } } app.add = function () { //获取事务对象 var tx = db.transaction('goods', 'readwrite') //获取存储对象 var goods = tx.objectStore('goods') var items = [ { id: 1, name: 'iPhone 11', price: 9999.5 }, { id: 2, name: '华为荣耀V9', price: 1997.3 }, { id: 3, name: 'OPPO R9S', price: 2991.5 }, ] for (var i = 0; i < items.length; i++) { //将数据添加到存储对象中 goods.add(items[i]) } log('添加成功!') } app.getAll = function () { //获取事务对象 var tx = db.transaction('goods', READ_WRITE) //获取存储对象 var goods = tx.objectStore('goods') var request = goods.openCursor() request.onerror = function (e) { log(e) } $('#tabGoods tr:gt(0)').remove() $("#txtId").val(""); $("#txtName").val(""); $("#txtPrice").val(""); request.onsuccess = function (e) { var cursor = e.target.result if (cursor) { //获取对象 var val = cursor.value var tr = $('<tr/>') $('<td/>').html(val.id).appendTo(tr) $('<td/>').html(val.name).appendTo(tr) $('<td/>').html(val.price).appendTo(tr) var del = $("<a href='#'>删除</a>") del.data('id', val.id) del.click(function () { var id = $(this).data('id') //获取事务对象 var tx = db.transaction('goods', READ_WRITE) //获取存储对象 var goods = tx.objectStore('goods') //删除主键为2的数据对象 var request = goods.delete(id) var that = $(this) request.onsuccess = function (e) { log(e.target.result) log(e.target) that.closest('tr').remove() } }) var edit = $("<a href='#'> | 编辑</a>") edit.data('obj', val) edit.click(function () { var obj = $(this).data('obj') $('#txtId').val(obj.id) $('#txtName').val(obj.name) $('#txtPrice').val(obj.price) app.currentObj = obj }) $('<td/>').append(del).append(edit).appendTo(tr) $('#tabGoods').append(tr) log(val) cursor.continue() } } } app.init() })() </script> </body> </html>
IndexedDB示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> .tab{ width: 100%; border: 1px solid blue; border-collapse: collapse; } .tab td,.tab th{ border: 1px solid blue; border-collapse: collapse; height: 30px; line-height: 30px; } </style> </head> <body> <div id="app"> <table border="0" cellspacing="0" cellpadding="0" class="tab"> <tr> <th>编号</th> <th>姓名</th> <th>操作</th> </tr> <tr v-for="(obj,index) in userList" :key="obj.id"> <td>{{obj.id}}</td> <td>{{obj.name}}</td> <td> <a href="#" @click="del(obj)">删除</a> </td> </tr> </table> <fieldset> <legend>用户信息</legend> <p> <label for="id">编号:</label> <input type="text" id="id"/> </p> <p> <label for="name">姓名:</label> <input type="text" id="name"/> </p> <p> <button type="button" onclick="addUser()">添加</button> </p> </fieldset> <p> <h2 id="msg"></h2> </p> </div> <script src="../js/vue/vue.js"></script> <script> //数据库 let db; //1如果没有数据库就创建,有就打开 function initDb() { //打开或创建名为gomall的数据库,并指定版本为3 let request = indexedDB.open("nfit", 3); //成功时的回调 request.onsuccess = function (e) { //获得成功打开的数据库实例 db = e.target.result; log("打开数据库成功!"); loadData(); }; //失败时的回调 request.onerror = function (e) { log("打开数据库失败"); }; //2初始化事件,更新版本与创建数据库时会执行 request.onupgradeneeded = function (e) { log("初始化事件"); //创建名称为goods的对象存储空间,指定id为主键,只能在onupgradeneeded事件中创建 db=e.target.result; db.createObjectStore("users", { keyPath: "id" }); log("创建对象存储空间成功!"); }; } if(!db){ initDb(); } function loadData(){ } //获得用户存储对象 function getUsersOS(){ const tran=db.transaction("users","readwrite"); const users=tran.objectStore("users"); return users; } //添加用户 const id=document.querySelector("#id"); const name=document.querySelector("#name"); function addUser(){ const users=getUsersOS(); users.add({id:id.value,name:name.value}); log("添加成功!"); } function log(msg){ document.getElementById("msg").innerHTML+=msg+"<br/>" } function loadData(){ const users=getUsersOS(); const req=users.openCursor(); req.onsuccess=function(e){ const cursor=e.target.result; if(cursor){ vm.userList.push(cursor.value); cursor.continue(); } } } const vm=new Vue({ el:"#app", data:{ userList:[] }, methods: { del(obj){ alert(obj.id); } }, }) </script> </body> </html>