HTML5学习-day02【悟空教程】

HTML5学习-day02【悟空教程】

JavaScript API

基础API提升

New Selectors

提供类似于jQuery中选择器的API

  • 通过类名查找元素
  • 通过CSS语法查找元素

Element.classList

提供类似于jQuery中CSS操作的API

访问历史 API

在此之前我们可以通过history对象实现前进、后退和刷新之类的操作

H5中开放了更多的API:历史状态操作

HTML5 history API包括:

  • history.pushState()方法:新增状态
  • history.replaceState()方法:替换状态
  • window.onpopstate事件:得到状态

HTML5 history API有什么用呢?

从Ajax翻页的问题说起

请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论。正当你想要停下滚轮细看的时候,手残按到了F5。然后,页面刷新了,评论又回到了第一页,所以你又要重新翻一次。

再或者,你想把这个评论发给别人分享,一面给了别人页面地址(为什么不直接复制呢?因为要连带视频等场景啊),一面又要加一句嘱咐:请翻到下面评论的第XX页的XX楼。

这就是问题。试想一下,如果浏览器能记住你当前的状态(比如看到了第十几页),而不是一刷新就还原,是不是就显得智能多了?

为什么用Ajax?

用Ajax实现翻页等内容切换是有原因的。在传统的无Ajax的站点里,页面A和页面B可能只有10%的地方是不同的,其他90%的内容(尤其是导航、页脚等公用元素)都是一样的,但却仍然需要浏览器下载并显示新的一整个页面。而如果使用Ajax,不仅节省了浏览器需要下载的资源,而且无刷新切换明显比页面跳转更平滑、流畅。

就视频下面的评论来说,Ajax可以说是必须的。视频这样的重量级元素,动不动给你重新加载一次,不能忍。

传统的跳转翻页的可取之处

传统的不使用Ajax的站点,每一个翻页是一个跳转,然后你可以在浏览器地址栏里看到诸如?page=2这样的参数。每一页就这样通过地址栏的URL做了标记,每一次请求,浏览器都会根据参数返回正确的页码。

所以,传统的跳转翻页,刷新也不会丢失状态。

结合两者

现在我们就可以想到,如果在Ajax更新页面局部内容的同时,也在地址栏的URL里更新状态参数,就可以做出更完美的Ajax翻页了。

不过,JavaScript修改location的除hash外的任意属性,页面都会以新URL重新加载。而唯一不引发刷新的hash参数并不会发送到服务器,因此服务器无法获得状态。

然后,HTML5 history API将解决这个问题。

介绍HTML5 history API

HTML5 history API只包括2个方法:history.pushState()history.replaceState(),以及1个事件:window.onpopstate

history.pushState()

它的完全体是 history.pushState(stateObject, title, url),包括三个参数。

第1个参数是状态对象,它可以理解为一个拿来存储自定义数据的元素。它和同时作为参数的url会关联在一起。

第2个参数是标题,是一个字符串,目前各类浏览器都会忽略它(以后才有可能启用,用作页面标题),所以设置成什么都没关系。目前建议设置为空字符串。

第3个参数是URL地址,一般会是简单的?page=2这样的参数风格的相对路径,它会自动以当前URL为基准。需要注意的是,本参数URL需要和当前页面URL同源,否则会抛出错误。

调用pushState()方法将新生成一条历史记录,方便用浏览器的“后退”和“前进”来导航(“后退”可是相当常用的按钮)。另外,从URL的同源策略可以看出,HTML5 history API的出发点是很明确的,就是让无跳转的单站点也可以将它的各个状态保存为浏览器的多条历史记录。当通过历史记录重新加载站点时,站点可以直接加载到对应的状态。

history.replaceState()

它和history.pushState()方法基本相同,区别只有一点,history.replaceState()不会新生成历史记录,而是将当前历史记录替换掉

window.onpopstate

push的对立就是pop,可以猜到这个事件是在浏览器取出历史记录并加载时触发的。但实际上,它的条件是比较苛刻的,几乎只有点击浏览器的“前进”、“后退”这些导航按钮,或者是由JavaScript调用的history.back()等导航方法,且切换前后的两条历史记录都属于同一个网页文档,才会触发本事件。

上面的“同一个网页文档”请理解为JavaScript环境的document是同一个,而不是指基础URL(去掉各类参数的)相同。也就是说,只要有重新加载发生(无论是跳转到一个新站点还是继续在本站点),JavaScript全局环境发生了变化,popstate事件都不会触发。

