因为浏览器存在各类限制,最好将整个cookie长度限制在4095B之内。javascript
cookie由浏览器保存的如下几块信息构成:css
名称: cookie的名称必须是通过URL编码后的字符串。 虽然它是不区分大小写的, 可是实际应用时建议把它看成区分大小写来使用。html
值: cookie中字符串值, 也必须是通过URI编码的字符串。java
域: 表示cookie对于哪一个域有效。ios
路径: cookie是针对域中的哪一个目录生效。web
失效时间: 表示cookie失效时间的时间戳, 它是GMT格式的日期。 将该事件设置小于当前时, 就至关于删除了cookie。chrome
安全标识: 指定该标识后, 只有使用SSL请求链接的时候cookie才会发送到服务器。 secure标识是cookie中惟一一个非键值对的部分, 它只包含一个secure单词。数据库
使用分号加空格分开各个信息构成:windows
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: CookieName=CookieValue; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com Other-header: other-header-value
或使用安全标志:数组
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: CookieName=CookieValue; expires=Mon, 22-Jan-07 07:10:24 GMT; domain=.wrox.com; path=/; secure //在这里! Other-header: other-header-value
在JavaScript中能够经过
document.cookie
能够读取当前域名下的cookie, 是用分号隔开的键值对构成的字符串。 相似于name = aa;
age = 15;
注意全部的键值对名称和值都是通过encodeURIComponent()
编码的, 使用时要进行解码。
当给document.cookie赋值时, 不会直接覆盖现有的cookie, 而是会追加一个新的cookie。 例如:
document.cookie = "a=1"; //执行后会看到新增了一个cookie。
如:
document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Oliver"); console.log(document.cookie); //name=Oliver;
若是给cookie赋值则会增长一个cookie:
document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Oliver"); document.cookie = encodeURIComponent("age") + "=" + encodeURIComponent("18"); console.log(document.cookie); //name=Oliver; age=18
如下函数是经常使用的cookie读写删除方法:
var CookieUtil = { //根据key读取cookie get: function(name) { //注意对键编码 var cookieName = encodeURIComponent(name) + "=", cookieStart = document.cookie.indexOf(cookieName), cookieValue = null, cookieEnd; //找到cookie键 if (cookieStart > -1) { //键后面第一个分号位置 cookieEnd = document.cookie.indexOf(";", cookieStart); if (cookieEnd == -1) { cookieEnd = document.cookie.length; } //cookie值解码 cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)); } return cookieValue; }, //设置cookie set: function(name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value); //失效时间,GMT时间格式 if (expires instanceof Date) { cookieText += "; expires=" + expires.toGMTString(); } if (path) { cookieText += "; path=" + path; } if (domain) { cookieText += "; domain=" + domain; } if (secure) { cookieText += "; secure"; } document.cookie = cookieText; }, //删除cookie,保持相同的键、域、路径、安全选项,而后设置失效时间便可 unset: function(name, path, domain, secure) { this.set(name, "", new Date(0), path, domain, secure); } };
能够像下面这样使用上述方法:
设置cookie:
CookieUtil.set("name","Oliver"); CookieUtil.set("age","18"); console.log(document.cookie); //name=Oliver; age=18
读取cookie的值:
console.log(CookieUtil.get("name")); //Oliver
删除cookie:
CookieUtil.unset("name"); // console.log(document.cookie); //age=18
设置cookie路径、域等:
CookieUtil.set("name","Oliver","/","localhost",null,false);
不然使用下面的代码建立和删除cookie:
//新cookie document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Oliver"); //删除上面建立的cookie document.cookie = encodeURIComponent("name") + "=" + encodeURIComponent("Oliver") + "; expires=" + new Date(0);
举例
css:
<style type="text/css"> *{ margin: 0; padding: 0; } div#message { border: 1px solid #ccc; min-height: 3em; background-color: #33CCFF; margin: 0; } p#content{ color:white; font-size: 2em; margin:0.3em; font-family: monospace; display: inline-block; float: left; /*border: 1px solid gray;*/ } a#notShow { width: 2em; display: block; height: 1em; float: right; /*border: 1px solid gray;*/ } </style>
DOM:
<div id="message"> <p id="content">welcome to my site!</p> <a href="" id="notShow">关闭</a> </div>
js:
<script type="text/javascript"> //CookieUtil函数 var CookieUtil = {...} //获取元素 var messageDiv = document.getElementById("message"); var notShowBtn = document.getElementById("notShow"); //点击关闭按钮新建cookie并关闭banner notShowBtn.onclick = function () { event.preventDefault(); CookieUtil.set("hideMessage", "hide"); messageDiv.style.display = "none"; }; //检查cookie当存在则关闭banner window.onload = function () { if (CookieUtil.get("hideMessage")) { messageDiv.style.display = "none"; } }; </script>
因为浏览器cookie数量是有限制的,为了减小cookie数量可使用子cookie的方式。在一个cookie值中使用相似查询字符串的格式能够存储多组键值对,这样就没必要每一个键值对都占用一个cookie了。子cookie值举例:
name=name1=value1&name2=value2
获取全部子cookie并将它放在一个对象中返回,对象的属性名为子cookie名称,对象的属性值为子cookie的值。
getAll: function(name) { var cookieName = encodeURIComponent(name) + "=", cookieStart = document.cookie.indexOf(cookieName), cookieValue = null, cookieEnd, subCookies, i, parts, result = {}; if (cookieStart > -1) { cookieEnd = document.cookie.indexOf(";", cookieStart) if (cookieEnd == -1) { cookieEnd = document.cookie.length; } //取出cookie字符串值 cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd); if (cookieValue.length > 0) { //用&将cookie值分隔成数组 subCookies = cookieValue.split("&"); for (i = 0, len = subCookies.length; i < len; i++) { //等号分隔出键值对 parts = subCookies[i].split("="); //将解码后的兼职对分别做为属性名称和属性值赋给对象 result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } return result; } } return null; }
get()获取单个子cookie。
get: function(name, subName) { //获取全部子cookie var subCookies = this.getAll(name); if (subCookies) { //从属性中获取单个子cookie return subCookies[subName]; } else { return null; } }
setAll设置整个cookie
setAll: function(name, subcookies, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + "=", subcookieParts = new Array(), subName; //遍历子cookie对象的属性 for (subName in subcookies) { //要先检测属性名 if (subName.length > 0 && subcookies.hasOwnProperty(subName)) { //属性名和属性值编码后=链接为字符串,并放到数组中 subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName])); } } if (subcookieParts.length > 0) { //用&链接子cookie串 cookieText += subcookieParts.join("&"); if (expires instanceof Date) { cookieText += "; expires=" + expires.toGMTString(); } if (path) { cookieText += "; path=" + path; } if (domain) { cookieText += "; domain=" + domain; } if (secure) { cookieText += "; secure"; } } else { cookieText += "; expires=" + (new Date(0)).toGMTString(); } //设置整个cookie document.cookie = cookieText; }
set设置单个子cookie
set: function(name, subName, value, expires, path, domain, secure) { //获取当前cookie对象 var subcookies = this.getAll(name) || {}; //单个cookie对应的属性替换 subcookies[subName] = value; //从新设置cookie this.setAll(name, subcookies, expires, path, domain, secure); }
删除整个cookie, 将失效时间设置为过时日期便可。
unsetAll: function(name, path, domain, secure) { this.setAll(name, null, new Date(0), path, domain, secure); }
删除单个子cookie,须要先获取全部子cookie对象,而后删除子cookie对应的属性,最后再将子cookie对象从新设置回去。
unset: function(name, subName, path, domain, secure) { //获取当前cookie对象 var subcookies = this.getAll(name); if (subcookies) { //删除子cookie对应的属性 delete subcookies[subName]; //从新设置cookie this.setAll(name, subcookies, null, path, domain, secure); } }
var SubCookieUtil = { getAll: function(name) { var cookieName = encodeURIComponent(name) + "=", cookieStart = document.cookie.indexOf(cookieName), cookieValue = null, cookieEnd, subCookies, i, parts, result = {}; if (cookieStart > -1) { cookieEnd = document.cookie.indexOf(";", cookieStart) if (cookieEnd == -1) { cookieEnd = document.cookie.length; } //取出cookie字符串值 cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd); if (cookieValue.length > 0) { //用&将cookie值分隔成数组 subCookies = cookieValue.split("&"); for (i = 0, len = subCookies.length; i < len; i++) { //等号分隔出键值对 parts = subCookies[i].split("="); //将解码后的兼职对分别做为属性名称和属性值赋给对象 result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } return result; } } return null; }, get: function(name, subName) { //获取全部子cookie var subCookies = this.getAll(name); if (subCookies) { //从属性中获取单个子cookie return subCookies[subName]; } else { return null; } }, setAll: function(name, subcookies, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + "=", subcookieParts = new Array(), subName; //遍历子cookie对象的属性 for (subName in subcookies) { //要先检测属性名 if (subName.length > 0 && subcookies.hasOwnProperty(subName)) { //属性名和属性值编码后=链接为字符串,并放到数组中 subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName])); } } if (subcookieParts.length > 0) { //用&链接子cookie串 cookieText += subcookieParts.join("&"); if (expires instanceof Date) { cookieText += "; expires=" + expires.toGMTString(); } if (path) { cookieText += "; path=" + path; } if (domain) { cookieText += "; domain=" + domain; } if (secure) { cookieText += "; secure"; } } else { cookieText += "; expires=" + (new Date(0)).toGMTString(); } //设置整个cookie document.cookie = cookieText; }, set: function(name, subName, value, expires, path, domain, secure) { //获取当前cookie对象 var subcookies = this.getAll(name) || {}; //单个cookie对应的属性替换 subcookies[subName] = value; //从新设置cookie this.setAll(name, subcookies, expires, path, domain, secure); }, unsetAll: function(name, path, domain, secure) { this.setAll(name, null, new Date(0), path, domain, secure); }, unset: function(name, subName, path, domain, secure) { //获取当前cookie对象 var subcookies = this.getAll(name); if (subcookies) { //删除子cookie对应的属性 delete subcookies[subName]; //从新设置cookie this.setAll(name, subcookies, null, path, domain, secure); } } };
举例:
获取cookie:
//若cookie为document.cookie=data=name=Oliver&book=jsbook; document.cookie = "data" + "=" + "name" + "=" + "Oliver" + "&" + "book" + "=" + "jsbook"; //取得所有子cookie var data = SubCookieUtil.getAll("data"); console.log(data.name); //Oliver console.log(data.book); //jsbook //获取单个子cookie data = SubCookieUtil.get("data", "name"); console.log(data); //Oliver console.log(document.cookie); //data=name=Oliver&book=jsbook
设置cookie:
//若cookie为document.cookie=data=name=Oliver&book=jsbook; document.cookie = "data" + "=" + "name" + "=" + "Oliver" + "&" + "book" + "=" + "jsbook"; //设置两个cookie SubCookieUtil.set("data", "name", "Nicholas"); SubCookieUtil.set("data", "book", "HTMLreference") console.log(document.cookie); //data=name=Nicholas&book=HTMLreference //设置所有子cookie SubCookieUtil.setAll("data", { name: "Troy", book: "JSON"}); console.log(document.cookie); //data=name=Troy&book=JSON //修改部分子cookie SubCookieUtil.set("data", "name", "Oli"); console.log(document.cookie); //data=name=Oli&book=JSON
删除cookie:
//若cookie为document.cookie=data=name=Oliver&book=jsbook; document.cookie = "data" + "=" + "name" + "=" + "Oliver" + "&" + "book" + "=" + "jsbook"; //删除部分子cookie SubCookieUtil.unset("data", "name"); console.log(document.cookie); //data=book=jsbook //删除所有cookie SubCookieUtil.unsetAll("data"); console.log(document.cookie); //[This site has nos.]
举例:
多个提醒banner的“再也不提醒功能”存在同一个cookie中
css部分:
{
margin: 0; padding: 0;
}
div#message {
border: 1px solid #ccc; background-color: #33CCFF; margin: 0;
}
p.content {
color: white; font-size: 2em; margin: 0.3em; font-family: monospace; display: block;
}
a.notShow {
width: 2em; display: block; float: right;
}
dom部分:
<div id="message"> <p class="content">welcome to my site!<a href="" class="notShow">shotdown</a></p> <p class="content">click to close this banner.<a href="" class="notShow">close</a></p> </div>
js部分:
//获取元素 var notShowBtn = document.getElementsByClassName("notShow"); //点击关闭按钮新建cookie并关闭banner var notShowBtnList = []; var hiddenElementCount = 0; for (var i = 0, len = notShowBtn.length; i < len; i++) { notShowBtnList.push(i); }; notShowBtnList.forEach(function(element, index) { notShowBtn[element].onclick = function() { event.preventDefault(); var value = "content" + element; SubCookieUtil.set("hideMessage", value, "hide"); notShowBtn[element].parentNode.style.display = "none"; }; }); //检查cookie当存在则关闭banner window.onload = function() { notShowBtnList.forEach(function(element, index) { var value = "content" + element; if (SubCookieUtil.get("hideMessage", value)) { notShowBtn[element].parentNode.style.display = "none"; } }); };
用如下代码:
<div style="behavior:url(#default#userData)" id="dataStore"></div>
而后利用setAttribute()方法保存数据
再调用save()
方法保存到指定的数据空间
下一次页面载入后就可使用load()
方法读取数据
最初的Web Storage规范包含了两种对象的定义:sessionStorage
和globalStorage
,这两种都是在windows对象属性中存在的。
该类型提供最大的存储空间来存储名值对,有一下方法:
clear()
删除全部值;
key(index)
得到index处的名字;
getItem(name)
根据指定的name获取对应的值;
removeItem(name)
删除name处的名值对;
setItem(name, value)
为指定的name设置一个对应的值;
其中后三中能够直接调用也能够经过Storage对象间接调用。
还可使用length属性来判断有多少名值对儿存放在Storage对象中,但没法判断对象中全部数据的大小,不过IE8提供了一个remainingSpace属性,用于获取还可使用的存储空间的字节数。
sessionStorage对象存储特定于某个会话的数据,也就是该数据只保持到该浏览器关闭。存储在sessionStorage中的数据能够跨越页面刷新而存在,同时若是浏览器支持,浏览器崩溃并重启以后依然可用(Firefox和WebKit都支持,IE则不行)。存储在sessionStorage中的数据只能由最初给定对象存储数据的页面访问到,因此对页面应用有限制。
可使用setItem()或者直接设置新的属性来存储数据。下面是这两种方法的例子。
sessionStorage.setItem("name", "Oliver"); //使用方法存储数据 sessionStorage.book = "JSON.com"; //使用属性存储数据
Firefox和Webkit实现了同步写入,因此添加到存储空间中的数据是马上被提交的。而IE的实现则是异步写入数据,因此在设置数据和将数据实际写入磁盘之间可能有一些延迟。
在IE8中能够强制把数据写入磁盘:在设置新数据以前使用
begin()
方法
而且全部设置完成以后调用
commit()
方法
看如下例子:
sessionStorage.begin(); sessionStorage.name = "oli"; sessionStorage.commit();
getItem()
可使用getItem()或者经过直接访问属性名来获取数据。
sessionStorage.name = "oli"; sessionStorage.age = 18; var val = sessionStorage.name; var val_1 = sessionStorage.age; console.log(val); //oli console.log(val_1); //18
key()
方法
经过结合length属性和key()方法来迭代sessionStorage中的值
for (var i = 0, len = sessionStorage.length; i < len; i++) { var key = sessionStorage.key(i); var val = sessionStorage.getItem(key); console.log(key + "=" + val); };
for-in
方法
还可使用for-in循环来迭代sessionStorage中的值:
for (key in sessionStorage) { var value = sessionStorage.getItem(key); console.log(key + "=" + value); }
removeItem()
方法
要从sessionStorage中删除数据,可使用delete操做符删除对象属性,也可调用removeItem()方法。
sessionStorage.name = "oli"; sessionStorage.age = 18; sessionStorage.removeItem("name"); delete sessionStorage.age;
sessionStorage对象应该主要用于仅针对会话的小段数据的存储。
Firefox 2中实现了globalStorage对象。做为最初的Web Storage规范的一部分,这个对象的目的是跨越会话存储数据,但有特定的访问限制。要使用globalStorage,首先要指定哪些域能够访问该数据。能够经过方括号标记使用属性来实现,如如下例子所示。
globalStorage["test.com"].name = "Oliver"; var name = globalStorage["test.com"].name;
其中,globalStorage不是Storage的实例,globalStorage["test.com"]才是
某些浏览器容许更加宽泛的访问限制,好比只根据顶级域名进行限制或者容许全局访问,以下面例子所示:
//存储数据,任何人均可以访问——不要这样作! globalStorage[""].name = "Nicholas"; //存储数据,可让任何以.net结尾域名访问——不要这样作! globalStorage["net"].name = "Nicholas";
要避免使用这种可宽泛访问的数据存储
对globalStorage空间的访问,是一句发起请求的页面的域名、协议和端口来限制的。例如,若是使用HTTPS协议在w3cmm.com中存储了数据,那么经过HTTP访问的w3cmm.com的页面就不能访问该数据。
globalStorage的每一个属性都是Storage的实例。所以,能够像以下代码中这样使用。
globalStorage["www.test.com"].name = "Nicholas"; globalStorage["www.test.com"].book = "Professional JavaScript"; globalStorage["www.test.com"].removeItem("name"); var book = globalStorage["www.test.com"].getItem("book");
若是你事先不能肯定域名,那么使用location.host
做为属性名比较安全。例如:
globalStorage[location.host].name = "Nicholas"; var book = globalStorage[location.host].getItem("book");
localStorage对象在修订过的HTML5规范中做为持久保存在客户端数据的方案取代了globalStorage。要访问同一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口上。这至关于globalStorage[location.host]。
因为localStorage是Storage的实例,因此能够像使用sessionStorage同样来使用它。下面是一些例子。
localStorage.setItem("name", "Oliver"); localStorage.book = "JSON"; var book = localStorage.book; var name = localStorage.getItem("name"); localStorage.removeItem("name"); delete localStorage.book; localStorage.clear();
对Storage对象进行任何修改,都会在文档上触发storage事件。当经过属性或setItem()方法保存数据,使用delete操做符或removeItem()删除数据,或着调用clear()方法时,都会发生该事件。这个事件的event对象有如下属性。
domain
:发生变化的存储空间的域名。
key
:设置或着删除的键名。
newValue
:若是是设置值,则是新值;若是是删除键,则是null。
oldValue
:键被更改以前的值。
以下代码:
var EventUtil = { addHandler: function(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } } }; EventUtil.addHandler(document, "storage", function(event) { alert("Storage changed for " + event.domain); });
与其它客户端数据存储方案相似,Web Storage一样也有限制。这些限制因浏览器而异。通常来讲,对存储空间大小的限制都是以每一个来源(协议、域和端口)为单位的。
对于localStorage而言,大多数桌面浏览器会设置每一个来源5MB的限制。Chrome和Safari对每一个来源的限制是2.5MB。而ios版Safari和Android版Webkit的限制也是2.5MB。
对sessionStorage的限制也是因浏览器而异。有的浏览器对sessionStorage的大小没有限制,但Chrome、Safari、ios版Safari和Android版Webkit都有限制,也都是2.5MB。IE8+和Opera对sessionStorage的限制是5MB。
推荐一篇文章使用 IndexedDB,来自MDN,连接地址:https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB
Indexed Database API 简称为IndexedDB,是在浏览器中保存结构化数据的一种数据库。
设计思想是创建一套API,方便保存和读取JavaScript对象,同时还支持查询和搜索。
该操做彻底是异步进行的。
差很少每一次IndexedDB操做,都须要注册onerror或onsuccess事件处理程序,以确保适当地处理结果。
在使用时须要加上浏览器提供商前缀:
var indexDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
indexDB.open()
IndexedDB就是一个数据库,IndexedDB最大的特点是使用对象保存数据,而不是使用表来保存数据。
使用IndexedDB前首先要打开它,即把要打开的数据库名传给indexDB.open()
。若是传入的数据库已经存在,就会发送一个打开它的请求;若是传入的数据库还不存在,就会发送一个建立并打开它的请求;
总之,调用indexedDB.open()会返回一个IDBRequest对象,在这个对象上能够添加onerror和onsuccess事件处理程序。
var request, database; request = indexedDB.open("Base"); request.onerror = function () { console.log(event.target.errorCode); }; request.onsuccess = function () { database = event.target.result; console.log(event.target.result); //IDBDatabase {} 指向数据库实例对象 console.log(event.target); //IDBOpenDBRequest {} 指向request对象 };
ABORT_ERR
错误码8 : A request was aborted,
example, through a call to IDBTransaction.abort.
CONSTRAINT_ERR
错误码4 : A mutation operation in the transaction failed because a constraint was not satisfied.For example, an object, such as an object store or index, already exists and a request attempted to create a new one.
DATA_ERR
错误码5 : Data provided to an operation does not meet requirements.
NON_TRANSIENT_ERR
错误码2 : An operation was not allowed on an object.Unless the cause of the error is corrected, retrying the same operation would result in failure.
NOT_ALLOWED_ERR
错误码6 :
An operation was called on an object where it is not allowed or at a time when it is not allowed.It also occurs
if a request is made on a source object that has been deleted or removed.
More specific variants of this error includes: TRANSACTION_INACTIVE_ERR and READ_ONLY_ERR.
NOT_FOUND_ERR
错误码3 : The operation failed because the requested database object could not be found;
example, an object store did not exist but was being opened.
QUOTA_ERR
错误码11 : Either there 's not enough remaining storage space or the storage quota was reached and the user declined to give more space to the database.
READ_ONLY_ERR
错误码9 : A mutation operation was attempted in a READ_ONLY transaction.
TIMEOUT_ERR
错误码10 : A lock
the transaction could not be obtained in a reasonable time.
TRANSACTION_INACTIVE_ERR
错误码7 : A request was made against a transaction that is either not currently active or is already finished.
UNKNOWN_ERR
错误码1 : The operation failed
reasons unrelated to the database itself, and it is not covered by any other error code--
for example, a failure due to disk IO errors.
VER_ERR
错误码12 : A request to open a database with a version lower than the one it already has.This can only happen with IDBOpenDBRequest.
indexedDB.setVersion()
设置版本号
indexedDB.version
获取版本号
默认状况下,IndexedDB数据库是没有版本号的。最好一开始就调用setVersion()
方法为数据库指定一个版本号(传入一个表示版本号的字符串)。
目前就chrome浏览器,版本号方法已再也不适用,另外,建立database后chrome浏览器自动设置版本号为"1":
var request, database; request = indexedDB.open("Base"); request.onerror = function () { console.log(event.target.errorCode); }; request.onsuccess = function () { database = event.target.result; console.log(database.setVersion); //undefined console.log(database.version); //1 };
要更新数据库的 schema,也就是建立或者删除对象存储空间,须要实现 onupgradeneeded 处理程序,这个处理程序将会做为一个容许你处理对象存储空间的 versionchange 事务的一部分被调用。
request.onupgradeneeded
如:
// 该事件仅在较新的浏览器中被实现 request.onupgradeneeded = function(event) { // 更新对象存储空间和索引 .... };
如需设置数据库版本号,用下面方法:(旧)
var request, database; request = indexedDB.open("Base"); request.onerror = function() { console.log(event.target.errorCode); }; request.onsuccess = function() { database = event.target.result; if (database.version != "1.0") { request = database.setVersion("1.0"); request.onerror = function() { console.log(event.target.errorCode); }; request.onsuccess = function() { console.log("database name: " + database.name + "; version: " + database.version); }; } else { console.log("database name: " + database.name + "; version: " + database.version); //database name: Base; version: 1 }; };
创建完数据库链接之后,就要建立对象存储空间。
键的提供能够有几种不一样的方法,这取决于对象存储空间是使用 key path 仍是 key generator。
若要保存用户记录由用户名、密码组成,那么保存一条记录的对象应该以下所示:
var userData = [{ username: "007", firstName: "James", lastName: "Bond", password: "foo" }, { username: "005", firstName: "Oliver", lastName: "Young", password: "boo" }];
其中username为键(keyPath),这个应该是全局惟一的(表明一个user)。
下面是为了保存用户记录而建立对象存储空间的示例:
var request = indexedDB.open("Base", 2); //注意填写版本号 request.onerror = function() { console.log(event.target.errorCode); }; request.onupgradeneeded = function() { var database = event.target.result; var store = database.createObjectStore("users", { keyPath: "username" }); //根据username建立一个名为users的对象集合(表) };
得到了对象存储空间的引用以后,就可使用
add()
或put()
方法向其中添加数据
这两个方法都接收一个参数,即要保存的对象,而后这个对象就会被保存到存储空间中。
这两个方法的区别在于,若是空间中已经包含了键值相同的对象:add()会返回错误;put()则会重写原有对象;
可使用下面的代码初始化(add()方法)存储空间:
request.onupgradeneeded = function() { var database = event.target.result; var store = database.createObjectStore("users", { keyPath: "username" }); for (var i in userData) { store.add(userData[i]); } };
for-in循环可用下面代码代替:
//users中保存着一批的用户对象 var i = 0, request, requests[], len = users.length; while (i < len) { request = store.add(users[i++]); request.onerror = function() { // 错误处理 }; request.onsuccess = function() { // 成功 }; requests.push(request); }
如下为完整的建立数据库和存储空间以及初始化数据的代码例子:
// //数据在这里userData // var userData = [{ username: "007", //username惟一 firstName: "James", lastName: "Bond", password: "foo" }, { username: "005", firstName: "Oliver", lastName: "Young", password: "boo" }]; // //建立数据库Alldata // var request = indexedDB.open("allData"); request.onerror = function() { console.log(event.target.errorCode); }; request.onsuccess = function() { var database = event.target.result; console.log("数据库已经建立:" + database.name + "; 版本号:" + database.version); }; // //建立存储空间(表)users,并初始化数据 // var request = indexedDB.open("allData", 2); request.onupgradeneeded = function() { var database = event.target.result; //建立存储空间 var store = database.createObjectStore("users", { keyPath: "username" }); //添加数据 for (var i in userData) { store.add(userData[i]); } };
另外,下面是数据库结构:
indexedDB |-allData数据库 |-users存储空间(表) |-(userData数据)
在数据库对象上调用transaction()方法就能够建立事务。任什么时候候,想要读取或修改数据,都要经过事务来组织全部的操做。
下面的代码保证只加载users存储空间中的数据,以便经过事务进行访问:
var transaction = database.transaction("users");
要访问多个对象存储空间,能够传入字符串数组:
var transaction = db.transaction(["users", "anotherStore"]);
上面的两个事务都是以只读的方式访问数据。要修改访问方式,必须在建立事务时传入第二个参数。
默认都是以只读的方式访问数据,要修改访问方式,必须传入第二个参数,这个参数表示访问模式:
READ_ONLY(0)
表示只读;
READ_WRITE(1)
表示读写;
VERSION_CHANGE(2)
表示改变
注意: 旧版实验性的实现使用不建议使用的常量 IDBTransaction.READ_WRITE 而不是 "readwrite"。
因此如今通常表示读写应该传入字符串"readwrite"
其中,上面三个访问模式已经被改成:
"readonly"
只读;
"readwrite"
读写;
以下:
var request = database.transaction("users", "readonly");
objectStore()
方法,并传入存储空间的名称,就能够访问特定的存储空间了。
就能够:
使用add()和put()方法添加数据;
使用get()能够取得值;
使用delete()能够删除对象;
使用clear()能够删除全部对象;
get()和delete()方法都接收一个对象键做为参数。全部的这5个方法都会返回一个新的请求对象。例如:
var request = database.transaction("users", "readonly").objectStore("users").get("007"); request.onsuccess = function () { var result = event.target.result; //"James" console.log(result.firstName); }; var request = database.transaction("users", "readwrite").objectStore("users").delete("005");
事务自己也有事件处理程序:onerror
和oncomplete
。
这两个事件能够提供事务级的状态信息。
注意:经过oncomplete事件的事件对象访问不到get()请求返回的任何数据,必须在onsuccess事件处理程序中才能访问到。
如下为实例:
var userData = [{ username: "007", //username惟一 firstName: "James", lastName: "Bond", password: "foo" }, { username: "005", firstName: "Oliver", lastName: "Young", password: "boo" }]; //建立数据库Alldata var request = indexedDB.open("allData"); request.onerror = function() { console.log(event.target.errorCode); }; request.onsuccess = function() { var database = event.target.result; console.log("数据库已经建立:" + database.name + "; 版本号:" + database.version); }; //建立存储空间(表)users并初始化数据 var request = indexedDB.open("allData", 2); var database; request.onupgradeneeded = function() { database = event.target.result; //建立存储空间 var store = database.createObjectStore("users", { keyPath: "username" }); //添加数据 for (var i in userData) { store.add(userData[i]); } }; var newPerson = { username: "001", firstName: "Troy", lastName: "Ruby", password: "hello" }; //事务 var request = indexedDB.open("allData", 2); request.onsuccess = function () { var database = event.target.result; // var request = database.transaction("users").objectStore("users").add(newPerson); //报错,由于只读,须要设置为"readwrite" var request = database.transaction("users", "readwrite").objectStore("users").add(newPerson); var request = database.transaction("users", "readonly").objectStore("users").get("007"); request.onsuccess = function () { var result = event.target.result; //"James" console.log(result.firstName); }; var request = database.transaction("users", "readwrite").objectStore("users").delete("005"); };
游标就是指向结果集的一个指针,在对象存储空间上调用;在须要检索多个对象的状况下,须要在事务内部建立游标。注意,必须在事务内部建立!
openCursor()
方法能够建立游标。
openCursor()方法返回的也是一个请求对象,也须要为该对象指定onsuccess和onerror事件处理函数
var transaction = database.transaction("users", "readonly").objectStore("users").openCursor();
或者:
var store = db.transaction("users").objectStore("users"), request = store.openCursor(); request.onsuccess = function(event) { // 处理成功 }; request.onerror = function(event) { // 处理失败 };
在前面的onsuccess事件处理程序执行时,能够经过event.target.result
取得存储空间中的下一个对象。
IDBCursor
实例具备如下几个属性:
key
: 对象的键;
value
:实际的对象;
direction
:数值,表示游标走动的方向。
默认是IDBCursor.NEXT(0)
, 表示下一项。
IDBCursor.NEXT_TO_DUPLICATE(1)
, 表示下一个不重复的项;
IDBCursor.PREV(2)
表示前一项;
IDBCursor.PREV_NO_DUPLICATE
表示前一个不重复的项。
primaryKey
:游标使用的键,有多是对象键,也有多是索引键(后面会讨论索引)
要检索某个信息的结果,以下代码:
var transaction = database.transaction("users", "readonly").objectStore("users").openCursor(); transaction.onsuccess = function() { var cursor = event.target.result; if (cursor) { console.log(cursor); //IDBCursorWithValue {}direction: "next"key: "005"primaryKey: "005"source: IDBObjectStorevalue: Object__proto__: IDBCursorWithValue console.log(cursor.value); //Object {username: "005", firstName: "Oliver", lastName: "Young", password: "boo"} console.log(cursor.key); //005 console.log("Key: " + cursor.key + "; Value: " + JSON.stringify(cursor.value)); //Key: 005; Value: {"username":"005","firstName":"Oliver","lastName":"Young","password":"boo"} } };
注意:返回的cursor.value是一个对象,必要时须要用到JSON.stringify方法
update()
方法更新记录
调用update()方法可使用指定的对象更新当前游标的value:
var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(); transaction.onsuccess = function() { var cursor = event.target.result; if (cursor) { if (cursor.key == "005") { console.log(cursor.value.firstName); //游标在第一个位置”005“因此他的firstName就是"Oliver"; 刷新浏览器,显示的结果则为Oli var value = cursor.value; //更改当前游标所指的对象("005")的firstName为"Oli"; value.firstName = "Oli"; var updateRequest = cursor.update(value); //使用update方法请求保存更新 updateRequest.onerror = function () { console.log(event.target.errorCode); }; updateRequest.onsuccess = function () { console.log("success"); //success }; } } }; transaction.onerror = function() { console.log(event.target.errorCode); };
delete()
方法删除记录
如:
var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(); transaction.onsuccess = function() { var cursor = event.target.result; if (cursor) { if (cursor.key == "005") { var deleteRequest = cursor.delete(); //请求删除此项 deleteRequest.onerror = function () { // body... }; deleteRequest.onsuccess = function () { // body... }; } } };
注意:若是当前的事务没有修改对象存储空间的权限,update()和delete()会抛出错误。
默认状况下每一个游标只发起一次请求;要想发起另外一次请求,必须调用下面的一个方法:
continue(key)
: 移动到结果集的下一项。参数key是可选的,不指定这个参数,游标移动到下一项;指定这个参数的话,游标会移动到指定键的位置。
advance(count)
: 向前移动count指定的项数。
使用移动游标的方法,能够用来遍历存储空间中的全部项:
var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(); transaction.onsuccess = function() { var cursor = event.target.result; if (cursor) { console.log("Key: " + cursor.key + "; Value: " + JSON.stringify(cursor.value)); cursor.continue(); //移动下一项 } else { console.log("done"); } };
调用continue()会触发另外一次请求,进而再次调用onsuccess处理程序。若是没有更多项能够遍历时,event.target.result的值为null。
键范围由IDBKeyRange的实例表示。
有四中定义键范围的方式:
var onlyrange = IDBKeyRange.only("007"); var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(onlyrange);
将onlyrange变量传入openCursor方法中
第二种定义键范围的方法是指定结果集的下界。下界表示游标开始的位置。
若是想要忽略键为"007"的对象自己,从它的下一个对象开始,能够传入第二个参数true:
var lowerBound = IDBKeyRange.lowerBound("003", true); //第二个参数为true表示不包括003 var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(lowerBound);
第三种定义键范围的方法是指定结果集的上界,也就是指定游标不能超过哪一个键。
若是不想包含键为指定值的对象,一样传入第二个参数true:
var upperBound = IDBKeyRange.upperBound("005", true); //第二个参数为true表示不包括005 var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(upperBound);
使用bound()方法能够同时指定上下界。
这个方法能够接收四个参数:表示下界的键,表示上界的键,可选的表示是否跳过下界的布尔值和可选的表示是否跳过上界的布尔值。
var bound = IDBKeyRange.bound("003", "005", true, false); //第三和第四个参数为true和false表示不包括003包括005 var transaction = database.transaction("users", "readwrite").objectStore("users").openCursor(bound);
openCursor()能够接收两个参数,一个是刚才的IDBKeyRange实例,第二个是表示方向的数值常量,也就是前面讲到的IDBCursor中的常量。
正常状况下,游标都是从存储空间的第一项开始,调用continue()或advance()前进到最后一项。游标的默认方向值是
IDBCursor.NEXT
或IDBCursor.NEXT_NO_DUPLICATE
IDBCursor.next
或IDBCursor.nextunique
也能够建立一个游标,从最后一个对象开始,逐个迭代,直到第一个对象,这时要传入的常量是:
IDBCursor.PREV
或IDBCursor.PREV_NO_DUPLICATE
IDBCursor.prev
或IDBCursor.prevunique
对于有些数据,须要建立多个键,如把用户ID做为主键,以用户名建立索引
首先引用对象存储空间,而后调用
createIndex()
方法
以下:
request.onupgradeneeded = function() { var database = event.target.result; var store = database.createObjectStore("users", { keyPath: "username" }); for (var i in userData) { store.add(userData[i]); } var index = store.createIndex("firstName", "firstName", { unique: false }); //建立名为firstName的索引,属性名为firstName,属性值非惟一 };
index()
方法
以下:
var index = database.transaction("users").objectStore("users").index("firstName"); console.log(index); //IDBIndex对象
openCursor()
方法
以下:
var request = index.openCursor(); request.onsuccess = function () { console.log(event.target.result); //IDBCursorWithValue对象 };
遍历:
var request = index.openCursor(); request.onsuccess = function() { var cursor = event.target.result; //IDBCursorWithValue对象 if (cursor) { console.log(cursor.key + "; " + JSON.stringify(cursor.value)); cursor.continue(); } else { console.log("done"); } // Alice; { // "username": "003", // "firstName": "Alice", // "lastName": "Young", // "password": "boo" // } // (index): 87 James; { // "username": "007", // "firstName": "James", // "lastName": "Bond", // "password": "foo" // } // (index): 87 Oliver; { // "username": "005", // "firstName": "Oliver", // "lastName": "Young", // "password": "boo" // } // (index): 90 done };
openKeyCursor()
方法
调用这个特殊的只返回每条记录主键的游标,这个方法在event.result.key中保存索引键,在event.result.value中保存的则是主键,而不是整个对象
get()
方法
只要传入相应的索引键便可:
var store = db.transaction("users").objectStore("users"); var index = store.index("firstName"); var request = index.get("007");
getKey()
方法
这个方法中event.result.value等于主键的值,而不是整个对象:
var store = db.transaction("users").objectStore("users"); var index = store.index("firstName"); var request = index.getKey("007");
name
:索引的名字
keyPath
:createIndex方法中属性的路径
objectStore
:索引的对象存储空间
unique
:表示索引键是否惟一
indexNames
属性
如:
var indexNames = store.indexNames; for (var i = 0, len = store.indexNames.length; i < len; i++) { var index = store.index(indexNames[i]); console.log(index.name); //firstName //lastName };
deleteIndex()
如:
store.deleteIndex("firstName");
每次打开数据库,都应该指定onversionchange事件处理程序:
request.onsuccess = function() { database = event.target.result; database.onversionchange = function () { database.close(); }; };
当设置version时,指定请求的onblocked事件处理程序也很重要:
var request = database.setVersion("2.0"); request.onblocked = function () { alert("轻先关闭其余标签页后再尝试"); }; request.onsuccess = function () { //处理成功,继续 };
同源
多个限制5MB大小
Firefox不容许本地访问IndexedDB
var userData = [{ username: "007", firstName: "James", lastName: "Bond", password: "foo" }, { username: "005", firstName: "Oliver", lastName: "Young", password: "boo" }, { username: "003", firstName: "Alice", lastName: "Young", password: "boo" }]; var newData = { username: "001", firstName: "Ali", lastName: "Bound", password: "hello" }; var anotherNewData = { username: "001", firstName: "holyshit", lastName: "Bound", password: "hello" }; var dbName = "allData", dbVersion = 1, dbStoreName = "users"; var db; //初始化数据库 function initDb() { console.log("initDb..."); //初始化数据库... var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { db = this.result; console.log("initDb Done."); }; req.onerror = function() { console.log("initDb:", event.target.errorCode); }; req.onupgradeneeded = function() { console.log("initDb.onupgradeneeded"); var store = event.target.result.createObjectStore(dbStoreName, { keyPath: "username" }); //这里不能用db,而是event.target.result; store.createIndex("firstName", "firstName", { unique: false }); store.createIndex("lastName", "lastName", { unique: false }); }; } initDb(); //添加数据 function addData(data) { console.log("add data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readwrite"); var store = transaction.objectStore(dbStoreName); if (Object.prototype.toString.call(data).toString() === "[object Array]") { for (var i in data) { store.add(data[i]); } } else if (Object.prototype.toString.call(data).toString() === "[object Object]") { store.add(data); } else { console.log("adding data: please choose Array or Object."); } }; } addData(userData); //提取数据 function getData(key, callback) { console.log("get data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var data = store.get(key); data.onsuccess = function() { var result = event.target.result; if (result) { callback(result); console.log("get data done."); } }; }; } // getData("003", function (result) { // console.log(result); // }); //删除数据 function deleteData(key) { console.log("delete data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readwrite"); var store = transaction.objectStore(dbStoreName); var data = store.delete(key); data.onsuccess = function() { console.log("delete data done."); }; }; } // deleteData("003"); //清空数据 function clearData() { console.log("delete data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readwrite"); var store = transaction.objectStore(dbStoreName); var data = store.clear(); data.onsuccess = function() { console.log("clear data done."); }; }; } // clearData(); //游标提取数据 function cursorGetData(key, callback) { console.log("cursor get data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var cursor = store.openCursor(); cursor.onsuccess = function() { var cursor = event.target.result; if (cursor.key !== key) { cursor.continue(); } else { var result = cursor.value; callback(result); console.log("cursor get data done."); } }; }; } // cursorGetData("007", function (result) { // console.log(result); // }); //游标修改数据 function cursorUpdateData(key, property, newValue) { console.log("cursor update data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readwrite"); var store = transaction.objectStore(dbStoreName); var cursor = store.openCursor(); cursor.onsuccess = function() { var cursor = event.target.result; if (cursor.key !== key) { cursor.continue(); } else { var target = cursor.value; for (var i in target) { if (i === property) { var value = Object.defineProperty(target, property, { value: newValue }); var updateReq = cursor.update(value); console.log("cursor update data done."); } } } }; }; } // cursorUpdateData("003", "firstName", "Ali"); //游标删除数据 function cursorDeleteData(key) { console.log("cursor delete data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readwrite"); var store = transaction.objectStore(dbStoreName); var cursor = store.openCursor(); cursor.onsuccess = function() { var cursor = event.target.result; if (cursor.key !== key) { cursor.continue(); } else { var deleteReq = cursor.delete(); console.log("cursor delete data done."); } }; }; } // cursorDeleteData("003"); //游标遍历全部项 function cursorTraversalData(callback) { console.log("cursor tarversal data..."); var req = indexedDB.open(dbName, dbVersion); var result = []; req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var cursor = store.openCursor(); cursor.onsuccess = function() { var cursor = event.target.result; if (cursor) { result.push(cursor.value); cursor.continue(); } else { console.log("cursor traversal data done."); if (result.length > 0) { callback(result); } } }; }; } // cursorTraversalData(function (result) { // for (var i in result) { // console.log(JSON.stringify(result[i])); // } // }); //遍历范围内全部项 function boundTraversalData(lower, upper, boo1, boo2, callback) { console.log("bound tarversal data..."); var req = indexedDB.open(dbName, dbVersion); var result = []; req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var bound = IDBKeyRange.bound(lower, upper, boo1, boo2); var cursor = store.openCursor(bound); cursor.onsuccess = function() { var cursor = event.target.result; if (cursor) { result.push(cursor.value); cursor.continue(); } else { console.log("bound traversal data done."); if (result.length > 0) { callback(result); } } }; }; } // boundTraversalData("003", "007", true, true, function(result) { // for (var i in result) { // console.log(JSON.stringify(result[i])); // } // }); //索引中取得对象 function indexGetData(index, key, callback) { console.log("index get data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var target = store.index(index); var data = target.get(key); data.onsuccess = function() { var result = event.target.result; if (result) { callback(result); console.log("index get data done."); } else { console.log("index get data: error output."); } }; }; } // indexGetData("firstName", "Alice", function (result) { // console.log(result); // }); //索引中取得主键 function indexGetKey(index, key, callback) { console.log("index get data..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var target = store.index(index); var data = target.getKey(key); data.onsuccess = function() { var result = event.target.result; if (result) { callback(result); console.log("index get key done."); } else { console.log("index get key: error output."); } }; }; } // indexGetKey("firstName", "Alice", function (result) { // console.log(result); // }); //访问全部索引 function getAllIndex(callback) { console.log("get all index..."); var req = indexedDB.open(dbName, dbVersion); req.onsuccess = function() { var transaction = db.transaction(dbStoreName, "readonly"); var store = transaction.objectStore(dbStoreName); var indexNames = store.indexNames; if (indexNames.length) { for (var i = 0, len = store.indexNames.length; i < len; i++) { var index = store.index(indexNames[i]); callback(index); } } }; } // getAllIndex(function (index) { // console.log(index.name); // console.log(index.keyPath); // console.log(index.objectStore.name); // console.log(index.unique); // });