第19章 Web存储

第19章 Web存储

Web应用的发展,使得客户端存储的用途也越来越多,而实现客户端存储的方式则是多种多样。最简单且兼容性最佳的方案是Cookie,但是作为真正的客户端存储,Cookie存在很多缺陷。HTML5提出了更多解决方案:如果存储复杂的数据,可以使用Web Database,该方法可以像客户端程序一样使用SQL;如果需要存储简单的key/value(键值对)信息,可以使用Web Storage。本章将主要介绍Cookie、Web Storage和Web Database。

【学习重点】

▲ 了解Cookie技术

▲ 正确存取Cookie信息

▲ 使用Web Storage技术存取信息

▲ 使用Web SQL技术存取信息

19.1 Cookie概述

HTTP是一种无序协议,它不会记录或跟踪用户的访问信息。这意味着Web服务器无法直接分辨用户的身份,记忆用户的足迹。不管是老用户,还是新用户,对于Web服务器来说,都是新面孔。这种无序状态能够在一定程度上降低服务器运行的复杂性,提高响应速度,当然也存在很多问题。例如,刚登录完毕,再次回来时就需要重新登录;刚设置的页面环境,如果刷新页面,则还需要重新设置;重复的表单信息,却需要反复输入。

19.1.1 认识Cookie

1995年Netscape 2.0版本浏览器推出,Netscape 2.0开始支持Cookie技术。Cookie是存储在用户系统中的一个文本文件,该文本文件与某个Web服务器域中的某个目录相关联,当用户的浏览器向服务器请求该目录中某个页面时,客户端的Cookie信息就会随着HTTP请求一起发送到服务器端。这样就可以使用JavaScript在客户端设置和检索Cookie信息,同时服务器端也能够读取这些Cookie信息,从而实现跟踪和记录用户访问的目的。

Cookie特别适合完成类似如下的各种工作:

☑ 跟踪访问者的访问次数、最后访问时间和访问者进入站点的路径等。

☑ 记录在线广告点击次数,记录用户是否投票、是否登录、是否选购等信息。

☑ 可以减轻表单输入的繁琐,只要输入一次,在有效时间内用户就不用再重复输入。

☑ 可以统计用户资料,并根据这些信息设计个性化服务。

☑ 可以记录用户的页面设置信息,避免重复配置环境。

由于Web服务器可以通过Cookie观察用户的浏览习惯,从而将用户信息加以细化,以方便站点为不同用户定制信息,实现个性化的服务。因此,Cookie一直是浏览器和服务器开发中钟情的一项技术,也确实为广大互联网用户带来了很大的便利。

19.1.2 Cookie字符串

在JavaScript中,一个Cookie实际就是一个字符串属性。当对Cookie进行读操作时,可以得到一个字符串,这个字符串包含了应用到当前文档的所有Cookie的名称和值。通过设置Cookie的值可以创建、修改或删除一个Cookie。除了名/值对外,每个Cookie都包含4个可选的属性,分别用来定义Cookie的有效期、可见性和安全性。

☑ 所谓有效期,就是Cookie存在的时间,由expires属性设置。在默认情况下,Cookie是临时存在的,当会话结束后就被删除(即浏览器被关闭后或退出Web服务器的域)。如果希望Cookie存在的时间超过一个浏览会话期,可以使用expires属性指定一个终止日期,这样浏览器就会把Cookie保存到一个本地文件中,以方便下次访问时读取。一旦超过了终止日期,那个Cookie就会自动从Cookie文件中删除。

☑ 可见性包括Cookie的路径(path)和域(domain)。path属性设置Cookie的有效路径(或者说可以访问的目录),默认为当前文件所在的目录。domain属性可以设置Cookie关联的域,即设置可以访问Cookie的站点,默认为当前站点。

☑ 安全性是指Cookie信息在客户端与服务器端传递时是否采取安全策略,它主要通过secure属性来设置,secure是一个布尔值,可以设置Cookie信息的传输方式。注意,expires、path、domain和secure都是Cookie的属性,而不是JavaScript对象的属性。

一个Cookie字符串应该由下面几部分组成。

☑ name(名称):每一个Cookie都有一个名称(具有唯一性,即一个Cookie中不能够包含多个名称)。这个名称可以包含字母、数字和下划线。与JavaScript变量不同,Cookie的名称是不区分大小写的。例如,name和Name是相同的。为了安全起见,建议保持大小写习惯,因为有些服务器端软件区分大小写。

☑ value(值):具体的Cookie信息,为了防止值中包含特殊的字符,建议使用JavaScript方法对其进行编码,当然在读取时也不要忘记解码。Cookie名称和值的字节数不能超过4096个字符(即4KB)。

☑ domain(域):不同网站只能够访问自己域内的Cookie信息,网站之间不能够互访。不过,用户可以通过设置域以实现网站之间的相互访问。

☑ path(路径):不同的Cookie信息只能够在指定的目录下有效,其他目录文件是禁止访问的。此时,可以通过该属性来设置有效访问的路径。

☑ expires(失效日期):Cookie信息仅是暂时存在的,不过通过设置expires属性可以延长Cookie的生存期。

☑ secure(安全标识):布尔值,当为true时,表示以安全方式传递Cookie信息,否则根据常规方式传递Cookie信息。

19.2 存取Cookie

用户可以使用JavaScript脚本操纵Cookie。本节将讲解如何使用JavaScript操纵Cookie。

19.2.1 写入Cookie

JavaScript在Document对象中定义了Cookie属性,该属性能够接收Cookie字符串信息,并把这些信息写入到客户端Cookie文件信息列表中。

     var c ="name=zhu";
     document.cookie=c;                    //写入Cookie信息

【示例】使用cookie存储信息。Cookie字符串是一组名/值对,名称和值之间以等号相连,名/值对之间使用分号进行分隔。值中不能够包含分号、逗号和空白符。如果值中包含特殊字符,可以使用escape()函数对其进行编码,当然在读取Cookie信息时也必须使用unscape()函数进行解码。

19.2.2 设置Cookie有效期

在默认状态下,Cookie信息只能在当前的Web浏览会话期(当前Web浏览窗口)中有效并存在,一旦退出浏览器,这些Cookie信息就会被自动删除。如果希望Cookie信息在客户端能够长久存在,可以通过expires属性来实现。

expires表示“有效期”的意思。设置expires属性的方法是:把字符串expires=date附加到Cookie属性值后面即可,语法格式如下:

     name=value; expires=date

date采用的日期规范必须是格林威治时间(GMT),即按如下格式设置:

     Sun, 30 Apr 2017 00:00:00 UTC

不过调用Date对象的Date.toGMTString()方法可以把时间对象快速转换为GMT格式。

【示例】本示例将创建一个有效期为一个月的Cookie信息。如果在本地系统中打开Cookie文件夹,可以看到新建的Cookie文本文件,其中已经存储了刚写入的用户信息。

19.2.3 设置Cookie的域和路径