popstate事件是设计出来和前面的2个方法搭配使用的。一般只有在通过前面2个方法设置了同一站点的多条历史记录,并在其之间导航(前进或后退)时,才会触发这个事件。同时,前面2个方法所设置的状态对象(第1个参数),也会在这个时候通过事件的event.state返还回来。

此外请注意,history.pushState()history.replaceState()本身调用时是不触发popstate事件的。pop和push毕竟不一样!

如何应用

HTML5 history API的内容不多,具体如何应用它来改进Ajax翻页呢?

首先,在服务器端添加对URL状态参数的支持,例如?page=3将会输出对应页码的内容(后端模板)。也可以是服务器端把对应页码的数据给JavaScript,由JavaScript向页面写入内容(前端模板)。

接下来,使用history.pushState(),在任一次翻页的同时,也设置正确的带参数的URL。代码可能是这样:

newURL = "?page=" + pageNow;
history.pushState(null, "", newURL);

到此,就解决了F5刷新状态还原的事了。

不过,还没有结束,在浏览器中点击后退,例如从?page=3退到?page=2,会发现没有变化。按道理说,这时候也应该对应变化。这就要用到popstate事件了。

window添加popstate事件,加入这种导航变化时的处理。代码可能是这样(jQuery):

这样,就完成了。这样看起来是否会觉得还挺容易的呢?在支持HTML5 history API的浏览器中,以上部分就已经做到了带页码记录的Ajax翻页。

有待斟酌的兼容性问题

根据[caniuse][]上的数据,IE10+及其他主流浏览器都支持HTML5 history API。为保证不支持的浏览器不报错,可以加入是否支持HTML5 history API的判断:

这样,一个Ajax翻页,在支持HTML5 history API的浏览器上,将会智能地保存当前页码信息,而不支持的浏览器仍然可以正常使用,只是不保存页码信息(就像改进前那样)。我认为,按照“渐进增强”的思路,这样就是最好的了,也就是:只使用较少的代码优化高级浏览器的使用体验。

如果真的想要在各类浏览器里都表现一致,拥有这样的记录功能呢?

这时候推荐使用Benjamin Lupton的[History.js][],它提供和HTML5 history API近似的api,会在不支持的浏览器里回退到hash形式去处理历史记录。尽管为了兼容这种hash的回退形式你可能要额外做点事(hash不会发送到服务器端),但它确实可以让你做到更广范围的兼容。

HTML5 history API并不完美

即使只考虑支持HTML5 history API的浏览器,它们对HTML5 history API的一些细节处理也会有差异和问题。History.js提供的只针对HTML5浏览器的版本,仍然包含了不少处理兼容问题的代码。

但是,不完美也没有关系。以我的测试结果,本文所介绍的简单的写法,就可以在绝大部分支持HTML5 history API的浏览器上正常运行。如果你担心有哪些浏览器会有潜在问题,去测试那个浏览器就可以了。你最后用于兼容处理的自写代码很可能远比一个JavaScript库少得多,毕竟,你也不一定会喜欢额外引入一个JavaScript库来完成一个功能吧。

一些相关内容

地址栏里的hash曾是过去被广泛用来记录页面状态的标记,你可以阅读[W3C Blog的这篇文章][]了解它的经历。

现在可以在不刷新的状况下操作浏览器地址栏和历史记录了,那同一站点的普通链接跳转是否都可以转变为Ajax来提升使用体验?是的,而且已经有了pjax[]这些专门完成这个功能的作品。

不只是翻页,HTML5 history API将尤其适合用在大量使用Ajax、包含多个视图的单页应用。

为一个页面的每一个状态都生成一条历史记录不一定合适(会让用户的历史记录变多变乱),酌情使用replaceState()而不是pushState()来控制历史记录的数量。

结语

HTML5 history API简单易学,不多的几行代码就可以做到“状态记录”这个小小的改进,如果可以由你选择“渐进增强”,它还真的可以上线!

演示

IE10+和其他主流浏览器

全屏 API

Please press F

基础API提升 学习目标

  • 熟练使用新选择器和ClassList
  • 对访问历史操作有基本的了解,为以后开发SPA做准备
  • 了解全屏API

网页存储

通过以下API,可以轻松构建离线H5应用

Application Cache

就是让网页可以离线访问的技术

演示


什么是Application Cache API?

HTML5提供了一系列的特性来支持离线应用:

  • application cache
  • localStrorage
  • web SQL & indexed database
  • online/offline events

本文要讲的是application cache。传统的web程序中浏览器也会对资源文件进行cache,但是并不是很可靠,有时起不到预期的效果。而HTML5中的application cache支持离线资源的访问,为离线web应用的开发提供了可能。

哪些浏览器支持Application Cache API?

目前支持application cache的浏览器如下:

使用application cache能带来什么好处?

使用application cache能够带来以下几点收益:

  • 用户可以在离线时继续使用
  • 缓存到本地,节省带宽,加速用户体验的反馈
  • 减轻服务器的负载

如何使用application cache

要使用application cache,主要用到缓存清单文件:manifest,该文件告诉浏览器需要缓存哪些资源

manifest文件结构

CACHE MANIFEST# 以上折行必需要写
CACHE:
# 这部分写需要缓存的资源文件列表
# 可以是相对路径也可以是绝对路径index.html
index.css
images/logo.png
js/main.js
http://img.baidu.com/js/tangram-base-1.5.2.1.jsNETWORK:
# 可选
# 这一部分是要绕过缓存直接读取的文件login.php
FALLBACK:
# 可选
# 这部分写当访问缓存失败后,备用访问的资源
# 每行两个文件,第一个是访问源,第二个是替换文件*.html /offline.html

manifest文件使用

写完一个manifest文件之后,像下面这样在你的web页面中引用他

<html manifest="demo.cache">
  ...</html>

其中文件后缀可以自定义,但是需要在服务器中进行相应配置,指定其为text/cache-manifest MIME 类型文件

在apache中定义如下:

AddType text/cache-manifest .cache

做完以上工作,你的应用程序就可以使用application cache了。

更新缓存的方式

开发人员想要通知客户的浏览器更新application cache的方法有以下两类:

更新manifest文件

浏览器发现manifest文件本身发生变化,便会根据新的manifest文件去获取新的资源进行缓存。

当manifest文件列表并没有变化的时候,我们通常通过修改manifest注释的方式来改变文件,从而实现更新。

通过javascript操作

浏览器提供了applicationCache供js访问,通过对于applicationCache对象的操作也能达到更新缓存的目的。

var appCache = window.applicationCache;

appCache.update(); //尝试更新缓存...if (appCache.status == window.applicationCache.UPDATEREADY) {
  appCache.swapCache();  //更新成功后,切换到新的缓存}

另外,用户想要更新缓存,可以通过删除缓存文件的方式来清除缓存。

applicationCache对象

该对象是window对象的直接子对象,window.applicationCache

基类:DOMApplicationCache

事件列表:

事件

接口

触发条件

后续事件

checking

Event

用户代理检查更新或者在第一次尝试下载manifest文件的时候,本事件往往是事件队列中第一个被触发的

noupdate, downloading, obsolete, error

noupdate

Event

检测出manifest文件没有更新

downloading

Event

用户代理发现更新并且正在取资源,或者第一次下载manifest文件列表中列举的资源

progress, error, cached, updateready

progress

ProgressEvent

用户代理正在下载资源manifest文件中的需要缓存的资源

progress, error, cached, updateready

cached

Event

manifest中列举的资源已经下载完成,并且已经缓存

updateready

Event

manifest中列举的文件已经重新下载并更新成功,接下来js可以使用swapCache()方法更新到应用程序中

obsolete

Event

manifest的请求出现404或者410错误,应用程序缓存被取消

error

Event

manifest的请求出现404或者410错误,更新缓存的请求失败

manifest文件没有改变,但是页面引用的manifest 文件没有被正确地下载

在取manifest列举的资源的过程中发生致命的错误

在更新过程中manifest文件发生变化

用户代理会尝试立即再次获取文件

属性:status 返回缓存的状态

可选值

匹配常量

描述

0

appCache.UNCACHED

未缓存

1

appCache.IDLE

闲置

2

appCache.CHECKING

检查中

3

appCache.DOWNLOADING

下载中

4

appCache.UPDATEREADY

已更新

5

appCache.OBSOLETE

失效

方法

方法名

描述

update()

发起应用程序缓存下载进程

abort()

取消正在进行的缓存下载

swapcache()

切换成本地最新的缓存环境

manifest解析机制

注意事项

  • 站点离线存储的容量限制是5M
  • 如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程将视为失败,浏览器继续全部使用老的缓存
  • 引用manifest的html必须与manifest文件同源,在同一个域下
  • 在manifest中使用的相对路径,相对参照物为manifest文件
  • CACHE MANIFEST字符串应在第一行,且必不可少
  • 系统会自动缓存引用清单文件的 HTML 文件
  • manifest文件中CACHE则与NETWORK,FALLBACK的位置顺序没有关系,如果是隐式声明需要在最前面
  • FALLBACK中的资源必须和manifest文件同源
  • 当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。
  • 站点中的其他页面即使没有设置manifest属性,请求的资源如果在缓存中也从缓存中访问
  • 当manifest文件发生改变时,资源请求本身也会触发更新

