深入剖析 HTML5
最近准备把wap版本升级成web app,考虑直接用html5。 目前,国内主流的UC、opera 都有开始对html5支持,所以大部分会直接支持。
原文地址:http://www.ibm.com/developerworks/cn/web/1212_zhouxiang_deepintohtml5/index.html
作为新一代的 HTML 标准,HTML5 不仅强化了 Web 网页的表现性能,还追加了本地数据库等 Web 应用的功能。虽然 HTML5 标准仍处于完善之中。然而,大部分新版本的浏览器已经能够支持某些 HTML5 的功能了。HTML5 标准所描述的功能非常强大,如:它提供了一些新的元素和属性,例如 <nav>(网站导航块)和 <footer> 等等,这种标签将有利于搜索引擎的索引整理。同样,也加入很多新的 JavaScript 对象和方法,使得我们可以基于更多的接口 API 开发功能更加强大的 web 应用。这篇文章将重点介绍 HTML5 所带给 web 开发工程师们的各种便利以及它的一些使用技巧。
自 1999 年 12 月发布的 HTML 4.01 后,后继的 HTML5 和其它标准被束之高阁,为了推动 Web 标准化运动的发展,一些公司联合起来,成立了一个叫做 Web 超文本应用技术工作组 - WHATWG 的组织,该组织对 HTML5 的发展起了至关重要的作用。HTML5 草案的前身名为 Web Applications 1.0,於 2004 年被 WHATWG 提出,於 2007 年被 W3C 接纳,成立了新的 HTML 工作团队。HTML5 的第一份正式草案已于 2008 年 1 月 22 日公布,总的来说有两大特点:首先,强化了 Web 网页的表现性能。其次,追加了本地数据库等 Web 应用的功能。HTML5 即将成为 HTML、XHTML 以及 HTML DOM 的新标准,虽然目前 HTML5 仍处于完善之中,然而,大部分现代浏览器已经具备了某些 HTML5 支持,所以 HTML5 前景一片看好。
HTML5 的最大的特点之一就是提供了很多新的元素,这些元素相当强大,也非常符合我们日常开发的需要。之前我们花了很大力气写好的一些控件,如:日期控件,进度条控件等等,已经被 HTML5 支持了。在接下来的章节中,我们会介绍一些 Web 开发中比较常用的新元素,并深入介绍他们的各种属性和特性。
HTML5 拥有多个新的表单输入类型。这些新特性提供了更好的输入控制和验证。来看一个简单示例:
<input type="email" name="user_email" /> <input type="url" name="user_url" /> |
这里是一个简单的 email、url 输入框使用示例,可见,我们只需要设定他的 type 属性即可。如果设定为 email,则浏览器会验证他的输入是否合法(必须为邮件格式),见下图:(支持浏览器 Firefox,Chrome 和 Opera)
Email 输入框也支持输入多个,只需要用“,”分隔开来即可,浏览器会对这些分隔开来的字符串分别验证。
让我们在深入一点,以下是浏览器验证所对等的正则表达式:
“/^[a-zA-Z0-9.!#$%& ’ *+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/”
这种验证如果有我们自己来做,会耗费大量时间,而且性能不会太好。值得庆幸的是:随着各大浏览器的不断完善,越来越能更好的支持 HTML5,不久的将来,我们就不用再考虑如何做这些验证了。
同理,“url”类型也是。这样一来,节省了大量的验证代码,使用起来极为方便。
再来看数字输入框的例子:
<input type="number" name="points" min="1" max="10" step="1"/> <input type="range" name="points" min="1" max="10" /> |
这里的 step 表示规定的合法的数字间隔。他们的效果图如下:(支持浏览器 Chrome,Safari 和 Opera)
这些数字输入控件使用方便且非常直观,由于是内置的控件,我们完全不用担心性能的问题,所以,强烈建议大家试用。
接下来我们再来看两个使用比较广泛的控件 - date 和 time。HTML5 拥有多个可供选取日期和时间的新输入类型:
1. date - 选取日、月、年
2. month - 选取月、年
3. week - 选取周和年
4. time - 选取时间(小时和分钟)
5. datetime - 选取时间、日、月、年(UTC 时间)
6. datetime-local - 选取时间、日、月、年(本地时间)
参见如下示例:
<input type="date" name="user_date" /> <input type="month" name="user_date" /> <input type="time" name="user_date" /> <input type="datetime" name="user_date" /> |
使用方式依然简单,可以参见如下效果图:(支持浏览器 Chrome,Safari 和 Opera)
如图 3 所示,这里列出了选月(Month)和选时间加日期(DateTime)的效果图。这些都是内置的一些控件,无需我们额外开发了。除此以外,还有“datatime-local”的类型,专门针对不同的国家实现本地化,功能相当完善。
还有“search”控件,专门用来作为搜索框出现在页面上,样式与文本框相同。以上这些简单的新输入控件使用便捷且性能颇高。
再来看看稍微复杂一些的控件:数据列表,在 HTML5 中被称作“datalist”,用来显示列表数据,类似一个简单的 select 控件。这个控件简单清晰,方便选择且支持输入,在如今的 Web 开发中越来越受到青睐,见如下示例:
<input type="url" list="url_list" name="link" /> <datalist id="url_list"> <option label="W3School" value="http://www.W3School.com.cn" /> <option label="Google" value="http://www.google.com" /> <option label="Microsoft" value="http://www.microsoft.com" /> </datalist> |
可见,datalist 控件的用法类似 select,参见如下效果图:(支持浏览器 Firefox,Chrome 和 Opera)
在我们输入时,他会自动弹出备选项,这种控件非常人性化,用户体验极好。
再来看看 keygen 元素,keygen 元素的作用是提供一种验证用户的可靠方法。keygen 元素是密钥对生成器(key-pair generator)。当提交表单时,会生成两个键,一个是私钥,一个公钥。私钥(private key)存储于客户端,公钥(public key)则被发送到服务器。公钥可用于之后验证用户的客户端证书(client certificate)。
<form action="demo_form.asp" method="get"> Username: <input type="text" name="usr_name" /> Encryption: <keygen name="security" /> <input type="submit" /> </form> |
这里我们加入了一个“name”为“security”的 Keygen 控件,在该表单提交时,浏览器会提示您输入密码以确保提交安全,如下图所示:(支持浏览器 Firefox,Chrome,Safari 和 Opera)
如图 5 所示,点击“submit”时,会弹出“password”对话框。通过很简单的代码,我们便能实现安全的表单提交。
目前,支持此元素的浏览器不多,不过相信各大浏览器厂商也都想早日完成 HTML5 完全支持的目标。
最后,我们来看看 output 元素,该元素很简单,专门用来输出,见如下示例:
function resCalc() { numA=document.getElementById("num_a").value; numB=document.getElementById("num_b").value; document.getElementById("result").value=Number(numA)+Number(numB); } <form onsubmit="return false"> <input id="num_a" /> + <input id="num_b" /> = <output id="result" onforminput="resCalc()"></output> </form> |
这里我们通过“onforminput”监听表单输入事件,通过对 Output 元素“value”属性的赋值来显示输出,代码简洁且通俗易懂。(支持浏览器 Firefox,Chrome,Safari 和 Opera)
前面我们介绍了新的表单相关元素,接下来我们来看看新的一些元素属性。HTML5 中提到的新属性非常多,这些属性控制着很多非常实用的功能,如:autocomplete(自动补全)、required(不能为空)、placeholder(提示符)等等,还有 autofocus、list、min、max、pattern、novalidate 等等。下面我们来一一介绍:
<form action="demo_form.asp" method="get" autocomplete="on"> First name: <input type="text" name="fname" /><br /> Last name: <input type="text" name="lname" /><br /> E-mail: <input type="email" name="email"autocomplete="off"/><br /> <input type="submit" /> </form> |
如果我们设置了“autocomplete”属性为“on”,那么当用户在自动完成域中开始输入时,浏览器会在该域中显示填写的选项。参见图 6:(支持浏览器 IE9,Firefox,Chrome,Safari 和 Opera)
我们再来看看关于 Form 的几个特殊属性:表单重写属性。表单重写属性(form override attributes)允许您重写 Form 元素的某些属性设定。
表单重写属性如下:
- formaction - 重写表单的 action 属性
- formenctype - 重写表单的 enctype 属性
- formmethod - 重写表单的 method 属性
- formnovalidate - 重写表单的 novalidate 属性
- formtarget - 重写表单的 target 属性
重写后,Form 会按照您重写的后的方案去执行。参考如下代码:
<form action="demo_form.asp" method="get" id="user_form"> E-mail: <input type="email" name="userid" /><br /> <input type="submit" value="Submit" /> <br /> <input type="submit" formaction="demo_admin.asp"value="Submit as admin" /> <br /> <input type="submit"formnovalidate="true"value="Submit without validation" /> <br /> </form> |
清单 8 中,我们就分别重写了 Form 表单的“formaction”和“formnovalidate”属性。在我们点击“Submit as admin”按钮时,Form 会提交到“demo_admin.asp”,点击“Submit without validation”按钮时,Form 不会做任何校验。(支持浏览器 Firefox,Chrome,Safari 和 Opera)
关于按钮,HTML5 也多了一些处理,比如“image”形式的按钮:
如果我们通过 Get 方式提交,则 url 参数会变成“user_name=aaa&x=52&y=51”,后面多出来的坐标信息可以用来判断用户的点击区域,以进行不同的后台处理。
接下来我们来看看三个比较实用的新属性:Pattern,Placeholder 和 Required。(支持浏览器 Firefox,Chrome,Safari 和 Opera)
Country code: <input type="text" name="country_code" pattern="[A-z]{3}"title="Three letter country code" /> |
可以看到,这里我们给输入框定义了 Pattern 为“[A-z]{3}”,也就是包含三个字母的正则表达式约束,如果输入不合法,我们会看到如下效果:(支持浏览器 Firefox,Chrome 和 Opera)
Pattern 这个属性让我们不用在通过 JavaScript 来绑定验证了。
再来看看 Placeholder,这个属性主要用来在用户未输入时,提示用户输入的数据的形式或者直接给出输入示例:
当该输入框获取焦点时,提示字符消失。参考如下代码:(支持浏览器 Firefox,Chrome,Safari 和 Opera)
<input type="search" name="user_search" placeholder="Search W3School" /> |
最后我们来看看 Required 属性,它主要就是要求用户必须有输入,否则校验不通过。他的代码很简单:
Name: <input type="text" name="usr_name" required="required" /> |
新的表单元素的相关属性还有很多,我们这里不一一介绍了,有兴趣的读者可以参考官方文档。(支持浏览器 Firefox,Chrome 和 Opera)
除了新的表单元素,还有很多其他的功能强大的新元素。
<video width="320" height="240" controls="controls"> <source src="movie.ogg" type="video/ogg"> <source src="movie.mp4" type="video/mp4"> Your browser does not support the video tag. </video> |
顾名思义,这是用来播放视屏的。这个元素的诞生,意味着我们不用再使用第三方插件了,无论是从性能还是从易用性上都大大优于从前。现在 Firefox、Chrome、Opera 等的最新版本都支持 Video 元素,现在主要支持 Ogg 和 MPEG 两种视屏格式。您还能够通过“controls”属性设置是否显示控制按钮,“preload”是否在页面加载时预加载视屏等等。(支持浏览器 IE9,Firefox,Chrome,Safari 和 Opera)
让我们再了解得深入一点:事实上,HTML5 的 Video 元素的功能远远不止这些内容。它有很多属性:
- currentSrc:当前的视屏源
- initialTime:初始化的时间点
- readyState:当前视屏播放的状态
- currentTime:视频的当前时间
- duration:视频的时间总长
- playbackRate:播放的速率,通过该属性可以实现快进,慢放等等
- autoplay:是否自动播放
- muted:是否静音
这里列举出相对比较实用的一些属性。它总共有 30 多个常用的属性功能,感兴趣的读者可以参考 W3C 官方网站进一步了解。
同样,它也有很多实用的方法和事件:
load()、play()、pause() 等等控制着视频的加载,播放和暂停。可监听的事件也是相当丰富:
- "onloadstart" // 客户端开始请求数据时触发
- "onprogress" // 客户端请求数据过程中
- "onabort" // 客户端主动终止下载时触发
- "onerror" // 请求数据时遇到错误时触发
- "onplay" //play() 和 autoplay 开始播放时触发
- "onpause" //pause() 时触发
- "onloadedmetadata" // 成功获取资源长度时触发
- "onloadeddata" // 成功获取视频资源时触发
- "oncanplay" // 视频可以播放时触发(但中途可能因为加载而暂停)
- "oncanplaythrough" // 可以播放,视频 / 音频全部加载完毕
- "ontimeupdate" // 播放时间改变时触发
- "onended" // 播放结束时触发
- "onratechange" // 播放速率改变时触发
- "ondurationchange" // 资源长度改变时触发
- "onvolumechange" // 音量改变时触发
由于事件种类很多,这里我们也只能列举出一部分相对比较实用的事件。由此可见,Video 标签是足够强大的,它完全有能力取代任何一种嵌入式的 Web 媒体播放器插件。
<audio controls="controls"> <source src="song.ogg" type="audio/ogg"> <source src="song.mp3" type="audio/mpeg"> Your browser does not support the audio tag. </audio> |
Audio 使用方法与 Video 类似,同样可以设定“controls”、“preload”等等属性。效果图如下:(支持浏览器 IE9,Firefox,Chrome,Safari 和 Opera)
Audio 元素的属性,方法和事件也是非常丰富的。它的这些功能和 Video 非常相似,读者也可以参考 Video 的相关介绍。
接下来我们来看看 Canvas 元素:(支持浏览器 IE9,Firefox,Chrome,Safari 和 Opera)
<script type="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.moveTo(10,10); cxt.lineTo(150,50); cxt.lineTo(10,50); cxt.stroke(); var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.fillStyle="#FF0000"; cxt.beginPath(); cxt.arc(70,18,15,0,Math.PI*2,true); cxt.closePath(); cxt.fill(); </script> |
Canvas 用来画矢量图,它提供了很多画矢量图的方法,而且效率非常好。
让我们深入剖析一下 Canvas 的功能:
- rect(x, y, width, height):直接画矩形,(moveTo 直接定位到 (0,0) 位置)
- fillRect(x,y,width,height):画一个填充的矩形
- strokeRect(x,y,width,height):画一个矩形边框
- clearRect(x,y,width,height):清空一块矩形区域并且设为透明
- arc(x, y, radius, startAngle, endAngle, anticlockwise):(x,y) 是圆弧的圆心,radius- 半径,startAngle 和 endAngle 是圆弧的开始和结束弧度,anticlockwise 为 true 是逆时针,否则为顺时针。
- quadraticCurveTo(cp1x, cp1y, x, y) / bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):(cp1x, cp1y),(cp2x,cp2y) 是贝塞尔曲线的控制点,(x,y) 是曲线的终点。
- drawImage(image, x, y, width, height):image 这里指图像对,width 和 height 是目标 canvas 上图像的宽高。
我们还能够设置线条样式:
- lineWidth:线条宽度
- lineCap:线条的端点类型可以是 butt( 默认 ),round 和 square
- lineJoin:连接线的类型,有 round,bevel 和 miter(默认)
- miterLimit:当设置 miter 时的选项
- Canvas 还支持渐变,该渐变能直接应用到各种填充效果中:
- createLinearGradient(x1,y1,x2,y2):(x1,y1) 到 (x2,y2) 的线性渐变
- createRadialGradient(x1,y1,r1,x2,y2,r2):(x1,y1,r1) 的圆到 (x2,y2,r2) 的圆的径向渐变
- addColorStop(position, color) 为 canvasGradient 对象添加颜色阶段,position 为 [0,1] 区间的值,代表添加颜色的位置,color 表示添加的颜色(如:#fff,rgba(0,0,0,1) 等)
接下来是一些更为高级的接口:
- ctx.shadowOffsetX:阴影 x 偏移
- ctx.shadowOffsetY:阴影 y 偏移
- ctx.shadowBlur:模糊度
- ctx.shadowColor:阴影颜色
- translate(x, y):移动 canvas 坐标
- rotate(angle):旋转的中心是 canvas 坐标原点,angle 是旋转的角度,可以通过 translate 来移动 canvas 的坐标,然后再旋转。
- scale(x, y):x 是水平方向的缩放因子,y 是垂直方向的缩放因子,必须都为正数
- transform,setTransform 等方法用于坐标变换(线性代数)
由此可见,Canvas 的功能是非常强大的,足以满足各种 Web 矢量图开发的需要。
感兴趣的读者还可以看看 WebGL,现在 Firefox、Chrome、Safari 和 Opera 的新版本基本都支持 WebGL 了(需要在浏览器中开启)。WebGL 定义了一套 API, 能够允许在网页中使用类似于 Open GL 的接口,实际上是一套基于 OpenGL ES 2.0 的 3d 图形 API。这些 API 是通过 HTML 5 的 canvas 标签来使用的。WebGL 主要用于 3D 矢量图,有更为强大的矢量图绘制接口。给出一段代码示例:
<canvas id="canvas_object" style="border:1px solid red;" width='600px' height='450px'></canvas> var canvas = document.getElementById( "canvas_object" ); g_WebGLContext = canvas.getContext( "experimental-webgl" ); // setup viewport g_WebGLContext.viewport(0, 0, canvas.width, canvas.height); // set clear color g_WebGLContext.clearColor( 0.0, 0.0, 0.0, 1.0 ); // create shader and data CreateShader(); CreateData(); // main loop setInterval( drawScene, 30 ); |
如清单 15 所示,WebGL 的代码基于 Canvas,与 Canvas 相关代码类似。WebGL 主要用于 3D 效果的增强,通过 WebGL 往往可以实现出我们意想不到的 3D 矢量图效果。
接下来我们来看看新的一些结构布局相关的元素:
如图 11 可以一目了然,这些新的元素(header, nav, section, article, footer 等等)本身其实类似于一个 <div>, 但是他们会有默认的布局方式,用户不用再用 div 加上 css 样式来模拟布局了。除此以外,如果用户在 <section> 标签内加入“contenteditable='true'”的属性,该 <section> 里面的内容便可以编辑了。有需求的读者可以关注一下这里。
再来看看 Details 和 Summary 标签,这两个标签通常配合使用,实现“总结 - 详细”信息的折叠效果:
其代码很简单,如下:(支持浏览器 Chrome)
<details> <summary>Name & Extension:</summary> <p><input type=text name=fn value="Pillar Magazine.pdf"/></p> <p><label><input type=checkbox name=ext checked/>Hide extension</label></p> </details> |
它们也可以和 <legend> 标签协同使用,有兴趣的读者可以深入研究一下。
接下来我们来看看 Dialog 元素,这里我们以一段对话为例:
<dt>Role A</dt> <dd>Do you like HTML5?</dd> <dt>Developer</dt> <dd>Yes<dd> |
这里,我们可以把 <dt> 作为说话的人,<dd> 作为说话的内容,其效果如下:
可见,对这些新元素,浏览器的解析更加智能化,为我们省去了很多样式的工作。
还有一些小标签如 <mark>,主要用于高亮显示一段话中的某些文字,<meter> 用于表示一个范围内的值,示例如下:
<p>your score is : <meter value="88.7" min="0" max="100" low="65" high="96" optimum="100">B+</meter> </p> |
其对应的样式如下:(支持浏览器 Chrome 和 Opera)
分两种情况,如果浏览器支持 <meter> 标签,则显示左边样式,否则,显示右边的样式。
同样,还有 <progress> 标签,专门用于显示进度:
<progress value="40" max="100">40%</progress> |
有了这些新的元素,我们不用再像以前那样通过很多 HTML,CSS 和 JavaScript 代码来模拟这样一个控件了。(支持浏览器 Firefox、Chrome 和 Opera)
在来看一个稍微复杂一点的元素:Menu。其实它的使用方式也非常简单,代码如下:
<menu type="toolbar"> <li> <menu label="File" type="popup"> <button type="button" onclick="file_new()">New...</button> <button type="button" onclick="file_open()">Open...</button> <button type="button" onclick="file_save()">Save</button> </menu> </li> <li> <menu label="Edit" type="popup"> <button type="button" onclick="edit_cut()">Copy</button> <button type="button" onclick="edit_copy()">Cut</button> <button type="button" onclick="edit_paste()">Paste</button> </menu> </li> </menu> |
这里的 Menu 元素内部的 Button 元素也可以改成 Command 元素,显示效果和 Button 元素类似,同时 Command 也支持 Checkbox,Radio 的形式,只需要设置他的“type”属性即可。(支持浏览器 Opera)
他的效果和很多现有的 Web 控件库里的 Menu 控件差不多,但是他是 Native 的元素,效率会快很多。
HTML5 的新元素还有很多,如 <datagrid>,<figure>,<datatemplate> 等等,这里我们不一一介绍,有兴趣的读者可以参考 W3C 的官方文档深入研究。
最后我们来看几个高级的专题,这些元素可能我们很少会用到,不过在某些特殊环境或者需求下可能就不一定了,还是很有了解一下的必要的。
首先,我们来看看 <a> 标签中的“rel”属性,它主要用来规定当前文档与目标 URL 之间的关系(仅在 href 属性存在时使用)。先来看一个例子:
<a rel="noreferrer" href="http://www.functravel.com/">Cheap Flights</a> |
这里的‘rel="noreferrer"’表示,当用户跟随该超链接(http://www.functravel.com/)时,浏览器不会发送 HTTP referer 头信息。“rel”属性还有很多可设定的值,如“alternate”,“archieve”,“author”,“external”,“index”等等,这里不再一一介绍。(支持浏览器 IE9、Firefox、Chrome,Safari 和 Opera)
再来看看 HTML5 中的 Microdata Model,它不是一个元素,也不是一个属性,他是一个模型,由一系列相关属性构成,主要用来标识 HTML 文档结构(Document),使其更加容易被计算机理解。如果您在您的 HTML 文档结构中加入了一些 Microdata 的标识描述,您的文档将更加容易被“理解”和搜索到,如 Google 的搜索引擎就能更迅速的搜索您的页面并且在查询结果中更为清晰的给出关于该页面的相关信息。来看这样一个示例:
<div itemscope itemtype="http://data-vocabulary.org/Person"> My name is <span itemprop="name">Bob Smith</span> but people call me <span itemprop="nickname">Smithy</span>. Here is my home page: <a href="http://www.example.com" itemprop="url">www.example.com</a> I live in Albuquerque, NM and work as an <span itemprop="title">engineer</span> at <span itemprop="affiliation">ACME Corp</span>. </div> |
这段 HTML 中加入了“itemtype”,“itemprop”等等属性,其实是用另一种方式标识该文档结构。Google 引擎爬到该页面时,会解析出所有您的 Microdata 信息,从而更好的展示了该页面的总体内容概览。而不是想普通页面那样的对其内容的一段默认的截取,这段截取的文字往往很难满足我们的预期。
HTML5 还有一些关于可用性(ARIA)和 Sandbox 的专题,这里不再深入,建议有兴趣的读者参考 W3C 官方文档。
这一节我们重点看看 HTML5 带给我们那些 JavaScript 常用的对象和方法。这些对象和方法可能我们已经梦寐以求很久了,现在他们终于来了!
先来了解一下 Storage 吧,主要用于客户端的持久化数据存储,分为三种类型:GlobalStorage、LocalStorage 和 SessionStorage,使用方式非常简单:(支持浏览器 IE8+,Firefox,Chrome,Safari 和 Opera)
globalStorage['developer.mozilla.org'].username = "John"; localStorage.username = "John"; sessionStorage.username = "John"; sessionStorage.setItem("username", "John"); |
“globalStorage”:不属于标准,Firefox 特有的对象,它可以跨域存取数据。
“localStorage”:主要针对同一个域下面数据的存取。
“sessionStorage”:顾名思义,其生命周期为一个 session,刷新标签页时数据保留,但是当标签页关闭时,sessionStorage 内存储的数据会消失。
以上三种 Storage 用户可以根据自己的需要来选择。
再来看一个“navigator”的“onLine”的属性,我们可以通过该属性值得知当前浏览器的运行模式:在线还是离线。(支持浏览器 Firefox 和 Opera)
图 18. “navigator”的 onLine 属性设置
当我们选择“Work Offline”时,“onLine”的属性值会置“false”,否则默认为“true”。
再来看几个新的方法。对于“getElementById”、“getElementsByTagName”等等这些定位页面元素的方法想必大家已经非常熟悉了,HTML5 又推出了“getElementsByClassName”,通过 Class 的名称检索元素集。使用方式如下:(支持浏览器 Firefox,Chrome,Safari 和 Opera)
var elements = document.getElementsByClassName('section'); elements[0].focus(); |
可以看出,其使用方式与“getElementsByTagName”基本无异,返回值同“getElementsByTagName”一样,是 HTMLCollection 类型。
建议大家留意一下“querySelectorAll”方法,这种基于 CSS Selector 检索元素的方法对我们的日常开发也是十分有帮助的。
var els = document.querySelectorAll("ul li:nth-child(odd)"); var els = document.querySelectorAll("table.test > tr > td"); |
接下来我们来看看关于浏览器历史(History)对象的一些新方法。
我们知道,使用“back()”,“forward()”和“go()”方法可以在用户的历史记录中前进和后退,但是针对 Web2.0 的零页面跳转的需求,HTML5 引入了“histtory.pushState()”和“history.replaceState()”这两个方法。它们允许在无页面跳转的情况下添加和修改 History 实体,即操作浏览器历史,并且改变当前页面的 URL(pushState 用于向 history 添加当前页面的记录,而 replaceState 和 pushState 的用法完全一样,唯一的区别就是它用于修改当前页面在 history 中的记录)。同时,这些方法还会和“window.onpopstate”事件一起工作。我们来看看下面的示例:(支持浏览器 Firefox、Chrome、Safari 和 Opera)
var stateObj = { myVar: "myValue" }; var stateObj2 = { myVar: "myValue2" }; history.pushState(stateObj, "history page", "hisPage.html"); history.pushState(stateObj, "history page 2", "hisPage2.html"); window.onpopstate = function(e) { console.log(e.state); }; |
这种方法将会使 URL 地址栏显示“http://yoursiteaddress.com/hisPage.html”,但浏览器不会加载“hisPage.html”页面,即使这个页面存在也不会加载。
如果现在用户继续访问一个新的网址(如 http://sina.com.cn),然后点击浏览器的后退。这时,地址栏将会显示“http://yoursiteaddress.com/hisPage.html”,同时页面会触发“popstate”事件,“stateObj”会从“popstate”事件传入供我们使用。如果我们再次点击后退,URL 将变成“http://yoursiteaddress.com/hisPage2.html”,同时页面会再次触发“popstate”事件。但是注意,这两次的回退不会造成页面的刷新。
同样,“onhashchange”事件也值得我们关注一下,它专门用于响应 URL 的 hash 值的变化,我们不用再像以前那样轮询地检测 hash 值的变化了。
最后,有兴趣的读者还可以看看“onpageshow”和“onpagehide”,他们对应于“onload”和“onunload”事件。页面加载时默认触发“onload”事件,但是如果页面是从“往返缓存”里面加载的,则触发“onpageshow”等事件。
接下来再来看一个跨域消息传递的使用。HTML5 里面推出了“postMessage”方法用于 Iframe 之间的消息传递,支持跨域传递。这意味着,我们不用再像以前那样通过 hash 值等等办法实现跨域 Iframe 的消息通讯,先看如下示例:
如果我们要实现如图 19 的功能(这里 iframe1、iframe2 和主界面都是在不同的域),如果 iframe1 需要发消息给 iframe2,只需要在 iframe1 中取得 iframe2 的对象,调用“postMessage”方法即可,代码参考如下:(支持浏览器 Firefox,Chrome,Safari 和 Opera)
//iframe1 window.parent.frames[1].postMessage(message, 'http://dm1.com'); //iframe2 if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', messageHandler, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onmessage', messageHandler); } function messageHandler(e){ if(event.origin !== http://dm1.com') return; var data = e.data; ...... } |
不难发现,这种做法会即时响应消息,从而让我们彻底摆脱过去轮询机制实现的麻烦。一方“postMessage”,另一方监听“onmessage”事件,代码非常清晰。
接下来是“Notifications”,又称“桌面提醒接口”,HTML5 的又一新特性,通过这个接口我们能直接让浏览器发消息到我们的桌面:
if (window.webkitNotifications) { if (window.webkitNotifications.checkPermission() == 0) { window.webkitNotifications.createNotification("xx.png", "Title", "Body").show(); } else { window.webkitNotifications.requestPermission(); return; } } |
可见,使用方法非常简单(createNotification),其效果如下:(支持浏览器 Chrome 和 Safari)
再来看看 HTML5 提供的浏览器缓存的相关接口(ApplicationCache),它可以用来支持离线浏览。
首先,通过“<html manifest="html5demo.manifest" lang="en">” 来引用清单文件,清单文件代码示例如下:
CACHE MANIFEST # 可缓存的文件 CACHE: /favicon.ico index.html stylesheet.css images/logo.png scripts/main.js # 无论是否处于离线状态,对这些资源的所有请求都会绕过缓存。即必须在线访问的文件。可使用通配符。 NETWORK: login.php /myapi # 无法访问资源时的后备资源 FALLBACK: /main.py /static.html images/large/ images/offline.jpg *.html /offline.html |
如上,“index.html”、“stylesheet.css”会自动保存到我们浏览器的缓存(ApplicationCache)中,当我们的网络断开时,访问这些文件时,浏览器会直接从缓存中取,让我们感觉像是在在线浏览网页一样。同时,我们也可以查看当前浏览器缓存的状态,手动更新缓存以及监听缓存相关事件并及时处理:(支持浏览器 Firefox、Chrome、Safari 和 Opera)
清单 30. 缓存(ApplicationCache)相关接口
// 查看当前浏览器缓存的状态 var appCache = window.applicationCache; switch (appCache.status) { case appCache.UNCACHED: // UNCACHED == 0 return 'UNCACHED'; break; case appCache.IDLE: // IDLE == 1 return 'IDLE'; break; case appCache.CHECKING: // CHECKING == 2 return 'CHECKING'; break; case appCache.DOWNLOADING: // DOWNLOADING == 3 return 'DOWNLOADING'; break; case appCache.UPDATEREADY: // UPDATEREADY == 4 return 'UPDATEREADY'; break; case appCache.OBSOLETE: // OBSOLETE == 5 return 'OBSOLETE'; break; default: return 'UKNOWN CACHE STATUS'; break; }; // 手动更新缓存 var appCache = window.applicationCache; appCache.update(); // 更新缓存 if (appCache.status == window.applicationCache.UPDATEREADY) { appCache.swapCache(); } // 监听缓存相关事件并及时处理 var appCache = window.applicationCache; function logEvent(e) { console.log(e); } function logError(e) { console.log("error " + e); }; appCache.addEventListener('cached', logEvent, false); appCache.addEventListener('checking', logEvent, false); appCache.addEventListener('downloading', logEvent, false); appCache.addEventListener('error', logError, false); appCache.addEventListener('noupdate', logEvent, false); appCache.addEventListener('obsolete', logEvent, false); appCache.addEventListener('progress', logEvent, false); appCache.addEventListener('updateready', function (e) { appCache.swapCache(); window.location.reload(); }, false); |
调用“applicationCache.update()”是以编程方式更新缓存,此操作将尝试更新用户的缓存(前提是已更改清单文件)。当 “applicationCache.status”处于 “UPDATEREADY” 状态时,调用“applicationCache.swapCache()”即可将原缓存换成新缓存。这里我们要注意:这里我们使用“update()”和“swapCache()”是不会向用户提供更新的资源的。此方法只是让浏览器检查是否有新的清单(html5demo.manifest 是否有变化),下载指定的更新内容以及重新填充应用缓存。
HTML5 中增加了文件操作的接口(FileReader 和 FileWriter),我们可以直接在 JavaScript 操作文件了:(支持浏览器 Firefox、Chrome 和 Opera)
//e.dataTransfer.files[0] 为文件的 url var file = e.dataTransfer.files[0], reader = new FileReader(); reader.onload = function(event){ holder.style.background = 'url(' + event.target.result + ') no-repeat center'; }; reader.readAsDataURL(file); |
这里我们主要调用“FileReader”的“readAsDataURL”方法读取文件,文件通过流读入完毕后,会回调“FileReader”的“onload”方法,并传入文件流内容(event.target.result),注意,此数据为二进制文件流数据,不再是文件地址。
同样,FileWriter 的用法也类似:
window.requestFileSystem(window.PERSISTENT, 1024*1024, onInitFs, errorHandler); function onInitFs(fs) { fs.root.getFile('test.txt', {create: false}, function(fileEntry) { // 创建 FileWriter('test.txt') fileEntry.createWriter(function(fileWriter) { fileWriter.seek(fileWriter.length); fileWriter.truncate(1); // 创建一个二进制流对象并写入文件'test.txt'. var bb = new BlobBuilder(); bb.append('Hello World'); fileWriter.write(bb.getBlob('text/plain')); }, errorHandler); }, errorHandler); } |
不难看出,文件对象通过“createWriter”构建出“fileWriter”,并通过“fileWriter”的“write”方法写入文件。
这些对象和接口可能不一定很常用,但是还是希望大家能够关注一下,也许以后在某些特殊的情况下我们会非常需要他们。
WebSocket 是 HTML5 的又一个令人振奋的功能,它使得我们的 Web 前端能够和服务器端进行全双工通信。这是一次 Web 前端通信机制的飞跃,基本上可以完全取代我们之前的长轮询模式。WebSocket 的使用方式非常简单,如下:(支持浏览器 Chrome 和 Safari)
var socket = new WebSocket(location); socket.onopen = function(event) { socket.send(“Hello, WebSocket”); // ”postMessage” } socket.onmessage = function(event) { alert(event.data); } socket.onclose = function(event) { alert(“closed”); } |
熟悉 Socket 编程的人可能一看就明白了,其实 WebSocket 的接口与 Java 开发中常用的 Socket 的接口非常相似,通过“send”发送请求,“onmessage”来处理返回的消息。WebSocket 对象有三个事件:open,close 和 message。当连接建立时触发 open 事件,当收到消息时触发 message 事件,当 WebSocket 连接关闭时触发 close 事件。同大多数 JavaScript API 一样,事件处理时会调用相应的“onopen”, “onmessage”, 和“onclose”回调函数。
最后我们来看看 HTML5 支持的地理信息定位,接口非常简单:
<script> function getLocation(){ if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(showPosition); } else { console.log("Geolocation is not supported"); } } function showPosition(position){ console.log("Latitude: " + position.coords.latitude + "<br />Longitude: " + position.coords.longitude); } getLocation(); </script> |
很明显,通过“getCurrentPosition”获取地理经纬度信息,通过回调函数“showPosition”基于地理信息作出相应处理,“position”对象里除了“latitude”和“longitude”之外,还有诸如“accuracy”,“altitude”,“speed”等等很多有用的值,希望大家注意一下。(支持浏览器 IE9、Firefox、Chrome,Safari 和 Opera)
拖拽是 HTML5 中的一个亮点,HTML5 已经能直接支持拖拽功能了,其使用方式也非常简单,参见如下代码:(支持浏览器 IE9、Firefox、Chrome、Safari 和 Opera)
// 便利所有可拖拽的 link var links = document.querySelectorAll('li > a'); ................ // 设置可拖拽属性,非必须 links[i].setAttribute('draggable', 'true'); // 绑定推拽起始事件 , addEvent 是根据浏览器选择 addEventListener 及 attachEvent 等等 addEvent(links[i], 'dragstart', function(e){ e.dataTransfer.effectAllowed = 'copy'; // 设置拖拽传递的数据 e.dataTransfer.setData('Text', this.id); }); // 拖拽释放的容器 var dropContainer = document.querySelector('#dropContainer'); // 监听拖拽事件 addEvent(dropContainer, 'dragover', function(e){ if (e.preventDefault) e.preventDefault(); // allows us to drop this.className = 'over'; ............ }); // 拖入 dropContainer 响应 addEvent(dropContainer, 'dragenter', function(e){ this.className = 'over'; return false; }); // 拖出 dropContainer 响应 addEvent(dropContainer, 'dragleave', function(){ this.className = ''; }); // 拖拽释放事件 addEvent(bin, 'drop', function(e){ // 获取推拽对象 var sourceElement = document.getElementById(e.dataTransfer.getData('Text')); // 进行相关处理 sourceElement.parentNode.removeChild(el); ......... }); |
不难看出,我们只需要在开始拖拽时设定好拖拽传递的数据,最后在释放容器里(dropContainer)实现一下拖拽元素释放的处理,整个拖拽功能就算是实现完毕了,整个过程清晰简单明了。HTML5 的拖拽还支持文件,用户可以直接拖拽系统中的文件,释放到页面的释放容器里(dropContainer),可以从“e.dataTransfer.files”变量里获取拖拽的文件进行相关处理。
再来看看 Web Database,关于 Web 的数据库这一特性其实并不是 HTML5 标准(规范)的一部分,它是一个独立于 HTML5 之外的一个标准,专门用于定义前端的基于 SQL 语言的数据库操作接口。其实该接口非常简单,主要还是写 SQL 语句,相信大家应该还是很熟悉的:(支持浏览器 Chrome、Safari 和 Opera)
<html> <head> <script type="text/javascript"> var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); var msg; 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")'); msg = '<p>Log message created and row inserted.</p>'; document.querySelector('#status').innerHTML = msg; }); db.transaction(function(tx){ tx.executeSql('SELECT * FROM LOGS', [], function(tx, results){ var len = results.rows.length, i; msg = "<p>Found rows: " + len + "</p>"; document.querySelector('#status').innerHTML += msg; for (i = 0; i < len; i++) { msg = "<p><b>" + results.rows.item(i).log + "</b></p>"; document.querySelector('#status').innerHTML += msg; } }, null); }); </script> </head> <body> <div id="status" name="status"> Status Message </div> </body> </html> |
这里的“openDatabase”接口可以打开已有数据库或者创建一个新的数据库,然后基于“transaction”接口开启事物处理,开始执行相关 SQL 语句(executeSql)。除了创建表,添加记录以外,他也能够检索表记录(results),取得结果并与外界 HTML&JavaScript 做交互。
我们来详细了解一下他的接口:
打开数据库:
openDatabase( dbName, dbVersion , dbDisplayName , dbEstimatedSize , callbackFun)
以下为参数释义:
- dbName:数据库实例名称,自定义
- dbVersion:数据库版本,目前为 1.0
- dbDisplayName:数据库显示的名称
- dbEstimatedSize:数据库预估的大小
- callbackFun:回调函数
打开数据库的事务:
db.transaction(callbackFun,errorCallbackFun,sucessCallbackFun)
在打开数据库以后,就可以使用事务 API transaction。每一个事务作为操作数据库的原子操作,不会被打断,从而避免了数据冲突。
以下为参数释义:
- callbackFun:回调函数
- errorCallbackFun:发生错误时的回调函数
- successCallbackFun:成功执行时的回调函数
执行 sql 语句:
executeSql( sqlString, arguments, callbackFun, errorCallbackFun)
此方法为 transaction 的操作,以下为参数释义:
- sqlString:sql 语句
- Argument:sql 参数
- callbackFun:回调函数
- errorCallbackFun:产生错误时的回调函数
另外,Web Database 也支持索引(index),这可以大大提高我们的数据库检索性能,我们可以通过“createIndex”,“openIndex”,“index.get(key)”等方法来使用这些功能。这里我们不做详细介绍,有兴趣的读者可以参考相关文档。
最后,我们来了解一下 Web Workers。我们知道,JavaScript 是单线程运行的,但是有了 Web Workers 之后,JavaScript 也可以多线程了。类似于我们常用的多线程编程,Web Workers 的使用方式相当简单:(支持浏览器 Firefox、Chrome、Safari 和 Opera)
主程序界面 <!DOCTYPE HTML> <html> <head> <title>Worker 示例 </title> </head> <body> <p> 迄今为止最大值为 : <output id="result"></output></p> <script> var worker = new Worker('worker.js'); worker.onmessage = function (event) { document.getElementById('result').textContent = event.data; }; </script> </body> </html> Worker 程序代码(worker.js) var n = 1; search: while (true) { n += 1; for (var i = 2; i <= Math.sqrt(n); i += 1) if (n % i == 0) continue search; postMessage(n); } |
所以通过新建 Worker 开启我们的另一个 JavaScript“线程”,执行相应脚本,该脚本可以通过“postMessage”与我们的“主线程”通信,主线程中通过“onmessage”监听并处理 worker 线程的“postMessage”消息。
为了加强交互性,我们一般希望手动启动 worker 线程,实现这种功能只需要稍微修改一下之前的代码:
主程序界面 <!DOCTYPE HTML> <html> <head> <title>Worker 示例 </title> </head> <body> <p> 迄今为止最大值为 : <output id="result"></output></p> <script> var worker = new Worker('worker.js'); worker.onmessage = function (event) { document.getElementById('result').textContent = event.data; }; function worker_start() { worker.postMessage("start"); } </script> <button onclick="worker_start()">Worker Start</button> </body> </html> Worker 程序代码(worker.js) self.addEventListener('message', function(e) { var n = 1; search: while (true) { n += 1; for (var i = 2; i <= Math.sqrt(n); i += 1) if (n % i == 0) continue search; postMessage(n); } }, false); |
很简单,这里我们只需要在主程序界面上让“worker”调用一下“postMessage”,然后在“worker”代码中监听“self.addEventListener('message', function(e) {......}”该消息即可。这就是当今的关于 JavaScript 的多线程解决方案。注意,这里只创建了一个额外线程,用户可以根据自己的需求创建更多的线程。
此外,还有 Shared Web Workers,顾名思义是一种可共享的 Web Workers,同一个域下面的不同的 JavaScript 脚本均可访问同一个 Shared Web Workers,其使用方式与普通 Web Workers 不同,它是通过“port”来建立连接(connection)和监听消息,同一个 Shared Web Workers 可以建立多个连接,同时与多个页面协同工作:
清单 39. Shared Web Workers 代码示例
主程序界面 <!DOCTYPE HTML> <title>Shared workers 示例 </title> <pre id="log">Log:</pre> <script> var worker = new SharedWorker('shared_work.js'); var log = document.getElementById('log'); worker.port.addEventListener('message', function(e) { log.textContent += '\n' + e.data; }, false); worker.port.start(); worker.port.postMessage('ping'); </script> <iframe src="inner.html"></iframe> 副界面(inner.html) <!DOCTYPE HTML> <title>Shared workers 示例 inner frame</title> <pre id=log>Inner log:</pre> <script> var worker = new SharedWorker('shared_work.js'); var log = document.getElementById('log'); worker.port.onmessage = function(e) { log.textContent += '\n' + e.data; } </script> Worker 程序代码(shared_work.js) var count = 0; onconnect = function(e) { count += 1; var port = e.ports[0]; port.postMessage('Hello World! You are connection #' + count); port.onmessage = function(e) { port.postMessage('pong'); } } |
细心的读者会发现,这里我们分别在两个不同页面构建了同一个“SharedWorker”(shared_work.js),所以在 Worker 的程序代码中,count 值为 2,即它记录了这两个连接。每次建立连接时,他都会发消息回去(“Hello World......”),并同时构建罅隙监听器,监听来自这两个页面的“postMessage”消息,接受到消息后,会以“pong”回应。Shared Web Workers 本身还有很多内容,这里不再深入,有兴趣的读者可以参考 W3C 官方文档。
这篇文章介绍了 HTML5 中各种新元素,新属性,新 JavaScript 接口以及一些复杂的新特性,从基本的新表单元素入手,扩展到新的页面元素,然后引入一些高级专题,由浅入深地介绍了 HTML5 中的各种对象和接口,如 WebSocket 对象、FileReader 对象以及 ApplicationCache 对象等等。最后,介绍了 HTML5 相关的 DND、Web Database 和 Web Worker 复杂新特性。这些新元素,新接口和新特性对我们的日常开发都很有帮助,建议大家平时可以多关注一下。
学习
- HTML5 信息:国内比较完善的 HTML5 门户,里面有最新的 HTML5 相关消息,各种相关教程和精彩实例。
- HTML5 W3C 标准:提供了很多 HTML5 的标准定义和使用指南,有很多经典实例。
- developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。
- developerWorks Ajax 资源中心:这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
- developerWorks Web 2.0 资源中心,这是有关 Web 2.0 相关信息的一站式中心,包括大量 Web 2.0 技术文章、教程、下载和相关技术资源。您还可以通过 Web 2.0 新手入门 栏目,迅速了解 Web 2.0 的相关概念。
- 查看 HTML5 专题,了解更多和 HTML5 相关的知识和动向。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。