Cookie信息是有域和路径限制的。在默认情况下,仅在当前页面路径内有效。例如,如果在下面的页面中写入了Cookie信息:

     http://www.mysite.cn/bbs/index.html

那么这些Cookie信息将只会与http://www.mysite.cn/bbs/路径下,以及其子目录下的页面相关联,其他域或本域其他目录中的文件是无权访问的。这种限制主要是为了保护用户信息的隐私,避免恶意存取。

不过这种限制比较大,通常开发人员会希望Cookie信息能够在整个网站内共享,即与网站内所有页面建立关联。这时就可以使用Cookie的path和domain属性。

path属性包含了与Cookie信息相关联的目录(即有效路径),而domain属性定义了Cookie信息的有效作用域。这些属性信息可以附加在Cookie字符串的末尾:

     name=value; expires=date; domain= domain; path=path;

path属性设置了可以读取一个Cookie的最顶层目录,将Cookie的路径设置为网页最顶层的目录,就可以让该目录下所有网页都能访问这个Cookie。例如,如果设置path=/,则Cookie信息将会与服务器根目录及其子目录相关联,即在整个网站中共享。如果只想让bbs目录下的网页可以使用该Cookie,则加入path=/bbs即可。

对于一些网站来说,可能它还会包含很多小的域名(即子域名)。例如,对于百度来说,它就包含:

http://www.baidu.com/

http://news.baidu.com/

http://tieba.baidu.com/

http://zhidao.baidu.com/

http://mp3.baidu.com/

……

在默认情况下,Cookie信息只能够在本域文件中访问,这是Cookie的一种安全特性。不过,可以通过设置Cookie的domain属性来修改域的范围。例如,在http://www.baidu.com/index.html文件中设置一个Cookie信息,则通过设置domain= tieba.baidu.com属性,就可以在http://tieba.baidu.com/路径下访问该Cookie信息。

如果希望在所有子域中访问Cookie信息,则可以在Cookie中加入类似domain=baidu.com的属性设置,这样该Cookie信息就与baidu.com的所有子域下的所有页面相关联,包括www、news、tieba、zhidao、mp3等子域区域。

19.2.4 设置Cookie安全传输

Cookie支持secure属性,该属性定义了Cookie信息的安全性,它可以指定Cookie信息通过网络如何在客户端与Web服务器端之间进行传递。

secure属性取值包括secure或者空字符串。在默认情况下,secure属性值为空,也就是说Cookie信息使用不安全的HTTP连接传递数据。如果一个Cookie设置了secure,那么Cookie信息在客户端与Web服务器之间进行传递时,就通过HTTPS或者其他安全协议传递数据。

19.2.5 案例:完善写入Cookie方法

比较完善的Cookie信息字符串应该包括下面几个部分:

☑ Cookie信息字符串,包含一个名/值对,默认为空。

☑ Cookie有效期,包含一个GMT格式的字符串,默认为当前会话期,即如果没有设置,则当关闭浏览器时,cookie信息就因过期而被清除。

☑ Cookie有效路径,默认为Cookie所在页面目录及其子目录。

☑ Cookie有效域,默认为设置Cookie的页面所在的域。

☑ Cookie安全性,默认为不采用安全加密措施进行传递。

【示例】本示例把写入Cookie信息的实现代码进行封装。

19.2.6 读取Cookie信息

所有Cookie信息都存储在document.cookie属性中,该属性返回值是一个字符串,但是这个字符串的结构比较特殊,实际上它就是一个数据列表,通过分号和等号进行标记信息列表。

为了获取已写入的Cookie信息,用户需要先读取document.cookie属性值,该属性值是一个由零个或多个名/值对的子字符串组成的列表,每对之间通过分号进行分隔。

【示例1】可以采用下面的方法把Cookie字符串转换为对象类型。

如果在写入Cookie信息时,使用了escape()方法编码Cookie值,则应该在读取时不要忘记使用unescape()方法解码Cookie值。

【示例2】下面代码使用上面定义的getCookie()函数读取Cookie信息,并查看每个名/值对信息。

【示例3】在实际开发中,更多的操作是直接读取某个Cookie值,而不是读取所有Cookie信息。下面示例定义一个比较实用的函数,用来读取指定名称的Cookie值。

19.2.7 修改和删除Cookie信息

如果要改变指定Cookie的值,只需要使用相同名称和新值重新设置该Cookie值即可。如果要删除某个Cookie信息,只需要为该Cookie设置一个已过期的expires属性值。

【示例】本示例封装了如何删除指定Cookie信息的方法,这个方法需要调用getCookie()函数。

19.2.8 Cookie的局限性及其突破

Cookie适合在客户端存储用户的简单个人信息,因为Cookie既不是通用的通信机制,也不是通用的数据传输机制。客观上分析,Web浏览器存储的Cookie总数不能够超过300个,如果在同一个域中存储Cookie信息,则Cookie个数不能够超过20,且每个Cookie字符串长度不能够超过4K,即每个Cookie文本文件的大小不能够超过4K。所以使用Cookie存储信息时一定要适度。用户不能够使用它来存储长期保存的大量数据,一般用它来保存用户的状态信息和访问足迹等小数据。

由于每个Web服务器最多只能够存储20个Cookie,为了避免超出这个限制,可以把多个用户信息都保存到一个Cookie中,而不是为每个用户信息都新建一个Cookie。由于Cookie可存储的字符串最大长度为4K(即4096个字符),在实际应用中,这个字符串长度完全满足各种用户信息的存储,那么如何在一个Cookie中存储更多的信息呢?

可以在Cookie中存储一组子名/值对。子名/值对的形式可以自由约定,并确保不引发歧义即可。例如,使用冒号作为子名和子值之间的分隔符,而使用逗号作为子名/值对之间的分隔符,约定类似于对象直接量:

     subName1 : subValue1, subName2 : subValue2, subName3 : subValue3

然后把这组子名/值串作为值传递给Cookie的名称:

     name=subName1:subValue1,subName2:subValue2,subName3:subValue3

为了确保子名/值串不引发歧义,建议使用escape()方法对其进行编码,读取时再使用unescape()方法转码即可。

【示例1】本示例演示了如何在Cookie中存储更多的信息。

【示例2】当读取Cookie信息时,首先需要获取Cookie值,然后调用unescape()方法对Cookie值进行解码,最后再访问Cookie值中每个子Cookie值。因此对于document.cookie来说,就需要分解3次才能得到精确的信息。

提示:现代浏览器都支持Cookie,但是也难免会出现意外。例如,个别老式浏览器不支持Cookie,或者用户禁止浏览器使用Cookie。为了安全起见,在使用Cookie之前,应该探测客户端是否启用Cookie,如果没有启用,则可以采取应急措施,避免不必要的损失,或者导致网站部分功能无法实现。

一般可以使用下面的方法来探测客户端浏览器是否支持Cookie:

如果浏览器启用了Cookie,则CookieEnabled属性值为true;当禁用了Cookie时,则该属性值为false。

19.3 综合案例:Cookie应用