整体介绍了一下appcache,接下来会对appcache做一些黑盒测试,以便我们了解更多。

这个demo中主要涉及到3类资源,两个页面,我们观察3类资源在不同的场景下浏览器的appcache策略。

demo代码:

test1.html如下:

<html manifest="manifest.appcache"><head>
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
   <title>demo</title></head><script type="text/javascript" src="js/index.js"></script><script type="text/javascript">
   applicationCache.onchecking = function(){
   console.log("checking")
}
applicationCache.ondownloading = function(){
   console.log("dowload")
}
applicationCache.onnoupdate = function(){
   console.log("noupdate")
}
applicationCache.onprogress = function(){
   console.log("progress")
}
applicationCache.oncached = function(){
   console.log("cached")
}
applicationCache.onupdateready = function(){
    console.log("updateready")
}
applicationCache.onobsolete = function(){
   console.log("obsolete")
}
applicationCache.onerror = function(){
   console.log("error")
}</script><link rel="stylesheet" type="text/css" href="css/index.css" media="all" /><body>  <div id="test">此处观察样式效果<div>
   <img src="img/index.jpg" />
   <img src="img/no-cache.jpg"></body></html>

test2.html如下:

   demo           此处观察样式效果
  • 我们在两个页面中对于applicationCache对象的事件进行了监听,并将当前触发的事件名输出到console中,以便我们了解发生了什么
  • 两者区别在于,一个引用了manifest,一个没有,用于进行对比。
  • js和css初始化为空,用于观察效果

结论及场景

1.application cache并不因服务器上资源改变而改变,只有manifest改变才会触发重新download,并且是所有cache文件均重新获取

正常载入test1.html时,console输出如下:

checking        /html5/appcache/:13
noupdate        /html5/appcache/:37

当js,css和图片发生变化时,载入test1.html ,console不变:

checking        /html5/appcache/:13
noupdate        /html5/appcache/:37

当manifest文件进行修改后,console如下:

checking       /html5/appcache/:13
dowload        /html5/appcache/:27
5 progress     /html5/appcache/:49
updateready    /html5/appcache/:67

2.对于另一个没有引用manifest文件的html,当它加载时,不会触发applicationCache的任何事件,但是会使用缓存。

页面加载的时候。console输入为空

修改服务器js,css等资源,页面中没有变化,修改manifest文件后,刷新页面,资源修改的效果出现。

3.直接请求资源的绝对路径,只要该url被缓存过,那么所有的访问均是该资源的缓存,而与引用所在的宿主页面是否有manifest没有关系

给js中写上alert("更新"),访问该资源的url,结果没有变化

更新manifest之后,该js的访问得到更新

4.js和css,图片文件的本身的访问,均会进行checking

直接在地址栏输入一个缓存的js文件,console显示如下:

Document was loaded from Application Cache with manifest http://localhost/html5/appcache/manifest.appcache
Application Cache Checking event
Application Cache NoUpdate event

修改manifest文件后,再次访问这个资源。显示如下:

Document was loaded from Application Cache with manifest http://localhost/html5/appcache/manifest.appcache
Application Cache Checking event
Application Cache Downloading event
Application Cache Progress event (0 of 4) http://localhost/html5/appcache/css/index.css
Application Cache Progress event (1 of 4) http://localhost/html5/appcache/js/index.js
Application Cache Progress event (2 of 4) http://localhost/html5/appcache/
Application Cache Progress event (3 of 4) http://localhost/html5/appcache/img/index.jpg
Application Cache Progress event (4 of 4) 
Application Cache UpdateReady event

所有的资源,均被重新下载

经过反复试验后,我们可以总结出以下浏览器在应用缓存方面处理url的逻辑策略:


Web Storage

localStorage & sessionStorage

演示

Web StorageAPI

web storage内容

web storage提供在浏览器端通过key/value的方式存储数据。包括以下两部分:

  • session storage(会话级别的存储,会话结束后失效)
  • local storage(持久性存储,用户主动删除或js操作清空)

web storage优势

web storage的提出的初衷主要是为了解决cookie在数据存储时的一些不足。和cookie相比,web storage主要有以下几点优势:

  • 存储空间大,默认5m
  • 节省带宽,不用在每次请求中发送到服务器端
  • 操作简便,封装了很多便捷的操作方法
  • 数据独立性强

支持web Storage的浏览器

  • chrome4+
  • FF3.5+
  • IE8+  
  • safari4+
  • opera10.5+

JS操作对象

使用 local storage和session storage主要通过在js中操作这两个对象来实现,分别为window.localStorage和window.sessionStorage.

