HTML5-解决方案-面向-HTML5-开发者的基本技巧-四-

396 阅读1小时+

HTML5 解决方案:面向 HTML5 开发者的基本技巧(四)

原文:HTML5 Solutions Essential Techniques for HTML5 Developers

协议:CC BY-NC-SA 4.0

九、HTML5 WebSocket

WebSocket 是一种通过单一传输控制协议(TCP)套接字提供双向全双工通信通道的技术。它被设计为在 web 浏览器和 web 服务器中实现,但是它可以被任何客户端或服务器应用使用。WebSocket 应用编程接口(API)正由 W3C ( [dev.w3.org/html5/websockets/](http://dev.w3.org/html5/websockets/))标准化,WebSocket 协议正由 IETF ( [www.ietf.org/](http://www.ietf.org/))标准化。由于除 80 之外的端口的普通 TCP 连接经常被家庭环境之外的管理员阻塞,所以它可以被用作克服这些限制并提供类似功能(具有一些额外的协议开销)的方法,同时在单个 TCP 端口上多路复用几个 WebSocket 服务。

使用 WebSocket,您将能够通过浏览器在服务器和客户端之间创建双向、永久和实时的连接。在本章中,您将学习如何建立与服务器的 WebSocket 连接,以及如何将数据立即推送到客户端。通过这样做,与使用基于 XMLHttpRequest 的 AJAX 请求的方法相比,您将最终解决需要将数据推送到客户端的实时 web 应用的低延迟问题。

解决方案 9-1:检查 WebSocket 浏览器支持

WebSocket 是可能真正导致 web 应用开发革命的新奇事物之一。模拟实时客户机-服务器通信的旧方法及其相关的 HTTP 协议开销将慢慢消失。

像往常一样,因为这是一项新功能,所以在创建复杂的应用之前,你必须确保你的浏览器支持这个 API。目前,已经测试支持 WebSocket 的浏览器如下:

  • 铬 4.0
  • 火狐 4.0 测试版
  • Opera 10.7 以上
  • Safari 5.0.2
  • iOS 4.2(移动 Safari)
  • 安卓 2.3(代号姜饼)

因为你不能忽视使用其他浏览器或这些浏览器的旧版本的用户,所以你需要为 WebSocket 应用配备一个嗅探器来检查支持。

请参见下面的解决方案。

涉及到什么

就像解决方案 9-1 一样,使用typeof JavaScript 操作符来检查支持。该运算符检查其操作数的数据类型。

表 9-1 显示了由typeof操作符返回的可能值列表。

**表 9-1。**type of 运算符返回的可能数据类型

| **数据类型** | **描述** | | :-- | :-- | | 数字 | 表示一个数字 | | 线 | 表示一个字符串 | | 布尔 | 表示布尔值(通常表示为真和假,或 1 和 0) | | 目标 | 表示一个对象 | | 空 | 表示空 | | 不明确的 | 表示未定义 | | 功能 | 表示一个函数 |

使用typeof操作符非常简单:您所要做的就是将命令放在您想要检查的变量之前,它会自动返回数据类型:

var num = 1; var str = "Hello HTML5"; alert( typeof num );   // it returns number alert( typeof str );   // it returns string

要检查浏览器支持,创建一个条件以确保 WebSocket 的typeof不会返回未定义的值。

如何建造它

要检查浏览器是否支持postMessage,您只需在编写使用新 API 的函数之前,将以下 JavaScript 条件插入脚本块中:

if (window.WebSocket) {   alert("WebSockets is supported in your browser."); }

您也可以使用 JavaScript 运算符来获得相同的结果:

if ("WebSocket" in window) {   alert("WebSockets is supported in your browser."); }

该代码必须被页面脚本块包围,如下例所示:

`

       Solution 9-1: Checking for WebSocket  browser support        

          

Solution 9-1: Checking for WebSocket browser support

  

`
专家提示

这里有两个 web 服务可以用来检查你的浏览器是否支持 WebSocket:

http://jsconsole.com/?WebSocket http://websockets.org/

这些链接将报告 WebSocket 支持,如图图 9-1 所示。

images

图 9-1。 WebSocket 支持报告

解决方案 9-2:建立 WebSocket 连接

WebSocket 允许您在客户端和服务器之间建立一个开放的连接。

要创建这种类型的客户机-服务器通信,您所要做的就是使用 WebSocket 对象及其 API 的方法,这样,一旦 web 服务器上的数据发生变化,它就可以向客户机发送请求,而不必进行轮询。

涉及到什么

WebSocket 对象用于建立套接字服务器连接。对象的构造函数有两个参数,url 和协议:

  • url 指定要访问的 url。它接受协议为 ws:或 wss:的 URL。ws:是 WebSocket 连接的协议。wss:用于安全的 WebSocket 连接。
  • 协议是可选的,它包含一个字符串或一个字符串数组。

以下是如何建立 WebSocket 连接的示例:

var myWS = new WebSocket("ws://www.comtaste.com/socket");

一旦创建了 WebSocket 构造函数,就可以访问readyState属性来获得连接的状态。readyState属性的值如表 9-2 所示。

**表 9-2。**的readyState

| **值** | **数值** | **描述** | | :-- | :-- | :-- | | 连接 | Zero | 连接尚未建立。 | | 打开 | one | WebSocket 连接已建立,通信是可能的。 | | 结束的 | Two | 连接正在通过结束握手。 | | 关闭的 | three | 连接已关闭或无法打开。 |

WebSocket 只有两个方法,send()close():

  • send(data) 使用连接传输数据。
  • close() 关闭客户端和服务器之间的连接。

如果连接期间没有发生错误,它将执行 open 事件,此时您的应用准备好使用以下语法向服务器发送数据:

myWS.send("Hello WebSocket connection !");

服务器能够在任何时候使用相同的过程发回消息。

若要截获服务器收到的消息,请使用 message 事件来传输带有包含消息的数据属性的事件对象。

让我们看看如何创建一个完整的示例。

如何建造它

这个解决方案的 web 页面只有一个按钮,它将启动一个 JavaScript 方法来检查浏览器是否支持 WebSocket API。下面是按钮元素的声明:

`  

Solution 9-1: Checking for WebSocket browser support

 

Create a WebSocket Connection

`

打开一个脚本块,并将事件处理程序附加到页面的 load 事件:

<script type="text/javascript"> window.addEventListener("load", init, false);

init()事件处理程序将只负责在单击您之前创建的按钮元素时注册一个事件监听器:

function init() { document.getElementById ('socketBtn').addEventListener('click', establishSocket,true); }

这一切都发生在 establishSocket()函数中,该函数检查浏览器对 WebSocket API 的支持:

function establishSocket() {    if ("WebSocket" in window) {

如果支持创建 WebSocket 构造函数,将会创建连接。在下一个解决方案中,我们将展示如何使用事件处理程序来检查套接字连接是否已经建立:

var ws = new WebSocket("ws://localhost/echo");

以下是该解决方案的完整代码:

`

         Solution 9-2: Establishing a WebSocket connection     

        function init()         {

                document.getElementById ('socketBtn').addEventListener('click',images  establishSocket,true);

        }         function establishSocket()         {

                if ("WebSocket" in window)         {           alert("WebSocket is supported by your Browser!");

          var ws = new WebSocket("ws://localhost/echo");

        } else {

     alert("WebSocket is not supported by your browser!");   }

        }

 

 

Solution 9-1: Checking for WebSocket browser support

 

Create a WebSocket Connection

`

通过点击 socketBtn 按钮,应用将尝试创建一个与本地服务器的套接字连接,但只有在它检查了浏览器支持 WebSocket API 之后,如图 9-2 所示。

images

**图 9-2。**浏览器 WebSocket API 支持消息

专家提示

为了测试您刚刚创建的示例,您的服务器上必须有一个套接字服务器端组件。有许多可以在网上找到,我们提供了一步一步的说明,如何安装一个解决方案 9-4。

但是,与此同时,您可以使用pywebsocket,您可以从以下地址下载:

  • [code.google.com/p/pywebsocket/](http://code.google.com/p/pywebsocket/)
  • [chemicaloliver.net/internet/getting-started-web-sockets-using-pywebsocket-mod_python-and-apache-in-ubuntu/](http://chemicaloliver.net/internet/getting-started-web-sockets-using-pywebsocket-mod_python-and-apache-in-ubuntu/)
  • [www.travisglines.com/web-coding/how-to-set-up-apache-to-serve-html5-websocket-applications-with-pywebsocket](http://www.travisglines.com/web-coding/how-to-set-up-apache-to-serve-html5-websocket-applications-with-pywebsocket)

这是一个创建于Python的开源套接字服务器,旨在为 Apache HTTP 服务器 mod_pywebsocket 提供 WebSocket 扩展。

首先从pywebSocket项目页面下载mod_pywebsocket-x.x.x.tar.gz文件。

按照以下说明安装和使用套接字服务器:

解压mod_pywebsocket-x.x.x.tar.gz文件,指向/src目录(pywebsocket - x.x.x/src/)

从终端外壳运行:

sudo python setup.py build

然后键入:

sudo python setup.py install

然后,通过以下方式阅读文档:

sudo mod_pyWebSocket

这将把它安装到您的 Python 环境中。

pyWebSocket带有示例代码,您可以在/src/example 文件夹中找到。您可以通过运行此示例来测试服务器。

为了启动服务器,转到pyWebSocket-x.x.x/src/mod_pyWebSocket文件夹并运行以下命令:

sudo python standalone.py -p 8000 -w /example/

这将启动服务器监听端口 8000,并使用您的echo_wsh.py所在的-w 选项指定的处理程序目录。

解决方案 9-3:处理 WebSocket 事件

一旦创建了 WebSocket 构造函数,应用将尝试在服务器和客户端之间建立套接字连接。但是,如果希望操作顺利进行,则有必要检查连接是否已成功建立。此外,一旦建立了套接字连接,您将需要在远程服务器发送或接收消息时收到通知。

这就是为什么有必要创建事件处理程序来满足这些需求。

在这个解决方案中,您将看到 WebSocket 提供了哪些事件,以及如何在您的应用中使用它们。

涉及到什么

在前面的解决方案中,您看到了如何通过创建 WebSocket 构造函数来建立套接字服务器连接:

  var ws = new WebSocket("ws://localhost/echo");

但是在这样做的时候,您不知道客户端和服务器之间的连接是否已经成功打开,也不能对可能发生的任何错误做出反应。

这就是为什么一旦创建了构造函数,就必须为套接字连接创建事件处理程序:帮助您查看是否发生了任何错误,连接是否已正确建立,或者接收服务器发送的消息。

您可以使用的事件列表及其描述在表 9-3 中提供:

表 9-3。 WebSocket 事件

| **事件** | **描述** | | :-- | :-- | | 打开 | 建立套接字连接时会发生此事件。 | | 消息 | 当客户端从服务器接收数据时,会发生此事件。 | | 错误 | 当通信中出现任何错误时,就会发生此事件。 | | 关闭 | 连接关闭时会发生此事件。 |

让我们看看使用套接字连接的 web 应用是如何管理这些事件的。

如何建造它

从上一个解决方案的代码开始,其中包括一个按钮,该按钮启动一个 JavaScript 方法来检查浏览器是否支持 WebSocket API。如果浏览器支持此功能,将使用send()方法创建一个 WebSocket 连接:

function establishSocket() {    if ("WebSocket" in window) { var ws = new WebSocket("ws://localhost/echo");

在这里,插入新代码来管理当客户端和服务器之间的连接已正确打开并且没有发生错误时执行的第一个事件:

ws.onopen = function()      {         ws.send("Message to send");         alert("Socket connection established. The Message is being  sent...");      };

现在您已经确定连接已经成功打开,您可以使用 WebSocket 的send()方法向服务器发送消息。

然后添加三个事件处理程序:一个用于管理从服务器接收消息的消息事件,第二个用于关闭事件,第三个用于处理错误事件以提供错误机制。

`ws.onmessage = function (evt)      {         var received_msg = evt.data;         alert("Message is received...");      };      ws.onclose = function()      {

        alert("Socket connection is being closed...");      };`

如果在连接过程中出现任何类型的错误,就会执行 error 事件,该事件应该单独处理。在这个事件处理程序中,您可以查看readyState属性的值是(1) OPEN 还是(2) CLOSING,如果是,就在 WebSocket 对象上执行一个名为 error 的简单事件。

         ws.onerror = function(evt)          {                 alert ('The following error occurred: ' + error);          }

以下是网页的完整代码:

`

         Solution 9-3: Handling WebSocket events     

        function init()         {

                document.getElementById ('socketBtn').addEventListener('click',images  establishSocket,true);

        }

        function establishSocket()         {

                if ("WebSocket" in window)         {           alert("WebSocket is supported by your Browser!");

          var ws = new WebSocket("ws://localhost/echo");                    ws.onopen = function()      {

        ws.send("Message to send");         alert("Socket connection established. The Message is being  sent...");      };

     ws.onmessage = function (evt)      {         var received_msg = evt.data;         alert("Message is received...");      };      ws.onclose = function()      {

        alert("Socket connection is being closed...");      };

         ws.onerror = function(evt)          {                  // check to see if the readyState attribute's value is (1) OPEN orimages   (2)CLOSING, and if so, fire a simple event named error at the WebSocket object.                 alert ('The following error occurred: ' + error);          }

        } else {

     alert("WebSocket is not supported by your browser!");   }

        }

 

 

Solution 9-3: Handling WebSocket events   

   Create a WebSocket Connection

`

解决方案 9-4:使用带有 WebSocket API 的 WebSocket 服务器

为了能够创建套接字连接,您需要在服务器上安装一些元素。您需要的是一个套接字服务器,它可以与客户机建立连接,并等待来自客户机的消息和服务器发送的消息。

在这个解决方案中,我们将通过必要的步骤来设置一个本地服务器和一个 PHP 套接字服务器,以测试一个使用 WebSocket API 的真实套接字连接。

涉及到什么

要创建一个完整的示例,您必须在机器上安装一个 PHP 套接字服务器。因此,您需要将它安装在您的远程服务器上,或者设置一个本地服务器来安装所有必需的模块。

您可以使用一些网络工具来帮助完成这项任务。XAMPP 就是这样一个工具。

XAMPP(见图 9-3 )是一个免费的、易于安装的 Apache 发行版,包含 MySQL、PHP 和 Perl。XAMPP 非常容易使用——只需下载、解压并启动。

images

图 9-3 。XAMPP 主页

目前,有四种 XAMPP 发行版:

  • Linux 的 XAMPP
  • Windows 的 XAMPP
  • XAMPP 换麦克·OS X
  • 索拉里斯的 XAMPP

根据您选择的服务器版本,安装阶段会略有不同。无论如何,在你的机器上安装 XAMPP 还是很简单的。

对于 Mac,安装是通过打开名为XAMPP Mac OS X 1.7.3.dmg的 DMG 镜像来完成的。你所需要做的就是将 XAMPP 文件夹拖放到你的应用文件夹来安装软件及其所有模块,如图 9-4 所示。

images

图 9-4 。只需将 XAMPP·DMG 的文件拖到 Mac 上的应用文件夹中就可以安装服务器了。

你需要大约 320 MB 的磁盘空间来安装它。安装完成后,打开应用文件夹,然后打开 XAMPP 控件,启动 XAMPP,如图 9-5 所示。

images

**图 9-5。**XAMPP 控制

你必须启动你需要的服务。在这种情况下,你只需要启动 Apache(也可以启动 MySQL 和 ProFTPD),如图图 9-6 所示。

images

**图 9-6。**启动 Apache

要检查您的本地 web 服务器是否已启动并运行,请打开浏览器并键入以下 URL: [localhost](http://localhost).开始页面将显示欢迎消息:

Welcome to XAMPP for Mac OS X 1.7.3! Congratulations: You successfully installed XAMPP on this system!

Mac OS X 的 XAMPP 打开屏幕如图 9-7 所示。

images

**图 9-7。**XAMPP for Mac OS X 起始页

XAMPP 的起始页提供了检查已安装软件状态的链接和一些小的编程示例。

至于 socket 服务器,你可以安装phpwebsocket,这是一个开源项目,你可以从下面的地址下载:[code.google.com/p/phpwebwocket/](http://code.google.com/p/phpwebwocket/)。当你去项目的源[code.google.com/p/phpwebsocket/source/browse/#svn/trunk/%20phpWebSocket](http://code.google.com/p/phpwebsocket/source/browse/#svn/trunk/%20phpWebSocket)时,你需要这个项目的三个文件,你可以将它们复制到 web 服务器,如图图 9-8 所示。

  • server.php
  • websocket.class.php
  • websocket.demo.php

images

**图 9-8。**将 phpwebsocket 文件复制到 web 服务器

除了phpwebsocket,您还可以决定安装:

  • jWebSocket (Java)
  • web-socket-ruby (ruby)
  • 套接字 IO 节点(node.js)
  • NodeJS(多个 WebSocket 服务器使用的服务器端 JavaScript 框架)
  • Kaazing WebSocket 网关(基于 Java 的 WebSocket 网关)
  • mod _ pyweb socket(Apache HTTP 服务器的基于 Python 的扩展)
  • Netty(包含 WebSocket 支持的 Java 网络框架)
  • wsproxy (WebSockets 到通用 TCP 套接字代理)
  • websocket (Python)

如有必要,您可以通过打开server.php文件并更改$master变量中的值来更改本地主机的地址和要使用的端口:

$master = WebSocket("localhost",12345);

通过遵循这些简单的步骤,您将能够使用 WebSocket API 创建一个到 socket 服务器的真正连接。

如何建造它

一旦服务器已经用 PHP 建立了套接字服务器,从客户端的角度来看,与解决方案 9-3 中创建的例子相比,几乎没有什么变化。

事实上,您可以从该代码开始,做一些小的补充。特别是,插入两个新的用户界面元素:一个按钮和一个文本输入,这将允许网页的用户向服务器发送消息:

`

Solution 9-4: Using a WebSocket server with the WebSocket API   

Send message!  

`

另一方面,在脚本块中,将连接字符串更改为套接字服务器:

ws = new WebSocket("ws://localhost:8000/socket/server.php");

然后添加一个函数,处理发送用户在输入文本中插入的消息,其 id 等于 message。通过点击带有事件监听器的按钮来调用该方法,事件监听器连接到init()功能中的按钮:

`function init()         {

                establishSocket();

                document.getElementById ('messageBtn').addEventListener('click',images  sendMsg,true);

        }

function sendMsg() {

    var text = document.getElementById('message').value;     if(text=="") {         alert('Please enter a message');         return ;     }     try{         ws.send(text);

    } catch(exception){         alert('Error:' + exception);     }

}`

我们在init()函数中调用establishSocket()函数,它将进行套接字连接。

另一方面,sendMsg()方法获取文本输入中插入的文本的值,如果它是空的,它将产生一个警告。它将使用 WebSocket 的send()方法向服务器发送消息。通过在 JavaScript 语句中插入try()catch()来调用send(),这将允许我们管理可能发生的任何错误:

`   try{         ws.send(text);

    } catch(exception){         alert('Error:' + exception);     }`

通过这几行代码,我们向 open socket 服务器发送了一条消息。在结束解决方案之前,您还必须管理套接字连接的关闭。

这就是我们使用 WebSocket API 的close()方法的原因,单击一个简单的按钮就会调用该方法:

function closeConnection()         {         ws.close();      }

将最后一部分添加到您的代码中,它由调用closeConnection()方法来关闭连接的链接组成。在主体中插入一个调用 JavaScript 函数的<a>元素:

<p><a href="javascript:closeConnection()">Close socket connection</a></p>

你完蛋了。要测试代码并查看它的运行情况,记住您必须用命令行激活phpwebsocket套接字服务器。如果您使用的是 Windows 版本,则可以通过 XAMPP 控件点击“shell”按钮来访问 Shell。对于 Mac,您打开终端外壳窗口并键入:

sudo su

使用此命令,您已经以 root 用户身份登录。

如果 XAMPP 没有启动,调用以下命令:

/Applications/XAMPP/xamppfiles/xampp start

Mac OS X 终端外壳窗口如图 9-9 所示。

images

**图 9-9。**Mac OS X 的终端外壳窗口

在这一点上,对于 Windows 和 Mac 来说,这个过程没有改变。下面是启动套接字服务器的命令:

php -q yourPath\server.php

WebSocket 服务器现在已经启动。

您可以从http://localhost开始执行这个解决方案的示例代码。

以下是该解决方案的完整代码:

`

         Solution 9-4: Handling WebSocket events

    

        var ws;

        window.addEventListener("load", init, false);

        function init()         {                 establishSocket();

                document.getElementById ('messageBtn').addEventListener('click',images  sendMsg,true);

        }

        function establishSocket()         {

                if ("WebSocket" in window)         {           alert("WebSocket is supported by your Browser!");

           ws = new WebSocket("ws://localhost:8000/socket/server.php");

                   ws.onopen = function()      {

        ws.send("Message to send");         alert("Socket connection established. Connection status: " + this.readyState);      };

     ws.onmessage = function (evt)      {         var received_msg = evt.data;         alert("Message is received...");      };      ws.onclose = function()      {

        alert("Socket connection is now closed ! ");      };

         ws.onerror = function(evt)          {                  //  check to see if the readyState attribute's value is OPEN (1) orimages  CLOSING (2), and if so, fire a simple event named error at the WebSocket object.                 alert ('The following error occurred: ' + error);          }

        } else {

     alert("WebSocket is not supported by your browser!");   }

        }

        function sendMsg()         {

    var text = document.getElementById('message').value;     if(text=="") {         alert('Please enter a message');         return ;     }     try{         ws.send(text);

    } catch(exception){         alert('Error:' + exception);     }         }

        function closeConnection()         {

        ws.close();

     }

         function onkey(event)          {          if(event.keyCode==13)          { sendMsg();          }          }

 

Solution 9-4: Using a WebSocket server with the WebSocket API   

<button id=images "messaggeBtn"> Send message!

 

Close socket connection

`
专家提示

为了给这个简单的应用增加一点用户友好性,您可以确保消息不仅通过单击按钮发送到套接字服务器,而且在按下 Return 或 Enter 键时发送。

这个结果很容易获得。只需管理 input 元素的onkeypress事件,并将其与一个事件处理程序相关联,该事件处理程序只需使用包含在keycode属性中的数值(对应于值 13)来检查用户是否按下了 Return 或 Enter。

从输入文本开始,在代码中插入这些小改动:

<input type="text" value="Insert a message to send" id="message" onkeypress=![images](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6e8ccc2dbaf142e289bfa42ff282cd26~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1773057933&x-signature=hm%2BzdbC6xRTOd%2FCwYGNxt6zCba0%3D) "onkey(event)" /> <button id="messaggeBtn"> Send message ! </button>

现在在文件的脚本块中插入onkey()函数:

function onkey(event)          {          if(event.keyCode==13)          { sendMsg();          }          }

保存并加载完成的文件。

检查您的浏览器是否支持 WebSocket 的另一种快速方法是在浏览器的地址栏中键入以下 JavaScript 命令,而不是 URL:

javascript:alert("This browser does " + (window.WebSocket ? "" : "not ") + "support![images](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6e8ccc2dbaf142e289bfa42ff282cd26~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1773057933&x-signature=hm%2BzdbC6xRTOd%2FCwYGNxt6zCba0%3D)  WebSocket API");

通过执行该命令,我们将立即知道是否支持 WebSocket,如图图 9.10 所示。

images

**图 9-10。**Safari 网络浏览器支持 WebSocket API。

总结

WebSocket 是 HTML5 中引入的最重要的新 API 之一。越来越多的,网络提供的应用允许我们与世界各地的人们实时协作。

使用 WebSocket,您将能够通过浏览器在服务器和客户端之间创建双向、永久和实时的连接。一旦建立了 WebSocket 连接,服务器就可以立即将数据推送到客户端。例如,这就是为什么与基于 XMLHttpRequest 的 AJAX 请求相比,WebSocket 解决了低延迟问题。

在本章中,您已经学习了使用 WebSocket 的基础知识。你还可以做更多的事情。喜欢玩它!

十、HTML5 地理定位 API

如果一个 web 应用知道你在哪里,那不是很好吗?想象一下,能够搜索特定于您当前位置的信息—最近的餐馆在哪里,您的哪些朋友在附近,您所在城镇的交通状况如何…

这都是可能的,并且是使用 HTML5 地理定位 API 添加的相对简单的特性。位置感知应用是一个热门话题,在不久的将来,它们将结束你必须手动指定当前位置的局面。

当你想到这个特性时,一个经常出现的问题就是隐私。您可能不总是希望应用知道您的位置,并可能与公众共享它。幸运的是,该特性的实现方式是,用户需要允许在每个域和每个会话的基础上明确共享他们的位置。用户还可以选择总是允许或总是拒绝共享位置。

请记住,地理定位通常是 HTML5 项目中的一个可选功能,如果用户不允许您获取他们的位置,您将需要执行错误处理。

桌面和移动设备对地理定位 API 有相对较好的支持,正如你在表 10-1 中看到的。就像所有与技术相关的事情一样,这只会变得更好。

images

了解地理定位 API

浏览器找到你的大概位置的能力看起来就像魔术一样,但是有一些秘密的因素使得这成为可能。

首先,有用户的 IP 地址。这表明用户在哪个国家和地区通过他或她的服务提供商连接到互联网。这一切都是通过浏览器在幕后发生的,但了解这在技术上是如何工作的可能会很有趣。

每个国家、地区和城市都被分配了一定的 IP 范围。每当用户连接到互联网时,他们会得到一个与其位置相匹配的 IP 地址。有些服务在国家、地区和城市级别维护定期更新的知识产权范围数据库。

更准确地说,浏览器会收集你附近的 Wi-Fi 信号信息。这包括私有网络和公共网络。提供这项服务的公司会对这些 Wi-Fi 信号进行索引和地理定位。

发送到该服务的 Wi-Fi 信号信息包括 MAC 地址(无线路由器的唯一标识符)、信号强度、SSID(无线网络的名称)以及自上次检测以来的毫秒数。它为用户检测到的每个无线信号都这样做。

如果使用的服务已经索引了这个特定的位置,它可以给出更精确的位置,直到你所在的街区和街道。

这种 Wi-Fi 信号地理定位也发生在幕后,它完全取决于你所在区域的覆盖情况。如果你在像旧金山或纽约这样的大城市的中心,你通常会比在偏僻的地方旅行时更接近你的位置。

基于 IP 的地理定位是一种总是有效的解决方案,但是不能给出最好的结果。如果用户连接到公司 VPN 或在代理服务器上,它也可能给出错误的结果。

所有浏览器都使用这两种方法的组合来给出它们能够收集的最准确的位置。

与在台式机和笔记本电脑上使用浏览器相比,移动设备和智能手机是另一回事。移动设备和智能手机通常内置专用的 GPS,正如我们在本章后面所讨论的,可以使用这个 GPS 来请求用户的精确位置。

在下面的解决方案中,我们将展示如何使用 HTML5 地理定位 API。

解决方案 10-1:使用导航器对象

在这个解决方案中,我们看一下 navigator 对象,以及它如何参与确定用户浏览器中的地理位置 API 支持。

涉及到什么

如果您使用过 JavaScript,您可能知道 navigator 对象并不完全是新的;您可以使用它来查找浏览器语言、用户代理、安装的插件等等。新的是它现在拥有的地理位置对象,它允许您使用地理位置 API 来获取用户的位置。

如何建造它

自然,在使用地理定位 API 时,您首先要做的是检测它是否受用户浏览器的支持。幸运的是,这并不难。

`

   `

你真的不可能得到比这更基本的检测了。该代码检查导航器对象是否定义了地理位置。在本章的后面,您将看到检测特定浏览器是否支持地理位置 API 的其他方法。

如果你使用的是当前的浏览器(你可能正在阅读一本关于开发 HTML5 的书),你会看到一个警告框,显示“browser supports geolocation :),”,如图图 10-1 所示。如果你在一个不支持地理定位的浏览器中测试这个,你会看到弹出的警告框说“browser does not support geolocation :(”(见图 10-2 )。

images

**图 10-1。**显示浏览器支持地理定位 API 的警告框

images

**图 10-2。**浏览器不支持显示地理定位 API 的警告框

在不支持地理位置 API 的真实情况下,您可能会提示用户指定他们的位置,或者退回到显示非特定于位置的信息。

navigator.geolocation对象内部有以下三种方法:

  • getCurrentPosition()尝试异步获取用户的当前位置。
  • watchPosition()每隔一段时间开始监控用户的位置。
  • clearWatch()停止监控用户的位置。

我们将在解决方案 10-2 中更详细地讨论这些方法。现在,您已经通过检测浏览器是否支持地理位置 API 迈出了第一步。

解决方案 10-2:获取当前位置

在这个解决方案中,您将使用 API 调用、结果和错误处理程序来访问用户的地理位置。

涉及到什么

一旦确定用户的浏览器支持地理定位 API,就可以尝试获取当前位置。重要的是要记住,仅仅让浏览器支持地理定位是不够的;用户还必须明确允许该信息可用。

只要您请求用户的位置,就会出现一个通知,除非用户已经指定始终允许对您的站点进行地理位置检测,否则用户需要单击一个允许按钮。

因为用户需要与浏览器交互,并且确定位置可能需要几秒钟,所以获取当前位置的方法调用是异步完成的。当成功获得用户的位置时,将调用一个给定的函数,传入坐标。当获取用户位置失败时,将调用一个给定的函数,并传递错误细节。

如何建造它

在解决方案 10-1 中,您看到了如何在用户浏览器中进行基本的地理定位功能检测。现在您知道它是否受支持,您可以触发方法来获取当前位置。这真的再简单不过了:

`

   `

注意:为了跨浏览器可靠地测试地理定位功能,您可能需要将文件上传到在线资源,或者在本地服务器上测试它们。在本地查看您的页面时,安全限制并不总是允许访问地理位置 API。

在现代浏览器中运行这段代码将会提示用户选择是共享还是拒绝访问他们的位置(见图 10-3 )。

images

**图 10-3。**谷歌 Chrome 浏览器提示地理位置访问

如果用户同意分享他们的位置,浏览器将施展魔法,调用第一个函数,该函数被指定为调用getCurrentPosition的参数,在我们的例子中,该函数被称为“successGeo”。如果用户不允许他们的位置被共享,或者在试图获取他们的位置时出错,第二个函数将被调用,在我们的例子中称为“failGeo”。

现在让我们实现这些功能。

`

  

`

当您在支持地理定位的浏览器上运行此代码时,它会提示您共享您的位置或拒绝访问。当您同意分享您的位置时,您通常会看到(除非在查找您的位置时出现错误)一个警告框,显示“我们找到了宝藏”并显示位置对象实例(参见图 10-4 )。

images

**图 10-4。**获得地理位置访问权限后,显示位置对象实例的警告框

如果您拒绝访问地理定位,您将会看到一个警告框“休斯顿,我们有一个问题”和 PositionError 实例(见图 10-5 )。

images

**图 10-5。**显示拒绝地理定位访问后的 PositionError 实例的警告框

在解答 10-3 和 10-4 中,你会学到更多关于这个位置对象的知识,以及你可以用它做什么。您还将学习 PositionError 对象及其包含的信息。

专家提示

我们一直使用的getCurrentPosition方法有第三个可选参数,允许您设置一些额外的选项。可用的三个选项是:

  • enableHighAccuracy是一个布尔设置,允许您使用精确的 GPS 检测(如果可用)。
  • maximumAge指定位置检测需要发生的最近时间(毫秒)。
  • timeout指定获取用户位置的尝试需要超时的时间(毫秒)。

特别是智能手机和平板电脑现在可以使用 GPS 硬件来提供非常准确的位置信息。如果您的站点针对这些设备之一,您可以通过在第三个参数对象中指定它来尝试启用高准确性:

navigator.geolocation.getCurrentPosition(successGeo, failGeo, {enableHighAccuracy: true});

重要的是要注意,如果不可用,将enableHighAccuracy设置为 true 不会自动降低精度。这意味着它可能会触发错误函数,然后您可以将enableHighAccuracy设置为 false 来重试。

具有 GPS 硬件的移动设备将允许用户指定是否使用高精度位置检测。即使当 GPS 关闭时,这些设备中的大多数仍然能够使用其他机制来获得最精确的位置。

获取用户的地理位置可能需要几秒钟的时间(尤其是在启用高精度的情况下),因此浏览器通常会缓存结果。您可以使用maximumAge属性来确保检测到的位置是新的,并且不超过 X 毫秒:

navigator.geolocation.getCurrentPosition(successGeo, failGeo, {maximumAge: 60000});

上面的代码在第三个参数中使用了maximumAge属性,以确保在不超过 60 秒的时间内检测到用户的位置。

为了确保用户在你的网站或应用上有一个好的体验,你会希望确保他们不会在等待位置确定的时候被卡住。启用高精度后,有时可能需要一点时间(GPS 试图锁定卫星,等等)。在这种情况下,最好使用超时属性。

navigator.geolocation.getCurrentPosition(successGeo, failGeo, {enableHighAccuracy: true,![images](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6e8ccc2dbaf142e289bfa42ff282cd26~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1773057933&x-signature=hm%2BzdbC6xRTOd%2FCwYGNxt6zCba0%3D)  timeout: 5000});

上面的代码行将尝试获取高精度的位置信息,并将超时设置为 5 秒。如果网络返回位置的时间超过 5 秒,它将触发错误功能,您可以采用不同的方式处理它,要么尝试获得精度不高的位置,要么退回到用户输入。

解决方案 10-3:使用位置对象

在这个解决方案中,我们将查看当用户允许地理位置访问时返回的位置对象,以及如何使用它来获取位置信息。

涉及到什么

此时,我们已经检测到用户有地理位置支持,我们已经触发了getCurrentPosition方法,并且用户已经同意共享他或她的位置。接下来发生的是,在幕后,浏览器(或者在某些情况下,GPS 硬件)检测用户的位置,并将其作为参数返回给指定的 success 函数。

在这个解决方案中,您将看到在这个位置对象中获得的信息。

如何建造它

这里我们来看看当用户同意共享位置信息,并且浏览器成功检索到地理位置时,我们得到了什么信息。

在解决方案 10-2 中,你看到了一个非常简单的成功函数,它会被触发并显示一个警告框。现在是时候对确定的位置数据做一些更有意义的事情了。

首先,您需要知道以下哪些属性包含在这个 position 参数中:

  • timestamp返回检测到位置的时间。
  • coords.latitude返回检测位置的纬度,以度为单位。
  • coords.longitude返回检测位置的经度,以度为单位。
  • coords.accuracy返回位置的精确度,以米为单位。
  • coords.altitude返回可用的高度。
  • coords.altitudeAccuracy返回可用的高度精度,以米为单位。
  • coords.speed返回速度(基于之前检测到的位置),单位为米/秒。
  • coords.heading返回从正北顺时针方向的角度,以度为单位。

这就是事情变得有点棘手的地方。这八个属性中只有三个保证在所有实现地理定位 API 的浏览器和设备上指定:coords.latitudecoords.longitude,coords.accuracy。任何其他属性都可能不受支持并报告为 null。让我们在代码中尝试一下,看看会返回什么位置。

`

  

`

运行这段代码并允许浏览器获取你的位置将触发三个警报:一个显示纬度,一个显示经度,一个显示以米为单位的精度(见图 10-6 )。当成功功能被触发时,这三个属性在任何情况下都不应该被定义。

images

**图 10-6。**显示纬度、经度和精度值的警告框

在后面的解决方案中,您将会看到此职位信息的更高级的用途。但是现在,您已经了解了如何获取用户的经度和纬度位置,以及这些信息的精确度。

专家提示

如果您正在使用您的网站或应用,并希望快速显示纬度和经度对应的内容,您只需将此数据传递给 Google maps 的查询字符串:

function successGeo(position) { document.location = 'http://maps.google.com/maps?q='+position.coords.latitude+',' +position.coords.longitude; }

当用于getCurrentPosition调用时,上述函数会将浏览器重定向到谷歌地图,检测到的纬度和经度显示在地图上(见图 10-7 )。

images

**图 10-7。**谷歌地图显示检测到的地理位置

虽然这对于简单的测试很有用,但是您通常会希望使用 Google Maps API 并为您的站点获取一个令牌,以获得一个位置地图的干净实现。

解决方案 10-4:处理位置错误

在这个解决方案中,我们来看看使用地理定位 API 时的位置错误,你将得到什么样的错误信息,以及如何解决它。

涉及到什么

不幸的是,你不能总是指望事情进展顺利。在使用地理位置 API 时,会出现用户不允许位置共享或者出现技术故障导致检测无法工作的情况。这可能是因为当您请求高精度位置数据时 GPS 硬件未启用、浏览器使用的位置数据库有问题、网络连接缓慢或其他问题。

捕捉这样的错误并透明地处理它们很重要,这样用户就不会有不良的 Web 体验。在这个解决方案中,我们查看发生错误时获得的信息。

如何建造它

如果在获取用户位置时出现错误,我们知道这不是因为浏览器不支持地理位置 API(参见解决方案 10-1),而是因为用户不允许共享或存在技术问题。

让我们首先看看返回给错误处理函数代码的PositionError实例中有哪些信息,错误处理函数代码是一个表示发生的特定错误的数字。

消息指明错误的文本消息。

出于实际原因,您总是希望检查PositionError实例的 code 属性。不同浏览器的错误信息字符串可能不同。

可能会返回以下错误代码:

  • 0: 未知错误,获取位置时出错。
  • 1: 用户不允许共享他或她的位置。
  • 2: 找不到位置,网络不通,或者 GPS 不可用。
  • 3: 超时,因为获取用户位置花费了太长时间。

现在您已经知道了这些错误代码,您可以在代码示例中使用它们:

`

  

`

在支持地理定位的浏览器中运行上述代码将提示用户是否要共享他们的位置。在这种情况下,单击“deny”,这将触发failGeo函数并传入一个错误代码为 1 的PositionError对象的实例。你应该会看到一个警告框显示“你没有让我看到你的位置,我们不再是朋友了吗?”(参见图 10-8 。)

images

**图 10-8。**警告框,显示用户拒绝地理定位访问后的消息

在实际实现中,当用户不允许共享他们的位置时,您可以在屏幕上显示一条消息,解释为什么您的站点想要使用它,允许他们改变主意和/或退回到不依赖于特定位置数据的版本。

如果在enableHighAccuracy设置为 true 时得到错误代码 2,您可能希望将其更改为 false,并再次尝试获取位置信息。如果您收到此错误并且没有请求高精度,则可能是网络中断,您可能希望在 X 秒后再次尝试调用getCurrentPosition

如果在使用高精度时出现超时错误代码 3,您可以将其设置为 false 并重试,或者让用户指定他们的位置。

在您的站点或应用中实现地理定位时,良好的错误处理对于良好的用户体验至关重要。

解决方案 10-5:跟踪用户位置

在这个解决方案中,我们着眼于随着时间的推移监控用户的位置,以及如何在项目中实现这一点。

涉及到什么

能够找到用户的当前位置当然很好,但是如果他们在移动呢?这种情况主要适用于使用地理定位 API 的移动站点或应用。

您可以使用一种叫做watchPosition的方法,它的工作方式与我们之前讨论的getCurrentPosition方法非常相似,而不必不断地轮询变化。

在本解决方案中,我们将了解如何开始和停止监控位置变化。

如何建造它

当考虑跟踪一个四处移动的用户的位置时,您可能会预期必须每隔一段时间设置一个函数调用来定期检查currentPosition并将其与前一个进行比较。实际上比这要简单。地理定位 API 中的watchPosition方法将为您处理这些。使用这种方法,设备将计算出最佳轮询时间,并将每次的位置传递给 success 函数。

只需调整您已经拥有的代码,就可以开始监控用户的位置,而不是只获取一次:

`

  

    if(navigator.geolocation) {       var watchID = navigator.geolocation.watchPosition(successGeo, failGeo);     } else {       alert('browser does not support geolocation :(');     }   

`

你可以从上面看到,这里并没有发生很大的变化;watchPosition方法的实现与getCurrentPosition相同。第一个参数是必需的,它在成功检索到位置信息时调用函数;第二个参数在发生错误时调用,并传递有关错误的信息;第三个参数允许您指定选项,如高精度、超时和以毫秒为单位的位置最大缓存。

有一点不同的是,调用watchPosition方法返回一个数字;在本例中,我们将它存储在一个名为watchID的变量中。当你想停止监视用户的位置时,你可以调用clearWatch方法并传入那个号码:

navigator.geolocation.clearWatch(watchID);

测试这个例子有点困难。一种方法是把这个页面放到网上,加载到你的智能手机上,跳上你的自行车或汽车。在设备确定的时间间隔内,当它发现你的位置发生变化时,它会调用函数并传入数据。

当你在监控用户的位置时,该设备将参考之前的时间戳和位置,并可以确定你的大致行驶速度。显然,当使用高精度 GPS 定位或者你在某个位置数据库精度特别好的地方时,这是最好的。

运行上面的示例代码,你会看到警告框出现,告诉你移动的速度(见图 10-9 )。

images

**图 10-9。**iPhone 上的提醒框,显示行驶速度

有了这样的例子,你真的开始看到浏览器中地理定位的力量,特别是如果你考虑引入其他人的位置。能够看到你的社交网络中的哪些朋友在附近,或者跟踪你在城市中的行走路线,这些功能现在在任何网站或应用中都相对容易实现。

专家提示

在测试地理位置监控时,您可能会发现有些设备的轮询速度对于您的特定用例来说有点太快了。在这种情况下,您可以放入一个条件语句和一个何时运行代码的阈值。

使用所谓的哈弗辛公式,您可以计算两个给定纬度和经度对之间的距离:

`

  

    function toRad(value) {       return value * Math.PI / 180;     }     function distanceCoords(lat1,lon1,lat2,lon2) {       var R = 6371;       var dLat = toRad(lat2-lat1);       var dLon = toRad(lon2-lon1);       var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(toRad(lat1)) *                  Math.cos(toRad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2);       var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));       return R * c;     }

    function successGeo(position) {       var currentLat = position.coords.latitude;       var currentLon = position.coords.longitude;       if(distanceCoords(lastLat, lastLon, currentLat, currentLon) > 1) {         lastLat = currentLat;         lastLon = currentLon;         alert('The user has moved at least 1 kilometer');       }     }

    if(navigator.geolocation) {       var watchID = navigator.geolocation.watchPosition(successGeo);     } else {       alert('browser does not support geolocation :(');     }

  

`

上面的代码使用一个公式来检查最后记录的位置和当前位置之间的距离是否大于一个给定值,在这个例子中是 1 千米。如果函数被调用,而用户没有充分移动,那么条件语句中的任何代码都会被跳过。

当您想要在路径点之间具有最小距离时,在地图上绘制位置时,此代码片段特别有用。

解决方案 10-6:使用 geo.js 开源库

在本解决方案中,我们将了解开源的geo.js库,它如何在使用非标准 API 的设备上用于地理定位支持,以及您如何随着时间的推移模拟位置监控。

涉及到什么

到目前为止,我们只关注实现 W3C 地理定位 API 的现代浏览器。在它被广泛使用之前(或者制造商喜欢用他们自己的方式),还有其他的 API 在使用。

开源的 geo.js 框架(http://code.google.com/p/geo-location-javascript)是抽象所有这些 API 的简单方法;它还允许您添加对旧的黑莓、诺基亚、webOS 和其他移动设备的支持。

如何建造它

使用 geo.js 库需要做的第一件事是下载它。您可以在以下网址找到代码:http://code.google.com/p/geo-location-javascript。除了这个外部 geo.js 文件中的链接之外,您会注意到一切看起来都非常类似于标准的 HTML5 地理位置 API。

`

  

`

你发现区别了吗?这并不容易发现,因为它本质上与我们一直使用的实现完全相同,只是 geo.js 在幕后负责所有特定于平台的代码。

在你之前引用navigator.geolocation,的地方,你现在可以写geo_position_js。唯一需要开始的是调用 geo.js 的init方法。这也将让您知道地理定位对于特定的浏览器和平台是否可用。

当在支持地理定位的设备和平台上运行这段代码时,将调用successGeo函数,您将看到显示用户纬度和经度的警告框(参见图 10-10 )。

images

**图 10-10。**使用geo.js在 iPhone 上显示经度和纬度警告框

专家提示

除了支持额外的浏览器和平台,geo.js对于测试跟踪位置正在移动的用户的代码也是一个非常有用的特性。我们在解决方案 10-5 中讨论了随着时间的推移跟踪用户的位置,尽管肯定有比开车到处测试应用更简单的方法。(更不用说在你的手应该放在方向盘上的时候编码的风险了!)

geo.js中,您可以使用geo_position_js_simulator并将一组位置和持续时间传递给它的init方法。

`var locations = new Array(); locations.push({ coords:{latitude:41.399856290690956,longitude:2.1961069107055664},images duration:5000 }); locations.push({ coords:{latitude:41.400634242252046,longitude:2.1971797943115234},images duration:5000 }); locations.push({ coords:{latitude:41.40124586762545,longitude:2.197995185852051},images duration:5000 }); locations.push({ coords:{latitude:41.401921867919995,longitude:2.1977806091308594},images duration:2000 }); locations.push({ coords:{latitude:41.402533481174856,longitude:2.197566032409668},images duration:5000 });

geo_position_js_simulator.init(locations);`

你可以看到,我们没有调用geo_position_js,而是使用geo_position_js_simulator来调用init方法。数组中的每个条目都是一个对象,包含经纬度坐标以及位置的持续时间。

然后geo_position_js_ simulator将使用这个模拟位置和计时信息来回放和模拟用户移动。一旦您完成了应用的测试,您就可以切换回使用来自浏览器的常规代码和实际位置数据。这不是很棒吗?

总结

在本章中,您了解了 HTML5 地理定位 API——它在技术上是如何工作的,以及最终用户的体验。您已经体验了检测对 API 的支持、获取当前位置和处理坐标的解决方案。当然,我们还讨论了如何处理您可能遇到的错误;当用户决定不共享位置信息时以及当存在网络或其他技术问题时。

从那以后,您已经看到了如何为用户实现位置跟踪,以及如何计算最小阈值。最后,我们讨论了如何使用geo.js库为在使用非标准实现的设备上进行地理定位提供额外的支持。

十一、HTML5 本地存储

在过去的十年里,我们经历了一场网络革命:每个人都想在互联网上出现,我们现有的大多数应用都从桌面转移到了网络上。虽然这听起来很棒,但我们现在处于移动时代,我们不仅在工作场所使用台式电脑工作;我们也用笔记本电脑在火车上工作,用平板电脑在飞机上工作,或者用智能手机在任何其他地方工作。

当然,你不能依赖 24/7 不间断的连接:并非所有的航班上都有 Wi-Fi,你可能会在穿越隧道时失去 4G 连接,而且有些地方你不会有 Wi-Fi 接入,或者充其量只能得到断断续续的连接。离线阅读静态网页非常容易,但是像 Gmail 这样的网络应用,你最喜欢的 RSS 阅读器,或者任何其他对你的工作至关重要的应用呢?

历史上,所有现有的将 web 应用离线的尝试要么是特定于浏览器的,要么是依赖于第三方插件(Adobe Flash、Adobe AIR、Google Gears 等等)。这就是 HTML5 试图解决的问题。

在本章中,您将学习如何创建和提供一个清单文件来启用 HTML5 离线存储支持,并理解ApplicationCache对象和事件流。

解答 11-1:了解偶尔连接的应用

偶尔连接的应用是基于这样的想法,即用户必须能够继续使用 Internet 应用工作,即使是在暂时与网络断开连接或远程资源不可用的情况下。为此,应用必须全部或部分缓存在您的设备上。此外,如果需要,脱机时保存在本地的任何用户数据都必须在连接再次可用时进行同步。

注意:在线时使用 HTML5 缓存机制也有利于你的应用。它将加载得更快,因为大多数资源将是本地的。

为了说明上面的描述,假设一名销售人员正在使用 CRM(客户关系管理)应用:在现场拜访客户时,他希望能够通过使用平板电脑上现有的 CRM 应用的一部分来收集他们的需求,平板电脑比传统的笔记本电脑轻得多,不需要互联网连接。当他回到办公室时,他希望数据通过网络自动同步,这样他就可以在台式计算机上继续业务流程。

这是一个简单的例子,但是你现在可以很容易地想象偶尔连接的应用在不久的将来会有多重要。

涉及到什么

要使用 HTML5 中的两种离线功能,您需要执行以下操作:

  • 使用支持 HTML5 离线应用缓存和本地存储的浏览器
  • 声明一个清单文件来告诉哪些文件应该被缓存
  • 管理连接更改(最终)

让我们先来看看浏览器对应用缓存和本地存储的支持,分别显示在表 11-1 和表 11-2 中。很难确定哪个浏览器支持哪些功能,因为这些功能会定期更新。你可以从 caniuse.com这样的网站上获得这类信息,或者更好的是,你可以使用检测你当前浏览器支持的功能的网站,如 http://html5test.com/的或 http://www.modernizr.com/的来查看。

images

images

你会发现这两个功能都适用于大多数浏览器的当前版本,只有一个重要的例外——Internet Explorer(甚至是版本 9)仍然不支持应用缓存。

你会在解决方案 11-3 中看到清单文件到底是做什么的,所以剩下的唯一事情就是如何检测网络状态。在存储数据之前,您可能想知道用户是否在线。例如,这对于决定是在本地(客户端)存储值还是将值发送到服务器非常有用。为此,您可以使用 HTML5 API 的navigator.onLine属性。这是一个维护真/假值的属性(对于 online 为真;false 表示脱机),并将执行联机和脱机事件。您可以通过执行以下操作为这些事件注册侦听器:

  • 使用windowdocumentdocument.body上的addEventListener
  • documentdocument.body上的.ononline.onoffline属性设置为 JavaScript 函数对象
  • <body>标签上指定ononline="..."onoffline="..."属性

同样,行为和支持取决于浏览器:大多数支持通过浏览器的菜单切换到“脱机工作模式”,而有些不能检测物理网络状态的变化。Chrome 不支持。

我们来看一个简单的例子。

如何建造它

首先创建一个新的 HTML5 页面来测试 navigator 对象的 online 属性。

  1. Create the following HTML5 page: `

                     Understanding the Occasionally Connected Applications - Testing Network![images](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6e8ccc2dbaf142e289bfa42ff282cd26~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5biD5a6i6aOe6b6Z:q75.awebp?rk3s=f64ab15b&x-expires=1773057933&x-signature=hm%2BzdbC6xRTOd%2FCwYGNxt6zCba0%3D)  Status     
    Network Status:
    `

    当从远程 web 服务器启动此页面,并且您可以访问网络时,网络状态应该显示为“在线”然后使用浏览器的菜单切换到“脱机工作”,状态将变为“脱机”

  2. Test which element the event is sent back to. After the first <div> tag, add the following code: `

    Received by the body:

    Received by the window:
    `

    然后,在setInterval之后添加 JavaScript 部分:

    `bdy = document.getElementById("bodyStatus"); document.body.setAttribute("ononline", "bdy.innerHTML = "online""); document.body.setAttribute("onoffline", "bdy.innerHTML = "offline"");

    win = document.getElementById("windowStatus"); window.addEventListener("online", function(){win.innerHTML="online"}, false); window.addEventListener("offline",function(){win.innerHTML="offline"}, false);`

    您现在可以通过切换到离线工作模式来测试不同的浏览器,如 Internet Explorer、Firefox 或 Opera。注意他们的行为有何不同。

    images

    **图 11-1。**测试导航器对象的在线属性

注意:目前,我不建议依赖 navigator.onLine 属性,因为它在不同的浏览器中实现得很差:你不想知道用户是否通过菜单手动切换到离线模式,但你想知道他的互联网连接何时会断开。那么最好在清单中使用现有的回退机制(参见解决方案 1-3 ),或者使用 JavaScript 实现您自己的机制。

解决方案 11-2:检查 HTML5 存储支持

如前所述,许多离线存储功能已经存在。当然,最著名的是 Cookies,它在网络早期就已经存在了;然而,它们极其有限。Cookies 是存储在本地文本文件中的键值对字符串,随着每个 HTTP 请求发送到同一个域名,它们被发送到服务器。其他流行的尝试包括 Google Gears(用于 Internet Explorer 和 Firefox 的插件)、user data(IE 专用)或本地共享对象(Flash 播放器插件)。然而,所有这些都依赖于浏览器或插件。HTML5 试图通过实现自己的离线存储功能来解决这个问题。

在本解决方案中,您将了解如何检查您的浏览器是否支持这一新功能,以及如何使用它。

涉及到什么

HTML5 中有几个可用的存储 API(webSQL、File 和 Local),但我们将重点关注本地存储(也称为“Web 存储”或“DOM 存储”)。本地存储允许您在浏览器会话之间存储数据,在选项卡之间共享数据,并防止数据丢失。数据作为字符串(键值对,因此您需要序列化您的复杂值)存储在存储对象中,并且与 cookies 不同,永远不会传输到远程 web 服务器(除非您手动发送)。

要在本地存储器中存储数据,您可以使用localStorage.setItem:

localStorage.setItem("name", "Cyril Hanquez")

或者,您可以使用方括号语法:

localStorage["age"] = 38

或者,如果您愿意,也可以使用点符号:

localStorage.country = "Belgium"

同样,您可以使用getItem来检索数据:

localStorage.getItem("name")

或者,您也可以使用方括号语法:

localStorage["age"]

或者,您可以使用点符号:

localStorage.country

您可以执行的其他操作包括:

  • 使用localStorage.removeItem("age")删除数据
  • 使用localStorage.clear()清除所有存储数据
  • localStorage.length获取存储在本地存储器中的键/值对的数量
  • 使用localStorage.key(0)获取特定索引处的键的名称

注意:存储属性自动与脚本运行所在的域相关联。这意味着[google.com](http://google.com)不能访问[yahoo.com](http://yahoo.com)的存储。

长期存储localStorage还有一个变种叫做sessionStorage,它只维护当前浏览器窗口或标签中的存储。(它主要是出于安全目的。)

默认情况下,每个源获得 5 兆字节的存储空间。如果你想要更多的空间,浏览器通常会向用户请求更多的空间。然而,目前没有一个浏览器支持这一点。

现在让我们看看如何检查本地存储支持。

如何建造它

创建下面的 HTML5 页面,它将查找window.sessionStoragewindow.localStorage:

`

                 Checking for HTML5 Storage support

        

Checking for HTML5 Storage support

Session Storage: not supported
Local Storage: not supported
`

运行这个例子会告诉你你的浏览器是否支持本地和会话存储,如图 11-2 所示。

images

**图 11-2。**检查本地和会话存储

您也可以通过使用 Modernizr JavaScript 库并检查sessionstoragelocalstorage属性来获得相同的结果:

`

                 Checking for HTML5 Storage support (alternate)

        

                if (Modernizr.sessionstorage) {                         document.getElementById("session").innerHTML = "supported";                 };

                if (Modernizr.localstorage) {                         document.getElementById("local").innerHTML = "supported";                 };

        }         

Checking for HTML5 Storage support (alternate)

Session Storage: not supported
Local Storage: not supported
` ` `

您应该会得到与第一个示例完全相同的结果。

为了理解本地存储和会话存储之间的区别,让我们构建一个简单的例子。

首先创建一个新的 HTML5 页面来测试 navigator 对象的 online 属性。

  1. 创建下面的 HTML5 表单页面来编码一个键/值对`

        HTML5 localStorage/sessionStorage      

    HTML5 localStorage/sessionStorage

      

        

         

             value:      

         

          Name:      

      

     

    `
  2. 现在,在表单末尾添加一些按钮,对本地或会话存储进行基本操作`

                              

         

                        

    `
  3. 在显示存储值/对<div>      <h2>Items</h2> <table id="items_tbl"></table>      <p>      <label><input type="button" value="clear localStorage" onclick="clearStorage('local')"></label>      </p>       <p>      <label><input type="button" value="clear sessionStorage" onclick="clearStorage('session')"></label>      </p>    </div>的按钮后添加一个表格

  4. 在项目表`

         function setItem(typ) {        var name = document.forms.entryForm.name.value;        var data = document.forms.entryForm.data.value;        if (typ == "local") {               localStorage.setItem(name, data);        } else {               sessionStorage.setItem(name, data);        }

           showAll();      }

         function getItem(typ) {        var name = document.forms.entryForm.name.value;        if (typ == "local") {                document.forms.entryForm.data.value = localStorage.getItem(name);        } else {                document.forms.entryForm.data.value = sessionStorage.getItem(name);        }        showAll();      }

         function removeItem(typ) {        var name = document.forms.entryForm.name.value;        if (typ == "local") {                document.forms.entryForm.data.value = localStorage.removeItem(name);        } else {                document.forms.entryForm.data.value = sessionStorage.removeItem(name);        }        showAll();      }

         function clearStorage(typ) {        if (typ == "local") {                localStorage.clear();        } else {                sessionStorage.clear();        }        showAll(); }

         function showAll() {        var key = "";        var pairs = "TypeNameValue\n";        for (var s in window) {             switch (s) {                 case "sessionStorage":                     var j=0;                     for (j=0; j<=sessionStorage.length-1; j++) {                     key = sessionStorage.key(j);                     pairs += "session"+key+"\n"+sessionStorage.getItem(key)+"\n";                     }                 break;                 case "localStorage":                     var i=0;                     for (i=0; i<=localStorage.length-1; i++) {                     key = localStorage.key(i);                     pairs += "local"+key+"\n"+localStorage.getItem(key)+"\n";                        }

                    break;             }        }        if (pairs == "TypeNameValue\n") {            pairs += "empty\nempty\nempty\n";        }        document.getElementById('items_tbl').innerHTML = pairs;      }

       `后添加需要的 JavaScript 函数

  5. 通过调用 showAll 函数<body onload="showAll()">在页面加载时显示现有的键/值对

通过测试这个示例,您将看到当您在同一个浏览器中打开或关闭一个新的选项卡或窗口时,存储在 localeStorage 中的值/对仍然存在,但是存储在 sessionStorage 中的值/对甚至不会传递到另一个选项卡。

images

**图 11-3。**了解本地存储和会话存储的区别

注意:除非 localStorage,否则当文件在本地运行时,sessionStorage 是不存在的,除非使用 Chrome 浏览器。所以一定要使用网络服务器测试你的代码。

解决方案 11-3:为你的页面声明一个清单

清单文件是一个简单的文本文件,位于您的 web 服务器上,其中包含一个 URL 列表,这些 URL 指向您希望脱机使用的资源。

当您第一次请求包含对清单文件的引用的 HTML 文件时,您的浏览器将读取它,下载指定的资源(HTML、CSS、JavaScript、图像、视频等),并将它们存储在本地缓存中。对同一 HTML 文件的任何后续请求都将首先从缓存中加载资源(即使您在线),然后检查是否有新版本并更新应用缓存。完整的事件流将在下一个解决方案中详述。请注意,根据您的浏览器和安全级别,您可能会收到一个警告(按域或不按域),要求允许在本地存储数据。

注意:重要的是要记住,即使资产(例如图像)已经更新,只有在页面重新加载时才能看到最新版本。

涉及到什么

首先,您需要使用<html>元素上的 manifest 属性指向页面中的 manifest 文件。

`

... `

清单文件可以位于 web 服务器上的任何位置,但是它必须使用 MIME 类型 text/cache-manifest。您可能需要配置您的 web 服务器。对于 Apache,您可以在 conf 文件夹的mime.types文件中添加以下条目:

text/cache-manifest     .manifest

注意:在应用的每个页面中放置一个指向相同缓存清单的清单属性是一个最佳实践。

现在一切都已正确配置,您必须使用正确的语法编写清单文件。

如何建造它

清单中的行以 CR、LF 或 CRFL 结尾,但文本必须是 UTF-8 编码的。

  1. The first line of a cache manifest begins with CACHE MANIFEST, and then the file is divided into three parts:

    • *显式段:*使用头缓存
    • *回退部分:*使用报头回退
    • 在线白名单部分:使用头部网络

    如果缓存清单中没有定义节头,默认情况下,所有列出的资源都属于显式节。散列符号(#)用于插入注释。下面是一个基本的缓存清单:

    `CACHE MANIFEST

    this is a basic cache manifest

    index.html solution11-9781430233862.css #version 1`

    在这个例子中,两个文件index.htmlsolution11-9781430233862.css在第一次请求时存储在本地,然后任何后续请求,无论是在线还是离线,都将从缓存中获取它们。

    注意:指向清单文件的 HTML 文档隐式地包含在缓存中,但是,由于通常有多个入口点,所以将它们都列在缓存清单文件中会更方便。

  2. The FALLBACK section entries define substitutions for resources that haven't been cached for whatever reason. Fallback files are automatically included in the cache.

    让我们看看下面的例子:

    `CACHE MANIFEST

    a cache manifest with a fallback section

    index.html solution11-9781430233862.css

    FALLBACK:

    while offline, visitor number is replaced by ------

    counter.js      counter_offline.js #version 2`

    当用户在线时,从缓存中取出index.htmlsolution11-9781430233862.css;计数器(counter.js在网络上是可达的。

    当用户离线时,index.htmlsolution11-9781430233862.css也被从缓存中取出,但是计数器(counter.js)不可访问,因此它被离线版本(counter_offline.js)所替代。

  3. All requests to files or paths included in the NETWORK section must not be cached and are only accessible using the network, even if a matching cached resource is found. `CACHE MANIFEST

    a cache manifest with all 3 sections

    NETWORK: HTML5_logo.jpg

    FALLBACK:

    while offline, visitor number is replaced by ------

    counter.js      counter_offline.js

    CACHE: index.html counter.js solution11-9781430233862.css #version 3`

    在第三个例子中,我们明确地告诉浏览器不要缓存图片,所以当离线时,它将显示一个损坏的图像占位符。您还注意到,我们颠倒了各部分的顺序,因此您可以看到对全局机制完全没有影响。

注意:通过 SSL 提供的页面上的缓存限制被缓存清单覆盖,因此通过https提供的页面可以脱机工作。

解决方案 11-4:使用应用缓存对象

window.applicationCache对象给出缓存状态的信息,并触发与之相关的事件。

涉及到什么

applicationCache有一个属性 status,表示六个可能选项中缓存的状态,如表 11-3 所示。

**表 11-3。**六州

| **数值** | **状态** | | :-: | :-: | | Zero | 未缓存 | | one | 闲置的 | | Two | 检查 | | three | 下载 | | four | 更新就绪 | | five | 废弃 |

让我们来定义这些缓存状态:

| 0。 | *未缓存状态:*这是不存在缓存清单时的默认状态。 | | **1。** | *空闲状态:*这是最典型的状态,它意味着应用或网页有一个缓存清单,所有指定的资源都是最新的并存储在缓存中。 | | **2。** | *检查状态:*当浏览器正在读取清单文件时,缓存被置于此状态。 | | **3。** | *下载状态:*当从服务器下载清单中指定的新的或更新的资源并保存在缓存中时,缓存进入该状态。 | | **4。** | *UPDATEREADY 状态:*当清单文件已经被更新,并且新的或更新的资源现在在缓存中可用时,缓存处于这种状态。 | | **5。** | *过时状态:*如果曾经有一个有效的缓存,但是现在清单文件丢失了,那么缓存就进入这种状态。即使缓存中有资源,也将从网络上下载这些资源。 |

这些状态中的大多数都有关联的事件。我们将在下一个解决方案中讨论这一点。

如何建造它

对于此解决方案,我们将构建一个显示 applicationCache 状态的简单页面。

  1. 创建下面的 HTML5 页面`

         HTML5 ApplicationCache Object    

     

    `
  2. Add the following JavaScript code in the header section to check the value of the applicationCache `

    switch (myAppCache.status) {   case myAppCache.UNCACHED: // UNCACHED == 0     document.write('UNCACHED');     break;   case myAppCache.IDLE: // IDLE == 1     document.write('IDLE');     break;   case myAppCache.CHECKING: // CHECKING == 2     document.write('CHECKING');     break;   case myAppCache.DOWNLOADING: // DOWNLOADING == 3     document.write('DOWNLOADING');     break;   case myAppCache.UPDATEREADY: // UPDATEREADY == 5     document.write('UPDATEREADY');     break;   case myAppCache.OBSOLETE: // OBSOLETE == 5     document.write('OBSOLETE');     break;   default:     document.write('UKNOWN CACHE STATUS');     break; };  }  

    Call the init() function in the body of the page.

    `

    如果您运行此页面,它将显示未缓存,因为尚未指定清单。

  3. 创建一个名为 myCache.manifest 的空文件,只需键入 CACHE MANIFEST 即可创建一个简单的清单。默认情况下,指向清单文件的文档是隐式包含的,所以让我们在文件<html manifest="myCache.manifest">中添加对它的引用

现在,如果您再次运行该页面,您将看到状态现在将显示为 IDLE:由于您指定了一个存在的清单文件,它会自动缓存调用页面并更新 applicationcache 状态。

解决方案 11-5:应用缓存事件

在解决方案 11-3 中,我们简要描述了如何提供清单文件。然而,没有什么是神奇的,所以让我们详细看看不同的场景。

涉及到什么

在前面的解决方案中,我们提到大多数状态都有关联的事件。表 11-4 列出了常见事件及其相关的缓存状态。

**表 11-4。**与缓存状态相关的常见事件

| **事件** | **缓存状态** | | :-: | :-: | | `onchecking` | 检查 | | `ondownloading` | 下载 | | `onupdateready` | 更新就绪 | | `onobsolete` | 废弃 | | `oncached` | 闲置的 | | `onprogress` | - | | `onerror` | - | | `onnoupdate` | - |

我们现在将详细描述事件流。

  1. 一找到清单属性,浏览器就在applicationCache对象上执行onchecking事件。即使已经读取了缓存清单,也总是会执行此事件。
  2. 第一次读取清单时,会发生以下情况:
    • 执行一个ondownloading事件,清单文件中指定的所有资源开始下载。
    • 下载时,浏览器会定期执行一个onprogress事件,该事件包含有多少文件已经下载以及有多少文件仍在队列中的信息。
    • 如果一切都下载成功,那么浏览器执行最后的oncached事件,离线应用被完全缓存。您的应用现在处于空闲状态。
  3. 例如,如果已经从另一个页面读取了清单,那么所有需要的资源都必须在缓存中,但是您仍然需要检查自上次以来清单是否发生了更改。
    • 如果缓存清单相同,那么浏览器将执行一个onnoupdate事件,就这样。您的应用将处于空闲状态。
    • 如果缓存清单发生了变化,浏览器会执行一个ondownloading事件,所有的资源都会被重新下载。
    • 下载时,浏览器会定期执行一个onprogress事件,该事件包含关于已经下载了多少文件以及还有多少文件在队列中的信息。
    • 所有东西都下载成功后,这次浏览器执行一个onupdateready事件。这意味着缓存已经更新,但是新版本还没有使用!您需要告诉用户重新加载页面以显示最新版本。
  4. 如果流程中出现问题,浏览器就会执行一个onerror事件并停止。
  5. 您还可以使用applicationCache.update()以编程方式强制浏览器再次检查清单。然后,您可以使用applicationCache.swapCache()强制切换到新版本的缓存,但是这里仍然需要手动重新加载页面。
如何建造它

现在让我们构建一个在屏幕上显示事件流的例子。

  1. 创建下面的 HTML5 页面`

         HTML5 ApplicationCache Event    ` `/body> `
  2. 添加以下 JavaScript 代码来监听不同的 applicationCache 事件`

    appCache.addEventListener('checking',                           function(e) {                           document.write("Checking for application update
    ");                           }, false); appCache.addEventListener('cached',                           function(e) {                           document.write("Application cached
    ");                           }, false); appCache.addEventListener('noupdate',                           function(e) {                           document.write("No application update found
    ");                           }, false); appCache.addEventListener('obsolete',                           function(e) {                           document.write("Application obsolete
    ");                           }, false); appCache.addEventListener('error',                           function(e) {                           document.write("Application cache error
    ");                           }, false); appCache.addEventListener('downloading',                           function(e) {                           document.write("Downloading application update
    ");                           }, false); appCache.addEventListener('progress',                           function(e) {                           document.write("Application Cache progress
    ");                           }, false); appCache.addEventListener('updateready',                           function(e) {                           document.write("Application update ready
    ");                           }, false); `

  3. create an empty file called myCache.manifest and just type CACHE MANIFEST to create a simple manifest And add a reference to it in our file <html manifest="myCache.manifest">

    现在运行该页面并对其进行处理,例如,更改清单文件的名称以抛出错误等…

    要实现屏幕截图中的结果,您可以执行以下操作:

    • 第一次启动页面。
    • 在您的 web 服务器上,将文件 myCache.manifest 重命名为 myCache2.manifest:当您刷新页面时,它应该显示应用缓存错误,因为在 html 页面中对清单的引用没有改变。
    • 现在将 manifest 文件重命名回他原来的名字:刷新页面应该会显示截图(图 11-4 )。images

    ***图 11-4。*应用缓存事件

专家提示

您可以通过执行以下操作来监视updateready事件,从而强制刷新页面:

`// Check if the cache manifest has been updated.

if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {         // Swap the applicationCache         window.applicationCache.swapCache();         // force a reload the page         window.location.reload(); } else {        // Manifest didn't changed. }`

解决方案 11-6:删除本地缓存

在处理缓存清单时,您真的会感到头疼。您还将不得不处理浏览器和 web 服务器的缓存机制,在某些时候,您可能只想重新开始并清除缓存。

涉及到什么

目前,你不能使用applicationCache对象来做这件事。您需要直接在浏览器中清除缓存。然而,清除普通缓存是不够的——你必须找到通常隐藏在浏览器菜单和选项中的选项。

如何清除缓存

为了清空缓存,让我们首先use解决方案 11-2 的第二个例子,并在本地缓存中存储一些信息(见图 11-5 )。

images

**图 11-5。**在本地存储器中存储项目

现在,让我们尝试查找并清除最常见浏览器的应用缓存。

在 Firefox 中(参见图 11-6 ):

  • On Windows:

    工具images选项images高级images网络images离线存储

  • On OSX:

    火狐images偏好images高级images网络images离线存储

images

**图 11-6。**火狐 3 离线存储菜单

在铬合金中(参见图 11-7 ):

了解 Chrome 中不同种类的缓存的最佳方式是使用内置的开发工具。要启用它们,单击菜单中的工具images开发人员工具。然后在工具栏中选择 Resources,您将可以访问各种存储和缓存的详细视图,从 Cookies 到应用缓存。

images

**图 11-7。**谷歌 Chrome 10 资源开发工具

歌剧中(见图 11-8 ):

  • On Windows:

    菜单images设置images首选项images高级images存储

    images

    **图 11-8。**Opera 11.10 持久存储菜单

在 Safari 中(参见图 11-9 ):

和谷歌浏览器一样,你必须启用开发者工具。为此,在设置菜单中,选择首选项images高级,然后单击菜单栏中的显示开发。然后,在菜单中,点击开发images显示网页检查器,并点击存储。

images

**图 11-9。**Safari 5 存储开发工具

目前没有简单的方法来检查移动浏览器的缓存,但是大多数浏览器只有一个选项来清除缓存,即清除所有类型的缓存。

总结

这种 HTML5 本地存储功能为开发人员创造了新的机会,可以在多种平台和设备上运行丰富的应用——用户甚至可以离线工作,并在连接时同步,而不依赖于任何专有技术。鉴于不断增长的移动市场和浏览器对这一功能的更多支持,HTML5 本地存储将是最有用的新规范之一。

十二、HTML5 辅助功能

web 应用的可访问性是一个非常热门的话题。不仅使用网络的残疾人数量每天都在增加,处境困难的人浏览网络的环境也在增加。使用键盘而不是鼠标填写 web 表单,使用基于触摸的设备在屏幕上拖动对象,或者在手机上使用来自慢速 UMTS 连接([en.wikipedia.org/wiki/UMTS](http://en.wikipedia.org/wiki/UMTS))的多媒体元素,这些都是残疾人必须面对的经常性障碍。使应用具有可访问性的原因和实现这一点的技术并不涵盖影响访问 Web 的所有残疾,包括视觉、听觉、身体、语言、认知和神经残疾。相反,它涉及到为那些在访问网络时遇到某些困难的人改善访问。

可达性的四个原则

指南和成功标准是围绕以下四个原则组织的,这四个原则为任何人访问和使用 Web 内容奠定了必要的基础。任何想要使用 Web 的人都必须拥有以下内容:

  1. Perceivable: Information and user interface components must be presentable to users in ways they can perceive.

    这意味着用户必须能够感知所呈现的信息(不可能所有的感官都看不见)。

  2. Operable: User interface components and navigation must be operable.

    这意味着用户必须能够操作界面(界面不能要求用户不能执行的交互)。

  3. Understandable: Information and the operation of user interface must be understandable.

    这意味着用户必须能够理解信息以及用户界面的操作(内容或操作不能超出他们的理解)。

  4. Robust: Content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies.

    这意味着随着技术的进步,用户必须能够访问内容(随着技术和用户代理的发展,内容应该保持可访问性)。

有许多首字母缩略词涉及到这个问题:W3C、WCAG、WAI、ATAG(创作工具可访问性指南)和 UAAG(用户代理可访问性指南)。让我们试着澄清一下,因为有时事情看起来可能比实际情况更复杂。

万维网联盟(W3C)和网络无障碍倡议(WAI)于 2008 年 12 月 11 日发布了第二版网络内容无障碍指南(WCAG)。

这些指南的目的是解释如何通过定义可访问性级别来使残疾人能够访问 web 内容。在实践中,联盟希望让所有的网络用户获得相同的信息和使用相同的功能。

WCAG 的目的

网站内容可访问性指南(WCAG)是一系列网站可访问性指南的一部分,旨在帮助开发人员使内容具有可访问性,主要针对残疾用户,但也针对所有用户代理,包括高度受限的设备,如移动设备。

这组指南由 W3C 维护(图 12-1 ):

images

**图 12-1。**W3C 网站上的 WCAG 规范页面

指导方针的三个层次如下。

第 1 级:设计原则、指南和成功标准的概述

第 1 级介绍了新 WCAG 协议(第二代可访问性指南)、可访问性的四项原则、与技术无关的 13 项指南、每项指南的规定和示例定义,以及最终附录。

第 2 级:特定技术清单

2 级旨在支持一般准则。它包括一系列文档和清单,有助于定义用于符合新 WCAG 协议规范的技术信息。

第三级:特定技术的应用信息

第 3 级包括代码示例、屏幕截图和其他技术信息。这些例子包括:

通用技术
  • HTML 和 XHTML 的使用技巧
  • 级联样式表(CSS)技术
  • 服务器端脚本技术
  • 客户端脚本技术
  • 可缩放矢量图形(SVG)技术
  • 同步多媒体集成语言技术(SMIL)
  • 可扩展标记语言(XML)技术

因此,通过学习和应用 WCAG 中描述的最佳实践,您可以使 web 应用具有可访问性。

对话中还有一个问题需要补充:用于更新网页内容的软件。网络上的大多数内容都是使用开发工具(如内容管理系统,或 CMS)创建的。该软件通常决定内容的访问方式和程度,因此它在创建符合 WCAG 的内容方面发挥着重要作用。因此,对这些工具的开发者来说,遵循 WCAG 规范是一个基本要求,以使他们的软件更加通用,更适合创建可访问的内容。

创作工具可访问性指南,或 ATAG,就是带着这些需求诞生的。

另一方面,用户代理可访问性指南(UAAG)指导用户代理的开发,它打破了不同能力的个人(视觉、听觉、心理、认知和神经残疾)的可访问性障碍。用户代理包括浏览器、媒体播放器和所有执行 web 内容呈现和解析的软件。

解决方案 12-1:使用导航元素创建跳转链接

来自维基百科([en.wikipedia.org/wiki/Screen_reader](http://en.wikipedia.org/wiki/Screen_reader)):屏幕阅读器是一个软件应用,它试图识别和解释屏幕上显示的内容(或者更准确地说,发送到标准输出,无论视频监视器是否存在)。然后,这种解释通过文本到语音转换、声音图标或盲文输出设备重新呈现给用户。屏幕阅读器是一种辅助技术(AT ),通常与屏幕放大镜等其他辅助技术结合使用,可能对盲人、视障人士、文盲或有学习障碍的人有用。

残障人士(例如使用屏幕阅读器上网的人)有不同的用户需求。有些人可能希望快速跳到网页的特定部分,而其他人可能会等待屏幕阅读器按顺序阅读页面内容。

前一种用户类型可以利用屏幕阅读器提供的快捷方式(“跳过链接”)来快速确定他们可以跳过的内容;屏幕阅读器还可以根据请求提供某些元素,比如导航元素、内容、页眉、页脚等等。显然,开发人员必须设计页面,以便能够向屏幕阅读器提供这些提示。

让我们看看如何用 HTML5 创建这些函数。

涉及到什么

大多数网页可以简化为以下逻辑结构:页眉、页脚、导航菜单、主体、列等等。

在 HTML4 中,这种结构通常使用 DIV 标签创建,并与 id 属性相关联:

`

  
  
  
`

在本例中,我们创建了以下页面结构:页眉、正文、菜单、页面内容、右侧栏和页脚。内容被插入到这个结构中,然后用 CSS 语句格式化。

由于 HTML4 中缺少描述页面逻辑划分的语义,所以大量使用 DIV 块是必要的。

为了允许使用跳转链接进行快速导航,有一些技巧。跳转链接用于使使用辅助技术的人不必收听整个页面,而不仅仅是他们感兴趣的内容。这种方法在 WCAG 的 Checkpoint 13.6 中也有讨论:“组相关链接,识别组(对于用户代理),并且在用户代理这样做之前,提供绕过组的方法。”

一种方法使用可见的跳转链接,而其他方法使用隐藏的跳转链接。不管采用什么方法,跳过链接的概念都是基于

下面是一个 skip 链接的实际例子,它允许屏幕阅读器从导航菜单跳到主要内容:

`

  

  
` `   

Content

  
`

另一个跳过链接导航的例子是由许多内容丰富的门户网站实现的,它创建了一个实际的菜单来快速导航到页面的各个部分:

`

`

有了 HTML5,这种方法彻底改变了。HTML5 引入了一整套新元素,使得构建网页变得更加容易。

你已经在第三章中看到了 HTML5 的结构和语义元素。现在,您将使用这些元素在网页中创建快速导航功能,而无需创建特定的跳转链接。

目前并非所有的屏幕阅读器都能识别 HTML5 的新语义元素,例如导航标记。然而,生产这种软件的公司正与 W3C 密切合作来提供这种支持。

如何建造它

通过使用新的<nav>标签,浏览器现在有了一个标准的 HTML 元素来指定导航内容。因此,通过创建跳转链接来让用户快速跳转到页面某一部分的旧方法已经过时了。

使用新的<nav>元素,现在可以为屏幕阅读器指定页面内的导航内容,正如 HTML5 规范中的注释所解释的:

用户代理(如屏幕阅读器)的目标是那些可以从最初呈现时忽略的导航信息中受益的人,或者那些可以从导航信息立即可用中受益的人,他们可以使用该元素作为一种方式来确定页面上最初跳过和/或根据请求提供的内容。

当屏幕阅读器软件识别出<nav>元素时,它将能够为用户提供跳过导航部分的方法。

在同一个页面中可以使用多个<nav>元素。在下一个例子中,有两个<nav>元素。第一个让用户连接到外部网页,而第二个设计用于在页面内导航:

`

    

          Solution 12-1: Creating skip links with the nav element     

    

        

            

Solution 12-1: Creating skip links with the nav element

        

 

    

  

   

Stop using custom skip links!

   

And start using the semantic HTML5 tags.

  

  

      

   

    

The Nav tag

    

From the W3C NAV specs

    

    A section with navigation links.     

   

    

`

不是页面上的所有链接组都需要在一个<nav>元素中。只有由主要导航块组成的部分才适合于<nav>元素。特别是,页脚通常会有一个指向网站各种页面的链接列表,比如服务条款、主页和版权页面。对于这种情况,单独的页脚元素就足够了,不需要<nav>元素。

专家提示

说到菜单和跳转链接,HTML5 规范中有一个新元素需要考虑:<menu>

可以使用<menu>元素代替<nav>元素进行导航。情况是这样的:<menu>用于命令列表,它是一个交互元素,有几种可能专门用于 web 应用:

`

                                           

`

你可以把菜单元素想象成一个经典的桌面应用菜单系统。

解决方案 12-2:创建可访问的表格数据

表格是一种用于以表格格式表示数据的工具。然而,在过去,它们常常被用来控制页面布局。这种使用给网页的可访问性带来了很多问题,因为辅助技术可能会获得非常混乱的结果。特别是,屏幕阅读器等辅助工具的用户可能会发现很难导航带有用于布局的表格的页面。

另一方面,HTML 表格对于显示多维数据是必要的(正如 W3C 规范所提供的)。

为了使一个表具有高度的可访问性,我们必须确保它有必要的格式来优雅地转换,并且它也可以被诸如屏幕阅读器之类的设备读取。

这就是一些特定属性可以发挥作用的地方。如果明智地使用,这些属性使得任何人都可以访问表中的数据。

涉及到什么

设计可访问的数据表需要特别注意一系列属性的使用,这允许用户代理以最佳方式理解和解释表本身的内容和结构。

每个表格必须提供有关其包含的数据的信息。这些信息对于辅助技术的用户导航这些表格至关重要,因为它们将如何被读取和解释。

这就是为什么 HTML5 有一个<caption>标签,它将一个标签与表格关联起来;summary 属性不显示在标准的 web 浏览器上,但提供了用户代理可以阅读的文本,描述了表格本身的内容、结构和用途:

`

`

根据定义,每个表都可以表示一个或多个维度的数据。因此,有必要将每行的读数与相应的列相关联。这是保证用户正确阅读信息的唯一方法,例如,使用屏幕阅读器工具导航的用户。

事实上,范围属性、id 和头对允许识别头以及它们和单元之间的关系,这允许更容易地查阅数据:

<tr>     <th id="year">Year</th>     <th id="team">Team</th>     <th id="record" abbr="Record">Current Year Record</th>   </tr>   <tbody>   <tr>    <td headers="year">1915</td>    <td headers="team">Philadelphia Phillies</td>    <td headers="record">101-50</td>   </tr>   </tbody>

这种关联为单元格中的每个值提供了准确的列,这对于屏幕阅读器非常有用,因为它们可以在读取列中包含的数据之前随时读取列的标题。

现在让我们构建一个完整的示例。

如何建造它

记住屏幕阅读器是如何工作的。它并不像您想象的那样读取屏幕,而是读取 HTML 页面的底层源代码。这就是为什么使用提供表所包含数据的信息的属性和特性来使表格数据可访问是很重要的。

您可以从以下解决方案中了解如何做到这一点:

`

               Solution 12-2: Creating accessible tabular data     ` `
  List of team and their records   

This is a collection of the latest results in 2010

 
                     

  

                  

  

       

     

     

  

          

     

     

  

  List of teams and their records   

This is a collection of the latest results in 2010

 
YearTeamCurrent Year Record
1915Philadelphia Phillies101-50
1916Brooklyn Robins91-63
1915Philadelphia Phillies101-50
`

现在给这个表添加一些样式,只是为了从视觉的角度使它更令人愉快。创建一个 CSS 文件,您可以在其中为表格和单元格创建样式选择器:

`/* CSS Document */

table { border-top: 1px solid #999; border-left: 1px solid #999;

border-collapse:collapse; } th, td { padding: 5px; border-right: 1px solid #999; border-bottom: 1px solid #999; }

caption { font-family:Geneva, Arial, Helvetica, sans-serif; color:#993333; padding-bottom: 5px; }

th { background-color:#cccccc; font-family:"Courier New", Courier, mono; }`

将此文件另存为 table.css。现在要将 css 应用到页面,首先导入它,并在 head 标记中包含 style 标记:

`

               Solution 12-2: Creating accessible tabular data          `

现在表格的格式如图 12-2 所示。

images

**图 12-2。**可访问的表格数据继承样式。

专家提示

HTML5 引入了一个新的控件来表示数据:<datagrid>

<datagrid>元素是表格、树和列表的容器。它允许您选择行、列和单元格,并在客户端的浏览器中直接执行折叠行/列、对网格排序以及与数据交互等操作:

`

                  

  

       

     

     

  

          

     

     

  

1915Philadelphia Phillies101-50
1916Brooklyn Robins91-63
1915Philadelphia Phillies101-50

`

DataGridDataProvider接口表示对象必须实现的接口,以用作<datagrid>元素的定制数据视图。您可以使用DataGridDataProvider从数据库加载数据,XmlHttpRequest,或者 JavaScript 代码可以与之对话的任何东西。

解决方案 12-3:创建可访问的表单

我们已经在第四章中深入讨论了表单的重要性,你也看到了它们是如何成为让用户与网站互动的工具的。一个好的表单设计不仅在可访问性方面很重要,对可用性本身也很重要。事实上,表单中的设计和构造错误会降低应用的可理解性,甚至导致用户放弃页面,从而损害应用的良好效果。

因此,制作一个好的表单是网页设计师必须面对的一项极其困难的任务。

HTML5 为 HTML <input>元素的 type 属性引入了 13 个新值:search、tel、url、email、datetime、date、month、week、time、datetime-local、number、range 和 color。这些已经允许屏幕阅读器识别和传输更正确的数据给用户。

通过其他小的调整,比如使用键盘导航表单的可能性,可以极大地改善用户体验。

涉及到什么

一旦创建了可访问的表单,大部分工作将由辅助技术本身来完成。

第一个调整是用一个清晰的、解释性的标题,让用户立即明白他或她正在填写的是什么类型的表单。除了标题之外,对请求信息的原因提供一个简短的解释,或者简单地指定一些字段是否是必填的,几乎总是有用的。这就是为什么我们可以使用<legend>标签来表示其余内容的标题:

`

    This is a sign in form. Insert your email and password to access:

`

此外,尤其是当表单很长并且包括几个输入时,要填写的一组连贯的字段是另一个重要的选择。混淆数据是混淆用户的一个很好的方法。事实上,有一个<fieldset>标记,它表示一组表单控件,这些控件可以有选择地分组到一个公共名称下:

<form id="thisform">  <fieldset name="signin">

另一个有用的考虑是用键盘快捷键(用accesskey属性)导航表单并明确地将输入类型与标签关联起来的可能性:

<p><label for="name" accesskey="N" >Name:</label><br /> <input type="text" id="name" name="name" tabindex="2" /></p>

如何建造它

下面的示例显示了一个可访问的表单:

`

    

          Solution 12-3: Creating accessible form     

    

 

    This is a sign in form. Insert your email and password to access:

Email:

  

   

Password:

  

  

Insert your birthday:

  

  

  Remember this info?

  

`

为了使表单更加美观,可以添加 CSS 语句。为此,创建一个外部 CSS 文件,我们将其保存为form.css,并插入以下代码:

`#thisform label { font-family:Verdana, Arial, Helvetica, sans-serif; font-size:1.2em; font-weight: bold; color:#666666; }

#password, #email {

width: 200px;

} #thisform legend {                 font-weight: bold;                 font-size: 90%;

                color: #666;                 background: #eee;

                border: 1px solid #ccc;

                border-bottom-color: #999;

                border-right-color: #999; padding: 4px 10px;

                }

#thisform fieldset {

                border: 1px dotted #ff0000;                 padding: 5px 20px 10px 10px;

                }`

再次保存文件。现在打开您在此解决方案中创建的 HTML 文件。要将样式文件应用于表单,您可以导入它,并在头部声明一个<link>标记:

`           Solution 12-3: Creating accessible form     

    `

保存文件,在浏览器中执行 HTML 文件,查看最终结果,如图图 12-3 所示:

images

**图 12-3。**格式化且可访问的表单

解决方案 12-4:使用视频元素的字幕和注释

视频元素提出了无障碍领域的问题。例如,有些残疾用户听不到声音。要被所有用户认为是可访问的,视频必须伴有等效的文本替换。甚至 WCAG 也描述了视频文本替代品的重要性。

对于有听觉障碍的人来说,字幕或文本转录无疑是有用的,但对于没有快速连接的用户来说也是如此。因此,开发人员可以选择以下操作之一:

  • 在发布视频的页面的 HTML 代码中直接插入视频内容的简短摘要。
  • 在 HTML 中插入完整的语音文本,并带有一个链接,用于下载带有完整语音文本的文本文件。
  • 在视频中插入字幕。

最终的选择取决于开发人员,而且显然还取决于视频的重要性、视频本身的时长以及制作字幕的技术难度。

要制作字幕,必须使用字幕软件,该软件允许开发人员以所需的格式(QuickTime、Windows Media Player、Real Player 或 Flash)导出字幕文件,并将其发布到网络上。开发人员也可以编写程序代码,使文本与视频序列自动同步。使用 HTML5 和新的 video 元素,可以通过同步文本和视频来创建这种功能。

涉及到什么

给视频添加字幕有多种方法。您可以编写 JavaScript 代码,以编程方式将外部文本文件与视频同步,或者您可以使用一个可用的软件程序来为您完成大部分工作。

第一个重要的区别与隐藏式字幕和开放式字幕有关:

  • 开放字幕是视频序列的一部分,不能关闭。
  • 隐藏字幕是视频序列的基础,因为它们位于不同的文件中,并且可以由用户打开和关闭。

这两种方法都不是优选的,但是隐藏式字幕通过提供语言选择、打开和关闭字幕以及用于搜索字幕文本的索引系统,允许用户有更多的定制选项。

一旦决定了嵌入视频的字幕类型,你就必须选择软件。根据国家无障碍媒体中心(NCAM)指南([ncam.wgbh.org/invent_build/web_multimedia/tools-guidelines](http://ncam.wgbh.org/invent_build/web_multimedia/tools-guidelines))的建议,这些是最常见的添加字幕的软件程序:

  • 喜鹊:为 QuickTime、Windows Media、Real player 和 Flash 多媒体添加字幕和视频描述的免费软件。
  • CCforFlash:一个免费的 Flash 组件,可用于显示 Flash 视频和音频内容的标题。
  • ccPlayer 和 ccMP3Player:整合了 CCforFlash 组件的免费播放器;对于希望为 Flash 视频或音频添加标题的非 Flash 作者非常有用。
  • CaptionKeeper:将电视隐藏字幕数据转换为网络流格式的软件。
  • NCAM QA Favelet:帮助开发者识别网页易访问性问题的工具。
  • STEP:针对 Section 508 法规遵从性确定错误优先级的简单工具(STEP)。

在这个解决方案中,我们使用了 MAGpie Media Access Generator,这是一个免费的创作工具,用于在多媒体环境中创建字幕和音频描述信息。它允许您处理以下格式:QuickTime、Windows Media、Real、Flash、MP4 和 3GP 源文件。

注意:Mac 用户:MAGpie 是一个基于 Java 的应用,需要 QuickTime Java 才能正常运行。不幸的是,Apple 已经弃用了 QuickTime Java 接口,并且不再提供对它的支持。因此,喜鹊将不再在 Mac 上运行。

您可以在以下地址下载喜鹊:[ncam.wgbh.org/invent_build/web_multimedia/tools-guidelines/download-magpie](http://ncam.wgbh.org/invent_build/web_multimedia/tools-guidelines/download-magpie)

如何建造它

通过为字幕使用 MAGpie,您可以从文本文件导入字幕或手动插入语音文本。您可以将文本与视频序列同步,然后以所需的格式导出字幕文件。

执行此操作的过程非常简单。一旦你安装了喜鹊,打开它。

然后从文件images新建菜单中打开一个新项目,在初始窗口中选择要加字幕的视频。现在,系统会要求您选择想要使用的视频格式。您可以通过选择“Apple QuickTime Player”来选取 QuickTime 格式,而对于 RealPlayer 和 Windows Media Player 格式的视频,请选择“Oratrix GRiNS Player”。

现在,您必须指定您希望字幕显示的样式,然后插入视频文件的宽度和高度。

如果字幕文本是手动插入的,从 Magpie 窗口的轨道属性菜单中选择字幕,并将字幕插入该字段。

如果文本来自外部文件,使用“轨道images导入轨道”菜单导入文本。

要使文本与视频同步,请使用工具栏上的按钮,通过插入字幕的开头和结尾来开始视频。

通过这几个步骤,你已经将字幕与视频同步了。现在需要将字幕导出为可以由 HTML5 — MP4 的视频对象播放的格式。

在 MAGpie 文件images属性菜单中,您可以检查您是否选择了“Apple QuickTime Player ”,并通过从“导出”菜单中选择“QuickTime SMIL 1.0”来导出数据。当你点击 OK 按钮,喜鹊生成 2 个文件:一个 TXT 文件和一个 SMIL 扩展名的文件。

SMIL 代表同步多媒体集成语言,它支持交互式视听演示的简单创作。SMIL 通常用于将流式音频和视频与图像、文本或任何其他媒体类型集成在一起的“富媒体”(多媒体)演示。SMIL 是一种简单易学的类似 HTML 的语言,许多 SMIL 演示文稿都是使用简单的文本编辑器编写的。

要测试字幕的功能,请打开您用 QuickTime player 导出的 SMIL 文件并开始播放视频。

MAGpie 创建的文本文件包含字幕的样式数据、同步数据和字幕文本。最终结果如图图 12-4 所示。

images

**图 12-4。**喜鹊属性菜单

专家提示

说到字幕,Bruce Lawson 在 Dev 上发表了一篇有趣的文章。歌剧网站:[dev.opera.com/articles/view/accessible-html5-video-with-javascripted-captions/](http://dev.opera.com/articles/view/accessible-html5-video-with-javascripted-captions/)。这篇文章展示了一种动态添加字幕到视频对象的方法。

它使用了新的 HTML5 特性,允许任何元素拥有自定义数据属性,并将数据传递给脚本:

<span data-begin=1 data-end=6>

这是从第一秒开始到第二秒6</span>的字幕。

该解决方案需要一些 JavaScript 知识,但是值得研究,因为它在许多上下文中都很方便。

解决方案 12-5:使用 ARIA 项目

当开发一个网站或 web 应用,并试图使其具有可访问性时,你很快就会意识到 HTML 不是为编写这类应用而创建的。开发 HTML 是为了提供一种通过具有特定标识和功能的元素来导航文档的方法(因此是超文本语言),在大多数情况下,这些元素是按顺序进行的。HTML 是根据一次点击、一页 web 应用的概念创建的,这意味着在网站内导航的每一次点击都对应于对服务器的请求,然后对请求进行处理,并将响应返回给客户端。然而,对于 web 应用来说,事情最近有些变化。

首先是 Flash Player,然后是 JavaScript 和 AJAX,一次点击、一页的模式被一页的应用方法所改变。这意味着用户可以在应用中导航,而不需要更新整个页面的所有内容,例如一个表中的所有数据。事实上,应用会在后台自动更新页面内容以响应用户事件,维护页面中固定的用户界面元素。导航是在 web 应用中定义的“状态”之间进行的,而不是在页面之间。

事实上,大部分工作都委托给了客户机而不是服务器,这与过去的情况相反。

除了更加用户友好之外,这种方法还最大限度地减少了服务器请求,这些请求针对应用严格必需的数据进行了优化。web 应用的行为类似于装载在容器(即用户代理)中的桌面应用。

不幸的是,并非所有闪光的都是金子。

事实上,这种新的基于事件的系统对于辅助技术来说经常是一个问题,辅助技术期待旧的请求-响应模型。

页面的状态——即 AJAX 框架提供给用户的定制用户界面组件及其属性——对于辅助技术是不可用的。此外,由于不再有每次点击都加载一个新页面的请求-响应模型,浏览器的后退按钮通常会受到影响。

为了解决这类问题,W3C 正在进行一个名为 WAI-ARIA 的项目。基本上,ARIA 在浏览器中提供了解析系统的实现,该解析系统能够识别与网页的 HTML 元素相关联的某些属性,这些属性向辅助技术“解释”与它们相关联的角色和功能。这使得页面的内容对于那些看不到当某个事件被触发时会发生什么的人来说是清楚的。

该项目仍然是一个工作草案,还没有在最后的建议阶段。尽管如此,我们已经可以使用它的功能,所以在 web 应用中使用 ARIA 不会有负面影响。领先的浏览器和屏幕阅读器已经开始支持这个项目。

例如,Opera 9.5、Firefox 1.5 和 Internet Explorer 8 已经开始实现 ARIA 规范;而 WebKit 浏览器,如 Safari 和 Chrome,正在努力在即将到来的版本中支持 ARIA 规范。

就辅助技术而言,Jaws 7.1、Zoomtext 9、Windows Eyes 5.5 等也都支持 ARIA。

涉及到什么

ARIA 项目可以在以下情况下帮助辅助技术:

  • 与服务器的静默后台交互
  • 自定义组件(小部件)的键盘导航
  • 在应用或小部件的状态之间导航
  • 页面结构中角色的定义

ARIA 项目扩展并添加了开发人员在标记中声明的一系列属性,这些属性由浏览器和辅助技术解释。

与服务器的静默后台交互

对于屏幕阅读器来说,应用在后台加载数据和更新用户界面元素的上下文是最难解释的。ARIA 提供了一组新的属性来声明页面中的特殊区域,并在情况发生变化时警告用户代理。

这些属性是:

aria-live :这个属性指定一个元素将被更新,它描述了用户代理、辅助技术和用户可以从 live 区域期望的更新类型。它接受 off(该区域不是实时的)、礼貌(通知用户更新,但通常不中断当前任务)和 assertive(更新紧急传达给用户)值:

`<div id="liveRegion"          role="contentInfo"          aria-live="assertive" > This is a live region.

`

aria-atomic :表示辅助技术是否会向用户呈现全部或部分改变的区域。它接受 true 或 false 作为值:

`<div id="liveRegion"          role="contentInfo"          aria-live="assertive"           aria-atomic="true" > This is a live region.

`

aria-relevant :表示一个地区内有哪些变化是相关的。它接受表 12-1 中列出的值。

**表 12.1。**咏叹调——相关价值观

| **值** | **描述** | | :-- | :-- | | 添加内容: | 元素节点被添加到活动区域内的 DOM 中。 | | 移除: | 活动区域中的文本或元素节点将从 DOM 中移除。 | | 文本: | 文本被添加到活动区域的任何 DOM 子代节点中。 | | 全部: | 相当于所有值的组合;“添加删除文本”。 | | 附加文本(默认): | 相当于值的组合,“附加文本” |

aria-busy: 表示元素是否正在更新。它接受值 true 或 false

比我们想象的更多的是,用户使用键盘在应用中移动。因此,必须支持这一点,并且不要将应用链接到依赖于特定设备(如鼠标)的事件(例如,翻转事件)。

在这方面,ARIA 将其支持扩展到了tabindex属性,该属性允许您使用 Tab 键为页面上的对象分配导航顺序。以前在 HTML 中支持这个属性,但是只支持aareabuttonobject,inputselecttextarea属性。在这方面,ARIA 扩展了对tabindex属性的支持,该属性允许您为页面中的任何可见元素分配导航顺序:

`

  

Choose your favorite movie:

  <ul class="radiogroup"       id="rg1"       role="radiogroup"       aria-labelledby="radio_btn">

    <li id="r1"       tabindex="1"       role="radio">       Matrix          <li id="r2"         tabindex="2"         role="radio"> Inception          <li id="r3"         tabindex="3"         role="radio">      Avatar          <li id="r4"         tabindex="5"         role="radio">      Blade Runner          <li id="r5"         tabindex="4"         role="radio" >      The silent of the lambs           

`

在这个例子中,我们将一个tabindex关联到一个 UL 的 LI 元素,它模拟了一个单选按钮组组件。

ARIA 的tabindex属性也接受负值,允许您排除对象,但仍然通过使用键盘从导航中以编程方式接收焦点。

在应用或小工具的状态间导航

页面或组件的状态传达了关于该对象的特定信息。例如,一个页面可以有一个登录状态,该状态根据登录的用户加载某些用户界面元素。此外,单选按钮组件可以有一个在用户选择对象时启动的选定状态。

ARIA 提供了一组状态和属性,可以在各种上下文中使用,以便在应用的生命周期中为用户代理和屏幕阅读器提供特定的信息。例如,ARIA 提供了一些特定于接收用户输入和处理用户操作的通用用户界面元素的属性:

  • 空气-自动完成
  • aria-已检查(州)
  • aria-禁用(州)
  • aria-扩展(州)
  • 空气-haspopup
  • aria-隐藏(状态)
  • aria-无效(州)
  • 空气标签
  • 咏叹调级别
  • 咏叹调多线
  • 咏叹调-多选
  • 咏叹调取向
  • aria-pressed(状态)
  • aria-只读
  • aria-必需的
  • aria-选定(州)
  • 阿里索
  • 空值最大值
  • 阿莱瓦明
  • 阿里-瓦努阿图
  • aria-value text-空值文字

使用前面的例子,您可以使用aria-checked属性指出默认情况下列表中的哪个对象已经被选中。(通过添加一点 JavaScript 代码,您可以显示该对象的选定状态的图像。)

`

  

Choose your favorite movie:

  <ul class="radiogroup"       id="rg1"       role="radiogroup"       aria-labelledby="radio_btn">

    <li id="r1"       tabindex="1"       role="radio"       aria-checked="false">       Matrix          <li id="r2"         tabindex="2"         role="radio"         aria-checked="false">       Inception          <li id="r3"         tabindex="3"         role="radio"         aria-checked="false">      Avatar          <li id="r4"         tabindex="5"         role="radio"         aria-checked="true">      Blade Runner          <li id="r5"         tabindex="4"         role="radio"         aria-checked="false">      The silence of the lambs           

The following element therefore has a selected state:

  •      Blade Runner     
  • `
    在页面结构中定义角色

    HTML5 增加了新的标签来定义页面的语义结构(见第一章和第三章)。ARIA 还为页面和小部件定义了属性子集,以帮助定义它们的结构。这些属性被称为“角色”其中包括地标角色,即作为导航地标的页面区域;和小部件角色,它们或者代表独立的用户界面小部件,或者是更大的复合小部件的一部分。

    标志性角色包括:

    • 应用
    • 旗帜
    • 补充的
    • 内容信息
    • 形式
    • 主要的
    • 航行
    • 搜索

    小组件角色包括:

    • 警报
    • alertdialog(警报对话框)
    • 按钮
    • 检验盒
    • 对话
    • 网格栏
    • 原木
    • 选取框
    • menu item-功能表项目
    • 菜单项复选框
    • 菜单项收音机
    • 选择权
    • 进度条控件
    • 收音机
    • 卷动条
    • 滑块
    • 旋转按钮
    • 状态
    • 标签
    • tabpanel(仪表板)
    • 文本框
    • 计时器
    • 工具提示
    • 树项
    • 组合框
    • 格子
    • 列表框
    • 菜单
    • 菜单条
    • 选项按钮组
    • 小报记者
    • 树格栅
    如何建造它

    下面是实现 ARIA 项目某些功能的完整示例:

    `

              Solution 12-5: Using the ARIA project     

        

    Comtaste

      

    Choose your favorite movie:

      <ul class="radiogroup"       id="rg1"       role="radiogroup"       aria-labelledby="radio_btn">

        <li id="r1"       tabindex="1"       role="radio"       aria-checked="false">       Matrix          <li id="r2"         tabindex="2"         role="radio"         aria-checked="false">       Inception          <li id="r3"         tabindex="3"         role="radio"         aria-checked="false">      Avatar          <li id="r4"         tabindex="5"         role="radio"         aria-checked="true">      Blade Runner          <li id="r5"         tabindex="4"         role="radio"         aria-checked="false">      The silence of the lambs        

        

    `
    专家提示

    ARIA 项目不仅会得到用户代理的支持。事实上,Adobe 也已经开始在 Flash Player 中支持这个项目。在这篇来自 Adobe 博客([blogs.adobe.com/accessibility/2010/03/flash_player_and_flex_support.html](http://blogs.adobe.com/accessibility/2010/03/flash_player_and_flex_support.html))的可访问性部分的文章中,我们读到:

    这些升级扩展了 Flash Player 对通过 Microsoft Active Accessibility interface(MSAA)实现的辅助功能的现有支持,并将在所有三种主要操作系统(Windows、Mac 和 Linux)上实现辅助功能。Flash Player 将采用 Linux 基金会的 IAccessible2 和 W3C 的 WAI-ARIA 规范来满足用户和开发人员的需求,并简化与辅助技术供应商的互操作性。此外,还计划对免费开源的 Flex 软件开发工具包(SDK)进行增强,包括对 Flex 数据网格等复杂组件的改进,以及添加对 WAI-ARIA 的支持,以简化定制用户界面组件的开发。这些改进预计将从 Adobe Flash Player 的下一个主要版本(继 Flash Player 10.1 之后)以及 Flex SDK 的第一个连续版本开始。

    总结

    对于 web 开发来说,可访问性是一个非常重要的“社会”问题。当 web 应用或网站设计良好且开发完善时,所有用户都可以平等地访问信息和功能。

    HTML5 提供了供开发人员使用的新元素、属性和特性,以使内容更易于访问。在这一章中,你已经学会了如何避免使用跳转链接的方法,如何创建更易访问的表格数据和表单,以及如何将字幕与新的 HTML5 视频元素同步。此外,HTML5 支持正在进行的集成 WAI-ARIA 的过程,即解决方案 12-5 中讨论的可访问的富互联网应用套件。这里有一个有用的资源来了解更多关于新的 HTML5 辅助功能支持特性:[html5accessibility.com/](http://html5accessibility.com/)