Cookie操作比较简单,但是在默认状态下存取Cookie信息还是比较麻烦的。由于Cookie是通过字符串来存取信息的,这就容易导致赋值运算时需要转换读取信息的数据类型,本节介绍Cookie封装的方法,并利用该方法设计打字游戏。

19.3.1 封装Cookie

设计思路:定义函数Cookie(),该函数既可以写入指定的Cookie信息,删除指定的Cookie信息,同时也能够读取指定名称的Cookie值,另外还可以指定Cookie信息的有效期、有效路径、作用域和安全性选项设置。如果在调用Cookie()函数时,仅指定一个参数值,则表示读取指定名称的Cookie值;如果指定两个参数,则表示写入Cookie信息,其中第一个参数表示名称,第二个参数表示值。在第三个参数中还可以传递选项信息,这些信息以字典形式存储在对象中进行传递。前两个参数以字符串形式进行传递。

封装代码:

应用示例:写入Cookie信息。

读取Cookie信息:

     Cookie("user")

删除Cookie信息:

     Cookie("user",null);

19.3.2 打字游戏

Cookie可以记忆任何用户信息,如经常性、重复性操作,个人爱好等。从需求角度分析,灵活应用Cookie对于提升用户体验非常重要。从技术的角度分析,一个良好的JavaScript应用项目应该选择使用Cookie存储下面几类信息。

☑ 复杂的JavaScript对象实例,这个实例中可以包含基本类型、类成员变量等。

☑ 复杂的DOM节点的状态。

☑ 表单中各种域的初始状态,如文本框、下拉列表框、单选按钮和复选框的初始值等。

☑ 页面布局和风格,如主题、皮肤、窗口的大小、位置、打开页面URL等。

☑ 用户经常性的操作结果,如排序、查询等。

下面示例演示了如何使用Cookie设计一个打字游戏,页面包含3个控制按钮和一个文本区域:

     <input type="button" name="start" id="start" value="开始测试打字速度" />
     <input type="button" name="stop" id="stop" value="停止测试" />
     <input type="button" name="clear" id="clear" value="清除Cookie痕迹"/><br/>
     <textarea name="words" cols="80" rows="20" id="words"></textarea>

当单击“开始测试打字速度”按钮时,JavaScript首先判断用户的身份,没有发现用户在册,则会及时提示注册名称,然后开始计时。当单击“停止测试”按钮时,则JavaScript能够及时计算打字的字数、花费的时间(以分计)。测算打字速度,并与历史最好成绩进行比较,同时累计用户打字的总字数,演示效果如图19-1所示。

图19-1 打字游戏演示

设计思路:

当单击“开始测试打字速度”按钮时,将触发下面事件处理函数,用来检测用户身份,并开始计时:

测试完毕,单击“停止测试”按钮,将触发下面事件处理函数。该函数将汇总相关数据,并与Cookie中相关数据进行比对,存储相关Cookie数据,最后显示汇总信息:

定义清除Cookie信息的事件处理函数:

最后,在页面初始化事件处理函数中分别为3个按钮绑定上面定义的函数即可:

19.4 Web Storage概述

Web Storage(Web存储)提供了一种方式,可以让Web页面实现在客户端浏览器中以键值对的形式在本地保存数据。在了解HTML5的Web存储之前,先来回顾一下HTML4中使用的Cookies存储机制。

19.4.1 Cookie存储机制的优缺点

Cookie是HTML4中在客户端存储简单用户信息的一种方式,它使用文本来存储信息,当有应用程序使用Cookie时,服务器端就会发送Cookie到客户端,客户端浏览器将保存该信息。下一次页面请求时,客户端浏览器就会把Cookie发送到服务器。Cookie最典型的应用是用来保存用户信息、用户设置、密码记忆等。

使用Cookie有其优点,也有其缺陷,其优点主要表现在以下几个方面:

☑ 简单易用。

☑ 浏览器负责发送数据。

☑ 浏览器自动管理不同站点的Cookie。

但经过长期的使用,Cookie也越来越暴露出其先天的不足,主要有以下几点:

☑ 使用简单的文本存储数据,所以Cookie的安全性很差。Cookie保存在客户端浏览器,很容易被黑客窃取。

☑ Cookie中存储的数据容量有限,其上限为4KB。

☑ 存储Cookie的数量有限,多数浏览器上限为30或50个,而像IE 6只支持每个域名20个Cookie,也有部分浏览器存储Cookie的数量高达300个。

☑ 如果浏览器的安全配置为最高级别,则Cookie会失效。

☑ Cookie不适合大量数据的存储,因为Cookie会由每个对服务器的请求来传递,从而造成Cookie速度缓慢、效率低下。

就本地存储的大小而言,从Cookie支持4KB直到今天HTML5,中间经历了比较多的坎坷。IE的userData最少也能支持640KB,IE 8后已经支持DOM Storage,可惜只支持IE。Flash到目前用得最多,其支持的容量为Cookie的25倍,即100KB。之后Google推出的Gears不限制容量,但是需要安装额外的插件。而HTML5现在支持的容量至少为5MB,对于本地存储而言已经足够使用,也有的浏览器会让用户自行设置。

19.4.2 为什么要用Web Storage

HTML5的Web Storage提供了两种在客户端存储数据的方法,即localStorage和sessionStorage。

☑ localStorage

localStorage是一种没有时间限制的数据存储方式,可以将数据保存在客户端的硬盘或其他存储器,存储时间可以是一天、两天、几周或几年,浏览器的关闭并不意味着数据也随之消失,当再次打开浏览器时,依然可以访问这些数据。localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。

☑ sessionStorage

sessionStorage指的是针对一个session的数据存储,即将数据保存在session对象中。Web中的session指的是用户在浏览某个网站时,从进入网站到关闭浏览器所经过的这段时间,可以称为用户与浏览器进行交互的“会话时间”。session对象用来保存这个时间段内所有要保存的数据,当用户关闭浏览器后,则这些数据会被删除。

sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问,并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅是会话级别的存储。

从以上介绍可以看出,localStorage可以永久保存数据,而sessionStorage只能暂时保存数据,这是两者之间的重要区别,在具体使用时应该注意。

Web Storage存储机制比传统的Cookie更加强大,弥补了Cookie的诸多缺点,主要在以下两个方面做了加强。第一,Web Storage提供了易于使用的API接口,只需设置键值即可使用,简单方便。第二,在存储容量方面可根据用户分配的磁盘配额进行存储,能够在每个用户域存储5~10MB以上的内容,用户不仅可以存储session,还可以存储用户的许多信息,如设置偏好、本地化的数据和离线数据等。

Web Storage还提供了使用JavaScript编程的接口,开发者可以使用JavaScript客户端脚本实现许多以前只能在服务器端才能完成的工作。

19.4.3 Web Storage的优势和缺点

数据的本地存储一直都是以本地客户端应用为主,因为本地客户端应用相对于Web存储(Web Storage)的优势显而易见。在HTML5之前,Web应用的本地存储不能满足大量与复杂数据存储的需要。HTML5在Web存储方面所做的重大改进,包括支持在客户端本地建立一个数据库,这是对HTML4中的Cookies存储机制的改善和提高,其应用会越来越广泛。