这两个对象均是Storage类的两个实例,自然也具有Storage类的属性和方法。

Storage类的相关成员如下:

成员名

方法参数

描述

length

属性

获取存储数据的条数

key(n)

n:索引值

根据索引值,返回键名

getItem(key)

key:键名

根据键名,获取数据值

setItem(key,value)

key:键名 value:键值

根据键名和键值设置数据项,如果键名已经存在,则覆盖值

removeItem(key)

key:键名

根据键名删除一个数据项

clear()

清空当前的Storage对象

所有支持web Storage的浏览器均实现了以上标准方法,另外IE8还自己实现了remainingSpace用于查看剩余的存储空间

事件:

onstorage,当发生存储相关操作的时候触发

标准中事件对象的属性:

事件属性

描述

key

返回发生改变的数据项的键名 默认空字符串

oldValue

返回发生改变的数据项的旧值 默认null

newValue

返回发生改变的数据项的新值 默认null

url

返回发生改变的数据的页面所对应的url 默认空字符串

storageArea

返回代表所属的storage对象 默认null

其中,webkit内核的浏览器(Chrome、Safari)以及Opera是完全遵循标准的,IE8则只实现了url,Firefox下则均未实现。

各个浏览器对于事件注册的对象也不一致。其中IE和FF是document对象,chrome和opera是window对象,safari是body。

local storage的渐进设计方案

对于不支持的浏览器,可以做渐进设计,考虑的顺序如下:

  • localStorage
  • globalStorage
  • userdata
  • Cookie

session storage 的会话

session storage主要存储会话级别的数据,会话结束后,数据自动销毁。

关于浏览器会话在页面跳转时的理解,各个浏览器实现有些差异,具体表现如下:

浏览器

原窗口

target="_blank"

window.open

ctrl+click

跨域访问

IE8

FF3.6

chrome5

safari4

opera

web storage的安全注意事项:

  • 明文存储,不要存敏感信息
  • 不能抵御xss漏洞攻击
  • 对于存储的数据要严格过滤,防止自身产生存储型xss攻击
  • 容易遭受跨目录攻击
  • 容易遭受DNS欺骗攻击

IndexedDB

HTML5中的NoSQL数据库

indexedDB为何物

在使用一个技术之前,先搞清楚它是什么,这对你的理解很重要,从DB就可以看出,它肯定是一个数据库,而说到数据库,有两种不同类型的数据库,就是关系型数据库和非关系型数据库,关系型数据库如Mysql、Oracle等将数据存储在表中,而非关系型数据库如Redis、MongoDB等将数据集作为个体对象存储。indexedDB就是一个非关系型数据库,它不需要你去写一些特定的sql语句来对数据库进行操作,因为它是nosql的,数据形式使用的是json,

indexedDB出现的意义

也许熟悉前端存储的会说,不是有了LocalStorage和Cookies吗?为什么还要推出indexedDB呢?其实对于在浏览器里存储数据,你可以使用cookies或local storage,但它们都是比较简单的技术,而IndexedDB提供了类似数据库风格的数据存储和使用方式。

首先说说Cookies,英文直接翻译过来就是小甜点,听起来很好吃,实际上并不是,每次HTTP接受和发送都会传递Cookies数据,它会占用额外的流量。例如,如果你有一个10KB的Cookies数据,发送10次请求,那么,总计就会有100KB的数据在网络上传输。Cookies只能是字符串。浏览器里存储Cookies的空间有限,很多用户禁止浏览器使用Cookies。所以,Cookies只能用来存储小量的非关键的数据。

其次说说LocalStorage,LocalStorage是用key-value键值模式存储数据,但跟IndexedDB不一样的是,它的数据并不是按对象形式存储。它存储的数据都是字符串形式。如果你想让LocalStorage存储对象,你需要借助JSON.stringify()能将对象变成字符串形式,再用JSON.parse()将字符串还原成对象。但如果要存储大量的复杂的数据,这并不是一种很好的方案。毕竟,localstorage就是专门为小数量数据设计的,所以它的api设计为同步的。而IndexedDB很适合存储大量数据,它的API是异步调用的。IndexedDB使用索引存储数据,各种数据库操作放在事务中执行。IndexedDB甚至还支持简单的数据类型。IndexedDB比localstorage强大得多,但它的API也相对复杂。对于简单的数据,你应该继续使用localstorage,但当你希望存储大量数据时,IndexedDB会明显的更适合,IndexedDB能提供你更为复杂的查询数据的方式。

indexedDB的特性

1.对象仓库