☑ 存储空间:存储空间更大,IE 8下每个独立的存储空间为10MB,其他浏览器实现略有不同,但都比Cookie要大很多。

☑ 服务器:存储内容不会发送到服务器,当设置了Cookie后,Cookie的内容会随着请求一并发送的服务器,这对于本地存储的数据是一种带宽浪费。而Web Storage中的数据则仅是存在本地,不会与服务器发生任何交互。

☑ 接口:更多丰富易用的接口,Web Storage提供了一套更为丰富的接口,使得数据操作更为简便。

☑ 存储空间:独立的存储空间,每个域(包括子域)有独立的存储空间,各个存储空间是完全独立的,因此不会造成数据混乱。

Web Storage的缺陷主要集中在其安全性方面,具体体现在以下两点:

☑ 浏览器会为每个域分配独立的存储空间,即脚本在域A中是无法访问到域B中的存储空间的,但是浏览器却不会检查脚本所在的域与当前域是否相同。即在域B中嵌入域A中的脚本依然可以访问域B中的数据。

☑ 存储在本地的数据未加密而且永远不会过期,极易造成隐私泄漏。

19.4.4 浏览器支持概述

在HTML5的各项特性中,Web Storage的浏览器支持度是非常好的。其实,目前所有主流浏览器版本都在一定程度上支持Web Storage,具体说明如表19-1所示。

表19-1 浏览器支持概述

HTML5的Web Storage因其广泛的支持度而成为Web应用中最安全的API之一。事实证明各浏览器在API方面的实现基本一致,存在一定的兼容性问题,但不影响正常的使用。

19.5 使用WebStorage

Web Storage使用比较简单,下面分别介绍客户端数据的存储和获取,然后分析localStorage和sessionStorage之间的差异,最后了解其中一些高级特性,为HTML5数据开发奠定基础。

19.5.1 检查浏览器的支持性

在Web Storage API中,特定域名下的Storage数据库可以直接通过window对象访问。因此首先确定用户的浏览器是否支持Web Storage就非常重要。在编写代码时,只要检测window.localStorage和window.sessionStorage是否存在即可,详细代码如下:

许多浏览器不支持从文件系统直接访向文件式的sessionStorage。所以,在运行本章示例之前,应当确保是从Web服务器上获取页面。例如,可以通过本地虚拟服务器发出页面请求:

     http://localhost/test.html

对于很多API来说,特定的浏览器可能只支持其部分功能,但是因为Web Storage API非常小,所以它已经得到了相当广泛的支特。不过出于安全考虑,即使浏览器本身支持Web Storage,用户仍然可自行选择是否将其关闭。

☑ sessionStorage测试

测试方法:打开页面A,在页面A中写入当前的session数据,然后通过页面A中的链接或按钮进入页面B,如果页面B中能够访问到页面A中的数据,则说明浏览器将当前情况的页面A、B视为同一个session,测试结果如表19-2所示。

表19-2 sessionStorage兼容性测试

上面主要针对sessionStorage的一些特性进行了测试,测试的重点在于各个浏览器对于session的定义以及跨域情况。从表19-2中可以看出,出于安全性考虑,所有浏览器下session数据都是不允许跨域访问的,包括跨子域也是不允许的。其他方面主流浏览器中的实现较为一致。

API测试方法包括setItem(key,value)、removeItem(key)、getItem(key)、clear()、key(index),属性包括length、remainingSpace(非标准)。不过存储数据时可以简单地使用localStorage.key=value的方式。

标准中定义的接口在各个浏览器中都已实现,此外IE下新增了一个非标准的remainingSpace属性,用于获取存储空间中剩余的空间,结果如表19-3所示。

表19-3 API测试

此外关于setItem(key,value)方法中的value类型,理论上可以是任意类型,不过实际上浏览器会调用value的toString()方法来获取其字符串值并存储到本地,因此如果是自定义的类型,则需要自己定义有意义的toString()方法。

Web Storage标准事件为onstorage,当存储空间中的数据发生变化时触发。此外,IE自定义了一个onstoragecommit事件,当数据写入时触发。onstorage事件中的事件对象应该支持以下属性。

☑ key:被改变的键。

☑ oldValue:被改变键的旧值。

☑ newValue:被改变键的新值。

☑ url:被改变键的文档地址。

☑ storageArea:影响存储对象。

对于这一标准的实现,Webkit内核的浏览器(Chrome、Safari)以及Opera是完全遵循标准的,IE则只实现了url,Firefox下则均未实现,具体结果如表19-4所示。

表19-4 onStorage事件对象属性测试

此外,不同的浏览器事件注册的方式以及对象也不一致,其中IE和Firefox在document对象上注册,Chrome 5和Opera在window对象上注册,而Safari在body对象上注册。Firefox必须使用document.addEventListener注册,否则无效。

19.5.2 设置和获取数据

下面介绍如何使用sessionStorage设置和获取网页中的简单数据。设置数据值很简单,具体用法如下:

     window.sessionStorage.setItem('myFirstKey', 'myFirstValue');

使用上面的存储访问语句时,需要注意3点:

☑ 实现Web Storage的对象是window对象的子对象,因此window.sessionStorage包含了开发人员需要调用的函数。

☑ setItem()方法需要一个字符串类型的键和一个字符串类型的值来作为参数。虽然Web Storage支持传递非字符数据,但是目前浏览器可能还不支待其他数据类型。

☑ 调用的结果是将字符串myFirstKey设置到sessionStorage中,这些数据随后可以通过键myFirstKey获取。

获取数据需要调用getItem()函数。例如,如果把下面的声明语句添加到前面的示例中:

     alert(window.sessionStorage.get Item('myFirstKey'));

浏览器将弹出提示对话框,显示文本myFirstValue。可以看出,使用Web Storage设置和获取数据非常简单。不过,访问Storage对象还有更简单的方法。可以使用点语法设置数据,使用这种方法,可完全避免调用setItem和getItem,而只是根据键值的配对关系,直接在sessionStorage对象上设置和获取数据。使用这种方法设置数据调用代码可以改写为:

     window.sessionStorage.myFirstKey='myFirstValue';

同样,获取数据的代码可以改写为:

     alert(window.sessionStorage.myFirstKey);

JavaScript允许开发人员设置和获取几乎任何对象的属性,那么为什么还要引入sessionStorage对象。其实,二者之间最大的不同在于作用城。只要网页是同源的(包括规则、主机和端口),基于相同的键,我们都能够在其他网页中获得设置在sessionStorage上的数据。在对同一页面后续多次加载的情况下也是如此。大部分开发者对页面重新加载时经常会丢失脚本数据,但通过Web Storage保存的数据不再如此了,重新加载页面后这些数据仍然还在。

有时一个应用程序会用到多个标签页或窗口中的数据,或多个视图共享的数据。在这种情况下,比较恰当的做法是使用HTML5 Web Storage的另一种实现方式localStorage。localStorage与sessionStorage用法相同,唯一的区别是访问它们的名称不同,分别是通过localStorage和sessionStorage对象来访问。二者在行为上的差异主要是数据的保存时长及它们的共享方式。

localStorage数据的生命周期要比浏览器和窗口的生命周期长,同时被同源的多个窗口或者标签页共享;而sessionStorage数据的生命周期只在构建它们的窗口或者标签页中可见,数据被保存到存储它的窗口或者标签页关闭时。

19.5.3 防止数据泄露

数据能够保存多久呢?对于设置在sessionStorage中的对象,只要浏览器窗口(或标签)不关闭它们就会一直存在。当用户关闭窗口或浏览器,sessionStorage数据将被清除。所以开发人员不应该把真正有价值的东西放在里面,因为不能保证不论什么时候查询这些数据都会存在。

sessionStorage非常适合用于短时存在的流程中,如对话框和向导。如果数据需要存储在多个页面中,同时又不希望用户下一次访问应用程序时重新部署,则可将这些数据存储在sessionStorage中。以前,这类数据可能需要通过表单和cookie提交,并在页面加载时来回传递,而使用Storage可以避免这种开销。

sessionStorage还有另外一种特殊用法,它解决了一个一直困扰着诸多Web应用程序的问题:数据作用域,以购买机票的购物应用程序为例。在这个应用程序中,诸如理想的出发返回日期这样的用户偏好数据,可能会在浏览器和服务器间使用cookie来回传送,为的是在用户使用应用时,服务器应用程序能记住用户先前选择的偏好数据。

不过,用户打开多个窗口是很常见的,它们可能在查看旅游产品时同时打开多个窗口,比较不同代理商同一时间起飞的航班。这会导致Cookie系统出现问题,因为如果一个用户在比较价格和是否有票等情况时在浏览器窗口之间来回切换,它们很可能会在其中一个窗口设置cookie值,而在其他窗口中意外地将这些值应用到URL相同的另一个网页的后续操作中。这一现象也被称为数据泄露。其产生的根本原因在于cookie能够被同源网页共享。

而使用sessionStorage能够跨页面(使用该应用的页面)暂存如启程日期这样的临时数据,又不会将其泄漏到用户仍在浏览其他航班信息的窗口中。这样,不同的偏好信息就会被隔离在预订相应航班的窗口中。

19.5.4 Web Storage其他用法

在使用sessionStorage或localStorage对象的文档中,可以通过window对象来获取它们。除了名字和数据的生命周期外,它们的功能完全相同,其实现的接口代码如下:

具体说明如下:

☑ 使用length属性获取目前Storage对象中存储的键值对的数量。注意,Storage对象是同源的,这意味着Storage对象的长度只反映同源情况下的长度。

☑ key(index)方法允许获取一个指定位置的键。一般而言,最有用的情况是遍历特定Storage对象的所有键。键的索引从零开始,即第一个键的索引是0,最后一个键的索引是index (length-1)。获取到键后,就可以用它来获取其相应的数据。除非键本身或者在它前面的键被删除,否则其索引值会在给定Storage对象的生命周期内一直保留。

☑ getItem(key)函数是根据给定的键返回相应数据的一种方式,另一种方式是将Storage对象当作数组,而将键作为数组的索引。在这种情况下,如果Storage中不存在指定键,则返回null。

☑ 与getItem(key)函数类似,setItem(key,value)函数能够将数据存入指定键对应的位置。如果值已存在,则替换原值。需要注意的是设置数据可能会出错。如果用户已关闭了网站的存储,或者存储已达到其最大容量,那么此时设置数据将会抛出错误。因此,在需要设置数据的场合,务必保证应用程序能够处理此类异常。

☑ removeItem(key)函数的作用是删除数据项,如果数据存储在键参数下,则调用此函数会将相应的数据项剔除。如果键参数没有对应数据,则不执行任何操作。提示,与某些数据集或数据框架不同,删除数据项时不会将原有数据作为结果返回。在删除操作前请确保已经存储相应数据的副本。

☑ clear()函数能删除存储列表中的所有数据。空的Storage对象调用clear()方法也是安全的,此时调用不执行任何操作。

19.5.5 Web Storage事件监测

某些复杂情况下,多个网页、标签页或者Worker都需要访向存储的数据。此时,应用程序可能会在存储数据被修改后触发一系列操作。对于这种情况,Web Storage内建了一套事件通知机制,它可以将数据更新通知发送给监听者。无论监听窗口本身是否存储过数据,与执行存储操作的窗口同源的每个窗口的window对象上都会触发Web Storage事件。

添加如下事件监听器,即可接收同源窗口的Storage事件:

     window.addEventListener("storage", displayStorageEvent, true);

其中事件类型参数是storage,这样只要有同源的Storage事件发生(包括SessionStorage和LocaLStorage触发的事件),已注册的所有事件侦听器作为事件处理程序就会接收到相应的Storage事件。Storage事件的接口形式如下:

StorageEvent对象是传入事件处理程序的第一个对象,它包含了与存储变化有关的所有必要信息。

key属性包含了存储中被更新或删除的键。

oldValue属性包含了更新前键对应的数据,newValue属性包含更新后的数据。如果是新添加的数据,则oldValue属性值为null,如果是被删除的数据,newValue属性值为null。

url属性指向Storage事件发生的源。

storageArea属性是一个引用。它指向值发生改变的localStorage或sessionStorage对象,如此一来,处理程序就可以方便地查询到Storage中的当前值,或基于其他Storage的改变而执行其他操作。

【示例】下面代码是一个简单的事件处理程序,它以提示框的形式显示在当前页面上触发的Storage事件的详细信息。

19.5.6 案例1:设计网页皮肤

在网页设计中,会用JavaScript动态设计网页皮肤,当用户选择某种皮肤样式之后,则再次访问该网站或者页面,都将显示相同的皮肤样式。

对于皮肤配置数据,最适合使用localStorage进行存储,这样每次访问页面时,都会自动调用localStorage数据设置页面样式,避免用户每次访问页面时都需要重新设置选项,效果如图19-2所示。

19.5.7 案例2:跟踪localStorage数据

在本案例中,将调用localStorage对象的相关属性和方法,演示如何动态设置本地化数据。演示效果如图19-3所示。

图19-2 网页皮肤选项

图19-3 跟踪localStorage数据

首先构建一个交互表单的结构:

然后使用JavaScript采集、存储和读写localStorage数据。

19.5.8 案例3:设计计数器

sessionStorage可以作为会话计数器,localStorage则可以作为Web应用访问计数器。声明一个localStorage计数变量,当刷新页面时,会看到计数器在增长,即使关闭浏览器窗口,然后重新访问页面,计数器会继续计数。而sessionStorage计数变量只能够在当前会话期间显示页面访问量,即刷新页面会看到计数器在增长,而当关闭浏览器窗口,然后再试一次,计数器已经重置了,演示效果如图19-4所示。

图19-4 Web应用计数器

计数器代码如下:

19.5.9 综合案例:Web应用项目实时跟踪