有了数据库后我们自然希望创建一个表用来存储数据,但indexedDB中没有表的概念,而是objectStore,一个数据库中可以包含多个objectStore,objectStore是一个灵活的数据结构,可以存放多种类型数据。也就是说一个objectStore相当于一张表,里面存储的每条数据和一个键相关联。我们可以使用每条记录中的某个指定字段作为键值(keyPath),也可以使用自动生成的递增数字作为键值(keyGenerator),也可以不指定。选择键的类型不同,objectStore可以存储的数据结构也有差异。

键类型

存储数据

不使用

任意值,但是没添加一条数据的时候需要指定键参数

keyPath

任意值,但是没添加一条数据的时候需要指定键参数

keyGenerator

任意值

都使用

Javascript对象,如果对象中有keyPath指定的属性则不生成新的键值,如果没有自动生成递增键值,填充keyPath指定属性

如上图,有一个用于保存person的object Store,这个仓库的键就是person的ID值。

2. 事务性

在indexedDB中,每一个对数据库操作是在一个事务的上下文中执行的。事务范围一次影响一个或多个object stores,你通过传入一个object store名字的数组到创建事务范围的函数来定义。例如:db.transaction(storeName, \'readwrite\'),创建事务的第二个参数是事务模式。当请求一个事务时,必须决定是按照只读还是读写模式请求访问。

3. 基于请求

对indexedDB数据库的每次操作,描述为通过一个请求打开数据库,访问一个object store,再继续。IndexedDB API天生是基于请求的,这也是API异步本性指示。对于你在数据库执行的每次操作,你必须首先为这个操作创建一个请求。当请求完成,你可以响应由请求结果产生的事件和错误。

4. 异步

在IndexedDB大部分操作并不是我们常用的调用方法,返回结果的模式,而是请求—响应的模式,所谓异步API是指并不是这条指令执行完毕,我们就可以使用request.result来获取indexedDB对象了,就像使用ajax一样,语句执行完并不代表已经获取到了对象,所以我们一般在其回调函数中处理。

indexedDB怎么玩

IndexedDB 鼓励使用的基本模式如下所示:

  1. 打开数据库并且开始一个事务。
  2. 创建一个 object store。
  3. 构建一个请求来执行一些数据库操作,像增加或提取数据等。
  4. 通过监听正确类型的 DOM 事件以等待操作完成。
  5. 在操作结果上进行一些操作(可以在 request 对象中找到)

接下来如果想要理解indexedDB具体怎么玩,最好的方法就是创建一个简单的web应用:把人的姓名、电话、地址存储在IndexedDB里。IndexedDB里提供了简单的增、删、改、查接口,界面如下:

1.打开数据库

a) 首先,你需要知道你的浏览器是否支持IndexedDB。

var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;if(!indexedDB)
{
    console.log("你的浏览器不支持IndexedDB");
}

b) 创建请求打开indexedDB:一旦你的浏览器支持IndexedDB,我们就可以打开它。你不能直接打开IndexedDB数据库。IndexedDB需要你创建一个请求来打开它。

var request = indexedDB.open(name, version);

第一个参数是数据库的名称,第二个参数是数据库的版本号。版本号可以在升级数据库时用来调整数据库结构和数据。但你增加数据库版本号时,会触发onupgradeneeded事件,这时可能会出现成功、失败和阻止事件三种情况:

request.onerror = function(e) {
console.log(e.currentTarget.error.message);
};
request.onsuccess = function(e) {
myDB.db = e.target.result;
console.log(\'成功打开DB\');
};
request.onupgradeneeded = function(e) {        
var db = e.target.result;        
if (!db.objectStoreNames.contains(\'person\')) {
console.log("我需要创建一个新的存储对象");            
//如果表格不存在,创建一个新的表格(keyPath,主键 ; autoIncrement,是否自增),会返回一个对象(objectStore)
var objectStore = db.createObjectStore(\'person\', {
keyPath: "id",
autoIncrement: true
});            
//指定可以被索引的字段,unique字段是否唯一
objectStore.createIndex("name", "name", {
unique: false
});
objectStore.createIndex("phone", "phone", {
 unique: false
            });
        }
console.log(\'数据库版本更改为:\' + version);
};

onupgradeneeded事件在第一次打开页面初始化数据库时会被调用,或在当有版本号变化时。所以,你应该在onupgradeneeded函数里创建你的存储数据。如果没有版本号变化,而且页面之前被打开过,你会获得一个onsuccess事件。

2. 添加数据

a) 首先需要创建一个事务,并要求具有读写权限

var transaction = db.transaction(storeName, \'readwrite\');

b) 获取objectStore,再调用add方法添加数据

var store = transaction.objectStore(storeName);    
var request = store.get(key);
request.onsuccess = function(e) {
data = e.target.result;
console.log(student.name);
};

3.删除数据

删除跟新增一样,需要创建事务,然后调用删除接口,通过key删除对象。

var transaction = db.transaction(storeName, \'readwrite\');    
var store = transaction.objectStore(storeName);
store.delete(key);

4.查找数据

a) 按key查找

开启事务,获取objectStore,调用往get()方法,往方法里传入对象的key值,取出相应的对象

var transaction = db.transaction(storeName, \'readwrite\');    
var store = transaction.objectStore(storeName);    
var request = store.get(key);
request.onsuccess = function(e) {
data = e.target.result;
console.log(student.name);
};

我们可以在创建object store的时候指明索引,使用object store的createIndex创建索引,方法有三个参数:索引名称、索引属性字段名、索引属性值是否唯一。

b) 使用索引查找

objectStore.createIndex("name", "name", {
                unique: false});

如上代码中,我们建好了name索引,就可以用该索引来进行查询了:

var transaction = db.transaction(storeName);    
var store = transaction.objectStore(storeName);    
var index = store.index(search_index);
index.get(value).onsuccess = function(e) {
data = e.target.result;
console.log(student.id);
}

c) 游标遍历数据