现在,让我们将前面所学知识应用到Web应用程序中。随着应用变得越来越复杂,无须服务器交互而管理尽可能多的数据变得越来越重要。将数据存储在本地客户端,进而从本地而不是远程获取数据,既可降低网络流量,又可提升浏览器响应能力。

一个困扰开发人员的常见问题是,当用户从应用程序的一个页面切换到另一个页面时如何管理数据。传统实现方式是由服务器存储数据,当用户在网页间切换时来回传递数据。还有一种方法是应用程序尽可能地让用户停留在一个动态更新的网页上。不过用户更习惯于在页面间切换,当用户返回到应用程序的某个页面时,如果能够快速获取数据并加以显示,对于增强用户体验来说无疑是非常好的方式。

在本示例应用程序中,将演示用户在一个网站的页面间切换时,如何将应用程序的临时数据存储在本地,以及如何从每个页面的Storage中快速加载。

为了达到这一目的,需要引进一个示例网站,它应该可以保存和恢复Storage数据。本示例已经做好了一个包含一个页面的Storage数据管理和跟踪网站。当然,用户可以选择任意网站来做演示。关键是网站要包含多个页面,且能让用户在它们之间轻松切换。

这是一个简单的Web应用实时跟踪项目,使用HTML5的localStorage实现,可以在现代浏览器以及iPhone/iPad运行。演示效果如图19-5所示。

图19-5 Web应用项目实时跟踪

【操作步骤】

第1步,首先构建WebStorage本地化管理器页面结构。代码如下:

整个页面包含4部分结构:

☑ 提示框部分,这部分包含3个提示框结构<div id="about" class="hidden">、<div id="clearLog-message" title="Clear Log" class="hidden">、<div id="emptyFields-message" title="Missing Required Fields" class="hidden">,这3个提示框在默认状态下隐藏显示,当触发特定事件之后才能够正常显示。

☑ <header>部分是一个表单域结构,允许用户在其中执行Storage数据添加操作。

☑ <section>部分是一个信息列表模块,在该部分包含一个<header>提示框和一个列表结构,列表显示所有Storage数据。

☑ <footer>部分是一个待扩展的部分。

第2步,引入jQuery库,本示例借用了jQuery基础库以及UI部件,故在头部区域导入jQuery基础库和UI库。

     <link rel="stylesheet" href="scripts/ui-themes/smoothness/jquery-ui-1.8.custom.css" type="text/css">
     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
     <script type="text/JavaScript" src="scripts/jquery-ui-1.8.custom.min.js"></script>

第3步,在表单结构中添加初始化脚本,实现文本框的半自动化提示信息。

这里主要通过onfocus和onblur事件进行跟踪,实现自动填写文本框的提示信息。

第4步,从localStorage检索所有列表项,并把它们以列表形式显示出来。通过localStorage技术从本地读取用户存储的所有添加的项目管理数据,并把它显示出来。

第5步,编写删除项目函数。显示对话框提示删除单个项目,如果允许则执行删除操作。

第6步,完成两个功能函数之后,在页面初始化事件中调用getAllItems()函数,当提交表单时,把对应的项目数据保存到localStorage中,当单击清除所有项目时,则弹出提示对话框,并调用localStorage.clear()方法和getAllItems()函数。

19.6 Web SQL数据库

HTML5大大丰富了本地可存储的内容,添加了很多功能将原本必须要保存在服务器上的数据转为保存在本地,从而大大提高了Web应用程序的性能,减轻了服务器端的负担,使Web时代重新回到了客户端为重、服务器端为轻的时代。在这其中,一项非常重要的功能就是数据库的本地存储功能。在HTML5中内置了一个可以通过SQL语言来访问的数据库。在HTML4中,数据库只能放在服务器端,只能通过服务器来访问数据库,但是在HTML5中,可以像访问本地文件那样轻松地对内置数据库进行直接访问。

19.6.1 Web SQL数据库概述

以键/值对的形式存储数据的Storage API在数据持久化方面已经很强大了,但是HTML5应用同样可以访问索引数据库。数据库API的具体细节仍在完善中,并有多个方案。Web SQL Database是其中之一,并已经在Safari、Chrome和Opera中实现了,如表19-5所示显示了浏览器对于Web SQL Database的支持情况。

表19-5 浏览器支持概述

Web SQL Database允许应用程序通过一个异步JavaScript接口访问SQLLite数据库。虽然它既不是常见Web平台的一部分,也不是HTML5规范最终推荐的数据库API,但当针对如Safari移动版这样的特定平台时,SQL API很有用。在任何情况下,SQL API在浏览器中的数据库处理能力都是无可比拟的。与其他Storage API一样,浏览器能够限制同源页面可用Storage的大小,并且当用户数据被清除时,Storage中的数据也会被清除。

Web SQL数据库API实际上不是HTML5规范的组成部分,而是单独的规范。它通过一套API来操纵客户端的数据库。虽热Web SQL Database已经在Safari、Chrome和Opera中实砚,但是IE、Firefox中并没有实砚它,而且WHATWG也停止对Web SQL Database的开发。由于标准认定直接执行SQL语句不可取,Web SQL Database已被极新的规范—索引数据库(Indexed Database,原为WebSimpleDB)所取代。索引数据库更简便,而且不依赖于特定的SQL数据库版本。目前浏览器正在逐步实现对索引数据库的支持。

19.6.2 使用Web SQL数据库

SQL数据库已经得到了广泛的利用,所以HTML5也采用了这种数据库作为本地数据库。因此,如果先掌握了SQLLite数据库的基本知识,接着再学如何使用HTML5的数据库就不困难了。

HTML5数据库API是以一个独立规范的形式出现,它包含3个核心方法。

☑ openDatabase:使用现有数据库或创建新数据库的方式创建数据库对象。☑ transaction:允许根据情况控制事务提交或回滚。

☑ executeSql:用于执行真实的SQL查询。

使用JavaScript脚本编写SQLLite数据库有两个必要的步骤。

☑ 创建访问数据库的对象。

☑ 使用事务处理。

1.创建或打开数据库

首先,必须要使用openDatabase()方法来创建一个访问数据库的对象。具体用法如下:

openDatabase()方法可以打开已经存在的数据库,如果不存在则创建。openDatabase()方法中5个参数分别表示:数据库名、版本号、描述、数据库大小、创建回调。创建回调没有时也可以创建数据库。

【示例1】创建了一个数据库对象db,名称是Todo,版本编号为0.1。db还带有描述信息和大概的大小值。浏览器可使用这个描述与用户进行交流,说明数据库是用来做什么的。利用代码中提供的大小值,浏览器可以为内容留出足够的存储。如果需要,这个大小是可以改变的,所以没有必要预先假设允许用户使用多少空间。

     db=openDatabase("ToDo","0.1","A list of to do items.", 200000);

为了检测之前创建的连接是否成功,可以检查数据库对象是否为null:

注意:使用中绝不可以假设该连接已经成功建立,即使过去对于某个用户它是成功的。为什么一个连接会失败,这里面存在多个原因:也许浏览器出于安全原因拒绝访问,也许设备存储有限。面对活跃而快速进化的潜在浏览器,对用户机器、软件及其能力作出假设是非常不明智的行为。如当用户使用手持设备时,他们可自由处置的数据可能只有几兆字节。

2.访问和操作数据库

实际访问数据库时,还需要调用transaction()方法,用来执行事务处理。使用事务处理,可以防止在对数据库进行访问及执行有关操作时受到外界的打扰。因为在Web上,同时会有许多人都在对页面进行访问。如果在访问数据库的过程中,正在操作的数据被别的用户给修改掉,会引起很多意想不到的后果。因此,可以使用事务来达到在操作完了之前,阻止别的用户访问数据库的目的。

transaction()方法的使用方法如下:

     db.transaction(function(tx) {})

transaction()方法使用一个回调函数作为参数。在这个函数中,执行访问数据库的语句。

在transaction()方法的回调函数内,使用了作为参数传递给回调函数的transaction对象的executeSql()方法。executeSql()方法的完整定义如下:

     transaction.executeSql(sqlquery,[],dataHandler, errorHandler):

该方法使用4个参数,第一个参数为需要执行的SQL语句。

第二个参数为SQL语句中所有使用到的参数的数组。在executeSql()方法中,将SQL语句中所要使用到的参数先用“?”代替,然后依次将这些参数组成数组放在第二个参数中,如下所示:

     transaction.executeSql("UPDATE people set age-? where name=?;",[age, name]);

第三个参数为执行SQL语句成功时调用的回调函数。该回调函数的传递方法如下:

     function dataRandler(transaction, results){//执行SQL语句成功时的处理
     }

该回调函数使用两个参数,第一个参数为transaction对象,第二个参数为执行查询操作时返回的查询到的结果数据集对象。

第四个参数为执行SQL语句出错时调用的回调函数。该回调函数的传递方法如下:

     function errorHandler(transaction,errmeg) {//执行SQL语句出错时的处理
     }

该回调函数使用两个参数,第一个参数为transaction对象,第二个参数为执行发生错误时的错误信息文字。

【示例2】下面将在mydatabase数据库中创建表t1,并执行数据插入操作,完成插入两条记录。

在插入新记录时,还可以传递动态值:

当执行查询操作时,从查询到的结果数据集中依次把数据取出到页面上来,最简单的方法是使用for语句循环。结果数据集对象有一个rows属性,其中保存了查询到的每条记录,记录的条数可以用rows.length来获取,可以用for循环,用rows[index]或rows.Item (index])的形式来依次取出每条数据。在JavaScript脚本中,一般采用rows[index]的形式。另外在Chrome浏览器中,不支持rows.Item ([index)的形式。

【示例3】要读取已经存在的记录,使用一个回调函数来捕获结果,并通过for语句循环显示每条记录。

19.6.3 实例1:创建简单的本地数据库

本实例将完整地演示Web SQL Database API的使用,包括建立数据库、建立表格、插入数据、查询数据、将查询结果显示。在最新版本的Chrome、Safari或Opera浏览器中输出结果如图19-6所示。

实例完整代码如下:

其中第五行的var db=openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024);建立一个名称为mydb的数据库,它的版本为1.0,描述信息为Test DB,大小为2MB字节。可以看到此时有数据库建立,但并无表格建立,如图19-7所示。

图19-6 创建简单的本地数据库

图19-7 创建数据库mydb

openDatabase()方法打开一个已经存在的数据库,如果数据库不存在则创建数据库,创建数据库包括数据库名、版本号、描述、数据库大小、创建回调函数。最后一个参数创建回调函数,在创建数据库时调用,但即使没有这个参数,一样可以运行时创建数据库。

第七行到第十三行代码:

通过第八行语句可以在mydb数据库中建立一个LOGS表格。在这里只执行创建表格语句,而不执行后面两个插入操作时,在Chrome中可以看到在数据库mydb中有表格LOGS建立,但表格LOGS为空。

第九行、第十行执行插入操作,在插入新记录时,还可以传递动态值:

这里的e_id和e_log为外部变量,executeSql()在数组参数中将每个变量映射到“?”。在插入操作执行后,可以在Chrome中看到数据库的状态,可以看到插入的数据,此时并未执行查询语句,页面中并没有出现查询结果,如图19-8所示。

图19-8 创建数据表并插入数据

如果要读取已经存在的记录,使用一个回调函数捕获结果,如上面的第十四行到第二十四行代码:

执行查询之后,将信息输出到页面中,可以看到页面中的查询数据,如图19-6所示。

注意:如果不是绝对需要,不要使用Web SQL Database,因为它会让代码更加复杂(匿名内部类的内部函数、回调函数等)。在大多数情况下,本地存储或会话存储就能够完成相应的任务,尤其是能够保持对象状态持久化的情况。通过这些HTML5 Web SQL Database API接口,可以获得更多功能,相信以后会出现一些非常优秀的、建立在这些API之上的应用程序。

19.6.4 实例2:批量存储本地数据

Web SQL Database操作数据比较繁琐,为了提高代码执行效率,下面通过一个示例演示如何通过数组实现快速存储数据。

首先,创建一个本地数据库db,把预存储的数据放在数组names中,使用for语句执行批量操作,这样就省略了编写大量的executeSql语句,当数据量比较大时,这种批量操作方式就更加有效。

执行initDatabase()函数,完成数据初始化存储操作,然后调用doQuery()函数,再次使用for语句把本地数据库中的数据读取出来,并通过log()显示函数把数据显示在页面表格中。最后显示效果如图19-9所示。

图19-9 批量存储本地数据

数据库操作可能需要花点时间才能完成。不过,在获得查询结果集之前,查询操作会在后台运行,以避免阻塞脚本的执行。executeSQL()的第三个参数是回调函数,查询到的事务和结果集将作为参数供此回调函数使用。

19.6.5 综合案例:Web Storage和Web SQL混合开发

Web Storage使用getItem()方法读取数据,虽然这种一对一的数据读写方法使用起来比较方便,但是在实际使用过程中用处并不是很大,因为如果要保存的数据量比较大的话,使用这种方法会非常麻烦。

例如,以一个简单Web留言本示例看一下如何利用Web Storage来保存和读取大量数据。使用一个多行文本框来输入数据,当单击接钮时将文木框中的数据保存到localStorage中,在表单下部放置一个p元素来显示保存后的数据。

如果只保存文本框中的内容,并不能知道该内容是什么时候写好的,所以保存该内容的同时,也保存了当前日期和时间,并将该日期和时间一并显示在p元素中。

利用Web Storage保存数据时,数据必须是“键名/键值’这样的格式,所以将文本框的内容作为键值,保存时的日期和时间作为键名来进行保存,计算机中对于日期和时间的值是以时间戮的形式进行管理的,所以保存时不可能存在重复的键名。

整个页面的具体代码如下:

在该页面中,除了输入数据用的文本框与显示数据用的P元素之外,还放置了“追加”按钮与“初始化”按钮,单击“追加”按钮来保存数据,单击“初始化”按钮来消除全部数据,如图19-10所示。

在JavaScript脚本里含有3个供按钮调用的函数,分别是saveStorage()、loadStorage()、clearStorage(),简单说明如下。

☑ saveStorage()函数:这个函数比较简单,使用new Date().getTime()语句得到了当前的日期和时间,然后调用localStorage.setItem()方法,将得到的时间作为键值,并将文本框中的数据作为键名进行保存。保存完毕后,重新调用脚本中的loadStorage()函数在页面上重新显示保存后的数据。

☑ loadStorage()函数:取得保存后的所有数据,然后以表格的形式进行显示。取得全部数据时,需要用到localStorage两个比较重要的属性。

▶ loadStorage.length返回所有保存在localStorage中的数据的条数。

图19-10 使用localStorage存储数据的Web留言本

▶ IocalStorage.key(index)将想要得到数据的索引号作为index参数传入,可以得到localStorage中与这个索引号对应的数据。如想得到第6条数据,传入的index为5(index是从0开始计算的)。

先用loadStorage.length属性获取保存数据的条数,然后做一个循环,在循环内用一个变量,从0开始将该变量作为index参数传入localStorage.key(index)属性,每次循环时该变量加1,通过这种方法取得保存在localStorage中的所有数据。

☑ clearStorage()函数:将localStorage中保存的数据全部清除,在这个函数中只有一句语句localStorage.clear();调用localStorage的clear()方法时,所有保存在localStorage中的数据会全部被清除。

那么能不能将Web Storage作为简易数据库来利用呢?

如果想要将Web Storage作为数据库来利用,必须要考虑几个问题。

首先,在数据库中,大多数表都分为几列,怎样对列来进行管理呢?然后,怎样对数据进行检索呢?如果能够解决这些问题,就可以将Web Storage作为数据库来利用。

例如,设计一个客户联系信息管理网页。客户的联系信息包括姓名、E-mail地址、电话号码、备注列,把它们保存在localStorage中,如果输入客户的姓名并且进行检索,可以获取这个客户的所有联系信息。

首先,用客户的姓名作为键名来保存数据,这样在获取客户其他信息时会比较方便,那么怎样将客户联系信息分几列来进行保存呢?

要做到这一点,需要使用JSON数据格式。用这种JSON格式作为文本保存来保存对象,获取该对象时再通过JSON格式来获取,应该就可以在Web Storage中保存和读取具有复杂结构的数据了。

整个页面的具体代码如下:

在JavaScript脚本中存放了两个函数,分别是保存数据用的saveStorage()函数与检索数据用的findStorage()函数。

saveStorage()函数中的流程如下:

第1步,从各输入文本框中获取数据。

第2步,创建对象,将获取的数据作为对象的属性进行保存。

第3步,将对象转换成JSON格式的文本数据。

第4步,将文本数据保存在localStorage中。

为了将数据保存在一个对象中,使用new Object语句创建了一个对象,将各种数据保存在该对象的各个属性中,然后,为了将对象转换成JSON格式的文本数据,使用了JSON对象的stringify()方法,该方法的使用方法如下:

     var str=JSON.stringify(data);

该方法接收一个参数data,它表示要转换成JSON格式文本数据的对象,这个方法的作用是将对象转换成JSON格式的文本数据,并将其返回。

findStorage()函数中的流程如下:

第1步,在localStorage中将检索用的姓名作为键值,获取对应的数据。

第2步,将获取的数据转换成JSON对象。

第3步,取得JSON对象的各个属性值,创建要输出的内容。

第4步,将要输出的内容在页面上输出。

该函数的关键是使用JSON对象的parse()方法,将从localStorage中获取的数据转换成JSON对象。该方法的使用方法如下:

     var data=JSON.parse(str);

该方法接收一个参数str,它表示从localStorage()中取得的数据,该方法的作用是将传入的数据转换成JSON对象,并且将该对象返回。

上面示例的关键是利用了JSON对象的stringify()方法与parse()方法。注意,这个JSON对象只是被大部分最新版本的浏览器所支持,而不是所有浏览器、所有版本都支持。现在支持JSON对象的浏览器有IE 8+\Firefox 3.6+、Chrome +、Safari 5+、Opera 10+浏览器。

示例演示效果如图19-11所示。

图19-11 使用Web Storage模拟数据库

上面示例演示了如何使用Web Storage设计Web留言本,下面来看一下怎样利用Web SQL数据库实现同样的功能。

首先,设计页面中包含一个输入姓名用的文本框、一个输入留言用的文本框,以及一个保存数据时用的按钮。在按钮下面放置一个表格,保存数据后从数据库中重新取得所有数据,然后把数据显示在这个表格中。单击按钮时,调用saveData()函数,保存数据时的处理都被写在了这个函数中。

另外,打开页面时将调用init()函数,将数据库中全部已保存的留言信息显示在表格中。

整个页面的具体代码如下:

下面重点分析JavaScript脚本代码。

☑ 打开数据库

打开数据库的代码如下:

     var datatable=null;
     var db=openDatabase('MyData', '', 'My Database', 102400);

在JavaScript脚本一开始,使用了一个变量datatable。用这个变量来代表页面中的table元素。db变量代表使用openDatabase()方法创建的数据库访问对象。在示例中创建了MyData数据库并对其进行访问。

☑ 初始化

编写init()函数,该函数在页面打开时调用。为了在打开页面时就往页面表格中装入数据,所以在该函数中首先设定变量datatable为页面中的表格,然后调用脚本中另一个函数showAllData()来显示数据。

☑ 清除表格中当前显示的数据

removeAllData()函数是在showAllData()函数中被调用的一个必不可少的函数,它的作用是将页面中table元素下的子元素全部清除,只留下一个空表格框架,然后输入表头。这样在页面表格中当前显示的数据就全部被清除,以便重新读取数据并装入表格。

☑ 显示数据

showData()函数使用一个row参数,该参数表示从数据库中读取到的一行数据。该函数在页面表格中使用tr元素添加一行,并使用td元素添加各列,然后将传入的这行数据分别输入在表格中添加的这一行对应的各列中。

☑ 显示全部数据

showAllData()函数使用transaction()方法,在该方法的回调函数中执行executeSGL()方法获取全部数据。获取到数据之后,首先调用removeAllData()函数初始化页面表格,将该表格中当前显示的数据全部清除,然后在循环中调用showData()函数,将获取到的每一条数据作为参数传入,在页面上的表格中逐条显示获取到的每条数据。

☑ 追加数据

addData()函数在saveData()函数中被调用。在addData()函数中,使用transaction()方法,在该方法的回调函数中执行executeSGL()方法,将作为参数传入进来的数据保存在数据库中。

☑ 保存数据

saveData()函数首先调用addData()函数追加数据,然后调用showAllData()函数重新显示表格中的全部数据。

示例演示效果如图19-12所示。

图19-12 使用Web SQL设计Web留言本

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:黑客帝国 设计师:我叫白小胖 返回首页

打赏作者

唯美清泠

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值