对数据库熟悉的同学很好理解游标的作用,有了数据库object store的游标,我们就可以利用游标遍历object store了。

var transaction = db.transaction(storeName);   
var store = transaction.objectStore(storeName);    
var request = store.openCursor();
//打开游标
var dataList = new Array();    
var i = 0;
request.onsuccess = function(e) {        
var cursor = e.target.result;        
if (cursor) {
console.log(cursor.key);
dataList[i] = cursor.value;
console.log(dataList[i].name);
i++;
cursor.continue();
}
data = dataList;
};

4.更新对象

更新对象,首先要把它取出来,修改,然后再放回去。

var transaction = db.transaction(storeName, \'readwrite\');    
var store = transaction.objectStore(storeName);    
var request = store.get(key);
  request.onsuccess = function(e) {        var data = e.target.result;        
  for (a in newData) {            
  //除了keypath之外
  data.a = newData.a;
 }
   store.put(data);
};

5.关闭与删除数据库

关闭数据库可以直接调用数据库对象的close方法

function closeDB(db) {
    db.close();
}

删除数据库使用数据库对象的deleteDatabase方法

function deleteDB(name) {
    indexedDB.deleteDatabase(name);
}

总结

以上就是indexedDB的一些基本概念以及使用,由于篇幅原因,还有一些更深入的细节没有介绍,比如indexedDB的游标结合索引,发挥其真正的优势,有兴趣的小伙伴可以继续深入研究,还有就是要注意浏览器的支持问题,IE9以及更早的版本并不支持,火狐和谷歌浏览器没有问题,推荐使用,文章如果纰漏或者不足,欢迎指正~

WebSQL

HTML5中的关系型数据库

简介

Web SQL数据库API实际上未包含在HTML 5规范之中,它是一个独立的规范,它引入了一套使用SQL操作客户端数据库的API。这些 SQL 语句可以直接在 js中编写运行,并且带有基本的数据库事务性的支持。

兼容浏览器

  • chrome 17+
  • Safari5+
  • opera11.6+
  • iOS Safari3.2+
  • Opera Mobile11.0+
  • Android Browser2.1+

接口

var db = openDatabase(\'mydb\', \'1.0\', \'Test DB\', 2 * 1024 * 1024); 
db.transaction(function (tx) {
   tx.executeSql(\'CREATE TABLE IF NOT EXISTS LOGS (id unique, log)\');
   tx.executeSql(\'INSERT INTO LOGS (id, log) VALUES (1, "foobar")\');
   tx.executeSql(\'INSERT INTO LOGS (id, log) VALUES (2, "logmsg")\');
});
openDatabase  创建/打开数据库,返回数据库的引用
db.transaction 执行数据库事务
tx.executeSql 在事务中执行sql语句

示例

创建数据库

function initDB(){ 
 var myDB = null; 
    try { 
        if (!window.openDatabase) { 
            // 当前浏览器没有数据库支持
            alert(\'db not supported\'); 
        } else { 
            var shortName = \'testdb\'; 
            var version = \'1.0\'; 
            var displayName = \'test offline database\'; 
            var maxSize = 65536; // 字节
            myDB = openDatabase(shortName, version, displayName, maxSize); 
        } 
    } catch(e) { 
        // 这里开始异常处理 . 
        if (e == INVALID_STATE_ERR) { 
            // 数据库版本异常 . 
            alert("Invalid database version."); 
        } else { 
            alert("Unknown error "+e+"."); 
        } 
    } 
    // 返回创建好的数据库实例
    return myDB; 
 }

创建表

function createTables(db){ 
 db.transaction( 
 function (transaction) { 
 transaction.executeSql(\'CREATE TABLE IF NOT EXISTS User(name TEXT, age INTEGER);\', 
   [], function(result){}, function(tx,error){}); 
 } 
 ); 
 }

执行插入语句

 db.transaction( 
 function (transaction) { 
 transaction.executeSql(\'INSERT INTO User values(?,?)\',[“Mark”, 60],
  function(result){}, function(tx,error){}); 
 })

执行查询语句

db.transaction( 
 function (transaction) { 
 transaction.executeSql(\'SELECT * FROM User WHERE name=?\', [name], 
 function(result){}, function(tx,error){});
 })

执行删除语句

db.transaction( 
 function (transaction) { 
 transaction.executeSql(\'DELETE FROM User where name=?\',[name], 
 function(result){}, function(tx,error){}); 
 });

网页存储 学习目标

  • 熟练使用Application Cache
  • 熟练使用本地存储

文件系统

提供客户端本地操作文件的可能

File API

通过file表单或拖放操作选择文件

还可以通过JavaScript读取文件的名称、大小、类型、和修改时间

演示

FileReader

单只是读取文件信息没意思,读内容

FileReader就是用来读取本地文件的对象

演示

文件系统 学习目标

  • 熟练使用File API
  • 利用FileReader在页面本地预览

拖放操作

详解:https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations

网页内拖放

文件拖入

拖放操作 学习目标

  • 学会使用ondragenter
  • 学会使用ondragover
  • 学会使用ondragleave
  • 学会使用ondrop

设备信息访问

HTML5提供了让我们可以访问设备的硬件信息API

网络状态

对于离线应用,经常需要获取当前的网络连接状态

现在浏览器支持比较好的是是否在线

重力感应方向控制

获取移动设备的重力感应信息

DeviceOrientation事件规范

详解https://www.w3.org/html/ig/zh/wiki/DeviceOrientation%E4%BA%8B%E4%BB%B6%E8%A7%84%E8%8C%83

加速度计

坐标系

地理围栏

获取设备所在位置的坐标

演示

设备信息访问 学习目标

  • 课后尝试使用地理位置API结合百度地图做案例
  • 可以写一个摇一摇

原文发布于微信公众号 - Java帮帮(javahelp)

原文发表时间:2018-08-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏纯洁的微笑

分布式爬虫系统设计、实现与实战:爬取京东、苏宁易购全网手机商品数据+MySQL、HBase存储

3203
来自专栏PHP在线

Web开发常见的几个漏洞解决方法

平时工作,多数是开发Web项目,由于一般是开发内部使用的业务系统,所以对于安全性一般不是看的很重,基本上由于是内网系统,一般也很少会受到攻 击,但有时候一些系统...

46111
来自专栏大内老A

WCF 4.0一个鲜为人知的改变

本篇文章介绍可以算是WCF 4.0基于限流(Throttling)的新特性,是在修订《WCF技术剖析(卷1)》的时候编写演示实例的时候发现的。这个特性没有出现在...

2889
来自专栏娱乐心理测试

小程序的网络请求封装

5807
来自专栏Seebug漏洞平台

CVE-2017-16943 Exim UAF漏洞分析--后续

作者:Hcamael@知道创宇404实验室 上一篇分析出来后,经过@orange的提点,得知了meh公布的PoC是需要特殊配置才能触发,所以我上一篇分析文章最后...

3276
来自专栏张善友的专栏

[腾讯社区开放平台]介绍开放授权协议-OAuth

OAuth (开放授权) 是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所...

2707
来自专栏java学习

Hibernate学习笔记1

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hi...

1426
来自专栏安恒网络空间安全讲武堂

Amazing phpinfo()

前记 Xdebug 前记 定义 开启Xdebug 适用目标 实验效果 注意事项 session.upload_progress 定义 开启session.upl...

3656
来自专栏腾讯大数据的专栏

网卡收包流程

0.前言 为提升信鸽基础服务质量,笔者就网络收包全流程进行了内容整理。 网络编程中我们接触得比较多的是socket api和epoll模型,对于系统内核和网卡驱...

2.2K14
来自专栏散尽浮华

Kafka(分布式发布-订阅消息系统)工作流程说明

Kafka系统架构 Apache Kafka是分布式发布-订阅消息系统。它最初由LinkedIn公司开发,之后成为Apache项目的一部分。Kafka是一种快速...

1502

扫码关注云+社区

领取腾讯云代金券