首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

14.3  支持残疾用户和专用用户代理

Web开发人员必须越来越关注非图形化Web浏览器用户的需求,如只支持文本的浏览器、盲文输出设备、语音页面读取器和其他专用的用户代理。还有一些问题要考虑,对视力有缺陷的用户如何使用颜色和小文本。许多政府都要求其网站遵循某种可访问性原则:在美国,康复法案的第508条就有关这个内容。加拿大财政委员会也建立了相关标准,澳大利亚则有一个残疾差别法案。许多其他国家也要求遵循一些标准。

要研究网站的可访问性问题,可以从W3C Web Accessibility Initiative (WAI)入手,这是一个不错的起点。在http://www.w3.org/WAI/可以得到有关问题的介绍、原则和技术,以及评估可访问性的方法。

14.3.1  最大化可访问性的通用技术

要使网页和网站的可访问性最佳,主要问题是能够导航到各个页面,而且各个页面要提供所有用户都能阅读和理解的内容,另外即使访问者不使用传统的输入设备(如鼠标),也能得到支持。对于有不同类型残疾的用户,已经设计了一些特殊的用户代理和其他类型的Web访问软件,它们所依赖的网页就实现了本节将介绍的部分或所有特性,从而能更容易地访问页面。

1. 导航可访问性特性

以下建议有助于用户从一个页面导航到另一个页面,以及在页面内部导航:

q 如果不同页面的内容有一致的布局,用户一旦熟悉了这个布局,就能更容易地在页面中导航,例如菜单、内容和链接的位置。

q 在页面最前面放置“跳至”链接(本节稍后介绍),可以让相应类型用户代理的用户直接跳至他们想要的页面部分,而不必通过菜单、链接和其他非内容部分导航。

q 自引用链接(即再次加载同一个页面的链接)会导致混淆,应当尽可能避免使用。

q 每个页面上有一个有意义的<title>元素,这能让用户更快地确定这个页面和内容是否是他们需要的。

q 对于HTML <form>控件,在文本框或列表的左边放一个标题,这样可以让非可视的用户更容易地访问。同样,对于单选钮或复选框,可以把标题放在控件的右边,这也是一个广泛接受的标准。对于复杂的表单,<label>元素可以通过for属性将一个标题链接到控件。

q 超链接应当描述目标页面及其内容,而不是只包含无意义的文本(如“点击这里”)。

q 从左到右读表时,如果主行标识符值最先出现,就能更容易地使用表(有些语言的默认设计是从右到左读表)。<th>元素向用户指示这个单元格包含该行的标识值,可以增加一个scope属性,来标识此单元格包含的数据应用到行(scope="row")还是应用到列(scope="col")。8.1.2节中的例子显示了如何向一个HTML表增加标题属性。在本节的后面,你会看到另一个例子,其中显示了如何对一个GridView控件得到同样的结果。

2. 标识元素

以下建议有助于用户标识页面中的单个元素,如果用户无法使用“默认”视图,这还可以帮助他们搞清楚内容的含义。例如,可以让盲人用户访问图像的语音描述。

q 所有<frame>元素都应当包含一个name和一个title属性,所有交互式元素(如<a>、<input>、<select>和<textarea>)都应当包含一个title属性。这样属性可以为内容或目标提供扩展描述。对于ASP.NET服务器控件,可以使用控件的Tooltip属性来设置title。

q 每个图像元素中的alt属性相当于一个标题,它提供了图像的一个简短但有意义的文本描述。如果图像是一个间隔图、圆点或水平线,对页面的实际含义没有什么帮助,属性alt=""(空串)则指示可以将这个图像忽略。ASP.NET为适当的元素提供了一个GenerateEmpty- AlternateText属性,这会在相应的元素中创建一个空alt属性。

q 客户端地图图像中的每个<area>定义应当包含一个alt属性。一定要避免使用服务器端地图图像,因为大多数用户代理无法以一种有意义的方式将其表现给用户。

q 图像的longdesc属性非常适合提供另一个页面的链接,其中包含图像的详细描述或替代内容(如声音文件)。

3. 一般建议

以下建议可以为残疾用户和非标准用户代理的用户提供一般帮助:

q 对于固定的字体大小,更适合使用通用的标准化CSS字体大小,如"x-small"和"large"。这样一来,如果访问者需要,就能看到字体的扩大版本。

q 提供替代的文本内容,这对于所有非文本内容都有帮助。对于<object>和<applet>之类的元素,可以把文本放在元素标记内,而且在所包含元素(如<param>)的外面。浏览器和其他用户代理不加载或无法加载对象或applet时就会显示这个替代文本。

q 客户端脚本生成的内容对于许多基于文本的页面读取器都很可能不可用,其中大多数读取器无法执行脚本。在这种情况下,<noscript>元素是一个很有用的办法,可以用来提供替代内容。

4. 评价和测试页面和网站

Visual Studio包含一些特性,允许你检查网页的可访问性。“格式化”(Formatting)工具条包含一个按钮,利用这个按钮可以根据两个常用的建议检查页面,即网页内容易读指引(Web Content Accessibility Guidelines,WCAG)和康复法案第508条(由美国Access Board发布)。还可以在Error List窗口中显示一个列表,其中将列出需要考虑哪些问题(参见图14-5)。

图14-5  Visual Studio中检查页面的可访问性

Web内容可访问性原则(WCAG)可以由W3C网站(http://www.w3.org/TR/WAI- WEBCONTENT/得到。康复法案第508条见http://www.access-board.gov/508.htm。

本章开始时介绍HTML验证时给出了一个页面,图14-6显示了对这个页面运行可访问性检查的结果。尽管没有错误,但是这里有一些警告,指出应当向相关控件连接控件标签(如 “Enter your name”标题)。这里还有一个关于控件分组的通用建议,以便在专用用户代理中更易于使用。

图14-6  一个简单页面上运行可访问性检查的结果

不过,要看你的页面和网站对于残疾用户具体表现如何,以及在各种浏览器中如何显示,最好的办法是自行测试。可以在一个只支持文本的浏览器(如Lynx)中尝试访问你的页面。另外,可以尝试某个“页面读取器”应用(如IBM Home Page Reader),闭上眼睛或者关掉显示器后,看能不能顺利地你自己的网站中导航呢?

W3C在http://www.w3.org/WAI/References/Browsing提供了一个列表,其中列出了30余个浏览器、专用用户代理、面向残疾用户的工具和插件。http://www.w3.org/WAI/ER/existingtools.html也包含一个有用的列表,列出了测试页面的一组工具和资源。

14.3.2  ASP.NET控件中的可访问性特性

尽管ASP.NET提供的服务器控件无法为最大化可访问性提供面面俱到的解决方案(只有好的页面设计和实现才能真正做到这一点),不过这些服务器控件确实提供了一些有用的特性,如下所列:

q Caption属性和相关的CaptionAlign属性可以显示一个描述表的标题。Calendar、DetailsView、FormView、GridView、Table、DataList和DataGrid控件有这些属性。

q DescriptionUrl属性可以为非可视的页面读取器提供一个页面的URL,该页面包含图像的更多详细信息,可能采用一种文本或声音形式提供给用户,从而传达图像所包含或表示的内容。Image控件有这个属性。

q GenerateEmptyAlternateText属性指示控件要向其生成的元素中增加属性alt=""(一个空串)。如果图像对页面的含义或内容没有多大贡献,都应该有这个alt=""属性。这方面的例子包括图形化圆点、页面分隔图像,或者用于对齐或定位其他元素的“空”图像。Image、ImageButton和ImageMap控件有此属性。

q UseAccessibleHeader属性要求显示表的控件为标题单元格增加 scope属性,非可视的用户代理可以利用这个scope属性,使用户更容易理解表内容的含义。Calendar、GridView、DataList和DataGrid控件有此属性。

q AssociatedHeaderCellID属性是一个String值数组,将一个表单元格链接到一个或多个特定的表标题单元格(通过这些标题单元格的ID值)。如果一个表没有采用简单的表格布局(换句话说,使用了跨列或跨行,或者用行标题标识各行),这就允许非可视的用户代理将表中的数据与正确的标题关联。TableCell控件有此属性。

q AccessibleHeaderText属性可以指定一些文本,更详细地解释各个列标题的含义,这些文本在正常的输出中不会看到(因此不会影响表的布局)。如果控件要在一个GridView和DetailsView控件中生成列或行,就会有这个属性,这包括BoundField、AutoGeneratedField、ButtonField、CommandField、CheckBoxField、HyperlinkField、ImageField和TemplateField。

1. 使用替代文本和长描述

除了文本外,网上最常见的内容就是图像。大多数网站都包含大量的图像,有些提供了基本信息,有些只是额外的内容,有些仅用于装饰,还有一些用于控制元素之间的间隔,或者用于替代标准列表圆点和水平线。

对于视力有障碍的用户,使用文本浏览器或页面读取器时,这些图像都没用。这些用户无法看出这些图像是什么、它是否重要,以及其中包含什么。因此,一定要遵循前面给出的原则,提供替代内容和有关图像本身的信息。

这说明,至少要使用alt属性来标识和“量化”图像。如果图片带有一个描述(例如,一个新型飞船的描述),那么图像就应当有一个alt属性,指出这是“起飞时的飞船图片”。同样,其他类型的不可访问内容也应当带有一个替代文本描述。尽管用户还是看不到图像,但至少他们能知道其中包含什么。

不过,对于没有多大贡献的图像(如圆点和水平线),又怎么处理呢?在这种情况下,应当增加属性alt="",这指示用户可以忽略这个图像。问题在于,对于ASP.NET服务器控件,将一个属性设置为空串意味着这个控件生成元素时不会为元素生成相应的属性。解决办法是使用 GenerateEmptyAlternateText属性。将其设置为true,会使控件生成alt=""属性。

代码清单14-4显示了三个Image控件的简单声明。第一个将AlternateText属性(用于生成<img>元素的alt属性)设置为一个空串。第二个忽略了AlternateText属性,但是将GenerateEmptyAlternateText属性设置为True。第三个将AlternateText属性设置为一个合适的串,并增加了DescriptionUrl属性。这个属性会在元素中生成longdesc属性,在这个例子中它指向描述此图像的一个单独的页面。

代码清单14-4  为图像声明alt和Longdesc属性

<asp:Image id="img1" runat="server"

     ImageUrl="bullet.gif" ImageAlign="AbsMiddle"

     AlternateText="" />

Image with AlternateText=""

<asp:Image id="img2" runat="server"

       ImageUrl="bullet.gif" ImageAlign="AbsMiddle"

       GenerateEmptyAlternateText="True" />

Image with GenerateEmptyAlternateText="True"

<asp:Image id="Image1" runat="server"

     ImageUrl="bullet.gif" ImageAlign="AbsMiddle"

     AlternateText="Sample Bullet Image"

     DescriptionUrl="bullet.htm" />

Image with AlternateText="Sample Bullet Image"

and DescriptionUrl="bullet.htm"

图14-7显示了代码清单14-4的结果。可以看到,在一个正常的图形化浏览器中它们看上去都一样,只是各个控件后的文本有所不同。不过,如果把鼠标放在最后一个控件上,可以看到替代文本。

图14-7  使用图像的alt和longdesc属性

要了解alt和longdesc属性到底有什么区别,需要查看浏览器中生成的页面的源代码(从View菜单选择Source,就可以看到页面的源代码)。第一个图像没有alt属性(因为将AlternateText属性设置为一个空串会从元素中删除alt属性):

<img id="img1" src="bullet.gif" align="absmiddle"

     style="border-width:0px;" />

不出所料,第二个图像有空的alt属性,这是将GenerateEmptyAlternateText属性设置为True创建的:

<img id="img2" src="bullet.gif" alt="" align="absmiddle"

     style="border-width:0px;" />

第三个图像包含代码清单14-4中指定的alt属性,还有一个longdesc属性。专用页面读取器或文本浏览器可以把此属性中声明的URL("bullet.htm")提供为一个可选的链接,或者可以获取这个页面的内容,显示在图像旁边,或者以另外某种合适的方式来显示:

<img id="Image1" src="bullet.gif" longdesc="bullet.htm"

     alt="Sample Bullet Image" align="absmiddle"

     style="border-width:0px;" />

longdesc属性中引用的URL可能是一个描述该图像的声音文件,或者是一个链接,指向另一个包含有关该图像替代内容的网站,也可能是一个简单的文本文件。在这个例子中,页面bullet.htm是一个HTML页面,在正常的Web浏览器中的显示如图14-8所示。页面读取器或文本浏览器可以将其表示为图像的一个文本描述。

图14-8  longdesc属性中声明的替代描述的内容

2. 实现“D”链接和替代内容

上一节已经了解了如何为图像增加alt和longdesc属性,以及为其他元素提供替代内容,从而为用户提供额外的信息。要指示替代内容,还有另外一些可以接受的方法,其中一种方法是通过一个“D”链接或“描述”链接。这只是一个超链接,在相关内容(要为之提供额外信息的内容)旁边显示字母“d”或“D”,并导航到一个包含额外信息的页面或资源。

下一节将提供一个例子,显示一个实际使用的“D”链接。尽管这个链接通常对图形化浏览器的用户是可见的,但你可以使用某种方法把它隐藏起来,下一节讨论“跳至”链接时也会展示这种方法。不过,如果保持这个“D”链接可见,对于视力有缺陷或色盲用户来说则可以作为一个提示,这些用户可能在使用一个文本大小设置为大字体的图形化浏览器,由“D”链接可知,倘若他们无法完全理解默认内容(例如,使用了非常细或有色线条的图表),就可以通过这个链接得到更多信息。

3. 实现“跳至”链接

图形化浏览器的用户面对一个复杂的网页时,通常可以很容易地分清哪些部分包含他们想看的内容,他们可以跳过其他内容,如菜单、导航条、其他页面的链接,以及其他不相关的内容。不过,大多数专用浏览器或页面读取器都会顺序地提供内容,从页面最上面开始,依次一直到最后。这说明,每次页面加载时用户可能都必须千篇一律地经过这些不相关的内容,包括菜单、链接、否认条款、页面标题图像的描述等等。可以通过以下两种方法为这些用户提供方便:

q 实现“跳至”链接,允许用户跳过不相关的内容,直接跳到他们想访问的部分。本节将展示这种方法。

q 使用CSS的定位特性,确保在页面源代码中主要内容出现在不相关部分的前面。在支持CSS的图形化浏览器中,用户看不出任何差别。不过,采用顺序方式的页面读取器可以先显示重要内容。这里有一个小问题,除非增加了“跳至”链接跳到导航部分,否则用户可能会发现很难在网站中导航。

“跳至”链接就是页面读取器和文本浏览器可见的超链接,而对图形化浏览器的用户可能不可见(不过如果你愿意,也可以使之对图形化浏览器用户可见)。这个链接指向同一个页面中位于相应内容部分的一个锚元素。如果合适,可以在一个页面中使用多个“跳至”链接,但是为简单起见,应当把数目限制在2到3个以内。

ASP.NET中的一些控件会自动生成“跳至”链接,更确切地讲,是“跳过”链接。Menu控件就是这样一个例子,它会在其输出的最上面生成一个链接,允许用户跳过这个控件,直到跳到位于其输出最下面的一个锚。

代码清单14-5显示了两个“跳至”链接,以及位于页面相关部分前面的相应锚元素。对于这种情况,很多技术(如<font>元素和CSS样式)都会在图形化浏览器中隐藏链接。超链接中使用的图像是一个单像素透明GIF文件。使用CSS display和visible选择器要当心,因为这可能会导致一些专用浏览器无法显示链接。

代码清单14-5  声音页面读取器和文本浏览器可见的“跳至”链接

<div style="position:absolute;height:0px;"><font size="1"

color=#ffffff">

<a href="#navigation"

   style="color:#ffffff;font-size:1px;text-decoration:none">

  <img width="1" height="1" hspace="0" vspace="0"

       src="images/_blnk.gif" border="0" style="height:0"

       alt="Skip to Navigation Links" />

</a>

<a href="#content"

   style="color:#ffffff;font-size:1px;text-decoration:none">

  <img width="1" height="1" hspace="0" vspace="0"

       src="images/_blnk.gif" border="0" style="height:0"

       alt="Skip to Main Content" />

</a>

</font></div>

...

... page header here ...

...

<a name="navigation" />

...

... navigation links here ...

...

<a name="content" />

...

... main page content here ...

...

4. 可访问页面的一个例子

以下提供了一个展示“D”链接和“跳至”链接的例子,这里还提供了信息的替代视图。图14-9展示了一个网页,其中显示了一个图形化图表。这个页面包含一些“跳至”链接,在正常的浏览器中看不到这些链接,但在其他浏览器中可见,稍后就会了解到这一点。这个图表的上面有一些选项按钮,允许访问者选择查看信息的格式。默认将作为图表查看信息。在图表的下面,可以看到一个用来提供更多信息的“D”链接。

图14-9  图形化浏览器中显示图表的示例页面

这个例子使用了一个单独的类文件,它通过向DataTable增加行来生成一个DataSet,其中包含这个例子的静态信息。另一个单独的ASP.NET页面在运行时将图表动态生成为一个GIF图像(从图14-9中的alt属性可以看到文本)。第15章将介绍这个页面如何工作,而本章只关注这个例子的可访问特性。点击Table选项时,会重新加载页面,不过这一次信息将作为一个HTML表(由一个GridView控件生成)显示(参见图14-10)。同样,“D”链接出现在表的下面。

图14-10  在图形化浏览器中作为表查看同样的信息

代码清单14-6显示了这个页面HTML的主要部分。两个选项按钮分别声明了一个 AccessKey,而且其标题中与快捷键匹配的字母带有下划线。它们使用自动回送机制,所以一旦做出选择就会自动显示图表或表。不过,如果浏览器不支持客户端脚本,这就无法正常工作,所以在<noscript>元素中增加了一个提交按钮(标题为Go)。只有当浏览器不支持客户端脚本或禁用了客户端脚本时这个按钮才会出现。

代码清单14-6  显示图表或表的主页

<a name="content"/>

<span>Show data as a:&nbsp;</span>

<asp:RadioButton id="optShowChart" CssClass="body-text"

     GroupName="ShowAs" AutoPostBack="True" AccessKey="C"

     Text="<u>C</u>hart" Checked="True" runat="server" />&nbsp;

<asp:RadioButton id="optShowTable" CssClass="body-text"

     GroupName="ShowAs" AutoPostBack="True" AccessKey="T"

     Text="<u>T</u>able" runat="server" /> &nbsp;

<noscript>

  <!-- button to submit form if no client-side script -->

  <input type="submit" value="Go" />

</noscript>

<p />

<!-- placeholder to hold image of results -->

<asp:PlaceHolder id="ctlPlaceholder" runat="server" />

<!-- GridView to display results as a table -->

<asp:GridView id="grid1" EnableViewState="False" runat="server"

     UseAccessibleHeader="true" AccessKey="L"

     AutoGenerateColumns="False">

  <Columns>

    <asp:BoundField DataField="Language"

         HeaderText="Language and Country"

         AccessibleHeaderText="User langauge and country" />

    <asp:BoundField DataField="Count"

         HeaderText="Count"

         AccessibleHeaderText="Number of visitors" />

  </Columns>

</asp:GridView>

<!-- D link to more information page -->

<asp:HyperLink id="lnkD" runat="server" Text="D" AccessKey="D"

     ToolTip="Alternate Description"

     NavigateUrl="pie-describe.aspx" />

选项按钮后面是一个ASP.NET PlaceHolder控件。在第8章已经看到,这个控件在控件树中预留了一个位置,运行时可以在这里插入其他控件。PlaceHolder控件后面是显示了HTML表的GridView控件。最后,一个Hyperlink控件为页面提供“D”链接,其中包含替代内容。注意,这个代码中为GridView和Hyperlink都定义了一个AccessKey,以便没有使用或无法使用正常鼠标的用户能更容易地访问。

为了显示适当的内容,Page_Load事件处理器(如代码清单14-7所示)将检测选项按钮的设置。如果选择了Chart选项,代码会创建一个新的Image控件并设置相应的属性(指定生成图表GIF图像的一个单独的ASP.NET页面、alt属性的值,以及longdesc属性的值)。然后将这个新Image插入到PlaceHolder控件中。

代码清单14-7  可访问性示例页面的Page_Load事件处理器

protected void Page_Load(object sender, EventArgs e)

{

  // show table if client browser does not support color

  if (!Page.IsPostBack && !Request.Browser.IsColor)

  {

    optShowTable.Checked = true;

  }

  // see if we are generating a chart or a table

  if (optShowChart.Checked)

  {

    // insert new Image element for chart into page

    Image ctlImage = new Image();

    ctlImage.ImageUrl = "piechart.aspx";

    ctlImage.AlternateText = "Chart showing languages ...";

    ctlImage.DescriptionUrl = "pie-describe.aspx";

    ctlPlaceholder.Controls.Add(ctlImage);

  }

  else

  {

    // get table of the results using separate class method

    DemoLanguages dl = new DemoLanguages();

    grid1.DataSource = dl.GetDemoDataSet();

    grid1.DataBind();

    grid1.Caption = "<u>L</u>anguages for Visitors to our Site";

    grid1.AccessKey = "L";

  }

}

如果选择了Table选项,代码会使用单独类文件的GetdemoDataSet方法获取一个DataSet,将其绑定到GridView控件,从而把信息显示为一个HTML表。它还填充了表的标题,并设置了AccessKey。由于GridView将其viewstate禁用(声明中包含属性EnableViewState="False"),所以除非由此代码重置,否则不包含数据和标题。这说明,重新加载页面来显示一个图表时HTML表会消失。

代码清单14-7中的另一个特性是,页面首次加载时,会试图自动提供最合适的信息格式。Page_Load事件处理器中的代码使用当前HttpBrowserCapabilities实例(由当前Request的Browser属性得到)检查当前浏览器是否支持颜色。如果不支持,或者如果根本不支持图像,IsColor属性将为false,所以显然要显示一个表而不是图。

点击这个例子中的“D”链接,会打开替代内容页面,其中只包含前一页所示图/表中值的一个文本描述 (参见图14-11)。这个页面最下面的链接可以将用户带回到前一个页面。

图14-11  图形化浏览器中查看的替代内容页面

为这个页面创建替代内容的代码很简单,而且(正如你所料)所用的数据源与图表和表视图所用的数据源相同。代码清单14-8显示了这个页面的Page_Load事件处理器。其中包含一个相当通用的例程,它会迭代处理DataSet表中的行,并建立一个包含这些数据的String。然后将其插入到页面上的一个Label控件。最后一行代码将页面上Hyperlink的NavigateUrl属性设置为“引用页面”的URL,所以点击这个超链接时,用户会回到上一个页面。

代码清单14-8  替代内容页面中的Page_Load事件处理器

protected void Page_Load(object sender, EventArgs e)

{

  // get table of the results and display them

  DemoLanguages dl = new DemoLanguages();

  DataSet ds = dl.GetDemoDataSet();

  DataTable dt = ds.Tables[0];

  StringBuilder builder = new StringBuilder();

  foreach (DataRow row in ds.Tables[0].Rows)

  {

    builder.Append(row[0].ToString());

    builder.Append(", count = ");

    builder.Append(row[1].ToString() + "<br />");

  }

  lblOutput.Text = builder.ToString();

   // set "Back" link URL

  lnkBack.NavigateUrl = Request.UrlReferrer.ToString();

}

5. 在文本浏览器中查看可访问性页面示例

通过使用一个只支持文本的浏览器(如Lynx),可以很好地认识页面在非图形化用户代理中如何显示。这是一个简单的轻量级浏览器,只支持文本,很容易使用,而且可以在很多操作系统上运行。对于前面在图形化浏览器中看到的示例主面,图14-12显示了它在文本浏览器中的视图。

图14-12  文本浏览器中的示例页面“跳至”链接

在此,可以清楚地看到每个页面最上面(页面标题下面)的“跳至”链接(Lynx使用不同颜色来指示超链接和控件,不过这里是一个黑白切屏图,所以看不出不同的颜色,也看不到当前的输入焦点)。然后是导航部分,后面有两个选项按钮。注意,默认选择了Table选项,因为Lynx不支持图像和颜色,而且因为Lynx不支持客户端脚本,所以Go按钮可见。页面的最后是一个“D”链接,来显示信息的替代视图。

Lynx可以显示简单的HTML表,所以选择Go链接会以一种可用的格式显示信息(参见图14-13)。另外,选择“D”链接将显示替代内容页面的文本视图(参见图14-14)。

图14-13  文本浏览器中数据的表视图

图14-14  文本浏览器中的替代内容页面

Lynx浏览器的主页是http://lynx.browser.org/,可以根据你的操作系统从http://lynx.isc.org/ current/下载一个适用的版本。

6. 使HTML表的可访问性最佳

浏览器显示一个HTML表时,要想理解表中包含的信息,往往要求用户扫描各行和各列,在心里将它们与各行的标题描述相关联。通常表就像正在设计的一个电子表格,每个单元格值与它所在的列和行的描述关联。

对于大多数用户,为此只需查看标题和最左边的一列,找到所需的描述,然后向下扫描,找到对应行列标题描述的单元格。不过,对于非可视浏览器和用户代理的用户来说,这一点很难做到。他们的浏览器通常会逐行地迭代处理表,与行中各个单元格相关的标题描述很容易“脱离视线”。

为了帮助这些用户,HTML 4.0对表单元格包含了headers属性,应当将其设置为一个列表,包括此单元格相关标题和行描述的标题单元格ID值。这样一来,浏览器在迭代处理各行中的单元格时可以抽取出标题和行描述,以一种合适的方式提供给用户。

第8章(使用Table控件的例子)中,我们已经看到了这个解决方案,可以使用表中各个单元格的AssociatedHeaderCellID属性,将单元格与相关的标题单元格链接起来。代码清单14-9显示了为浏览器生成的HTML表。可以看到列和行<th>元素和每个<td>元素上的headers属性,它将单元格与适当的行和列标题相关联。

代码清单14-9  包含Headers属性的HTML表

<table id="MyTable" rules="all" border="1">

  <tr>

    <th id="Header0">Header0</th>

    <th id="Header1">Header1</th>

    <th id="Header2">Header2</th>

  </tr>

  <tr>

    <th id="RowDesc0">RowDescription0</th>

    <td headers="Header1,RowDesc0">Row0 Col1</td>

    <td headers="Header2,RowDesc0">Row0 Col2</td>

  </tr>

  <tr>

    <th id="RowDesc1">RowDescription1</th>

    <td headers="Header1,RowDesc1">Row1 Col1</td>

    <td headers="Header2,RowDesc1">Row1 Col2</td>

  </tr>

  ... more rows here ...

  <tr>

    <td>Foot0</td>

    <td>Foot1</td>

    <td>Foot2</td>

      </tr>

</table>

7. 使用快捷键、跳格顺序和表标题

上一节中见到的AssociatedHeaderCellID属性只应用于TableCell控件。如果想为GridView等控件生成的单元格增加headers属性,必须通过处理相应的事件来完成,即ASP.NET生成所显示的表时发生的事件。下面的例子将展示这一点,还展示了如何使用Label控件和快捷键,以及如何设置页面中的跳格顺序。

图14-15显示了这个例子生成的页面。可以看到,3个文本框和表格的标题都有带下划线的字母,这对应于快捷键,按下快捷键时会把输入焦点移到相应控件。还有一点在这里看不出来,Show按钮也有一个快捷键(Alt+S)。

图14-15  声明快捷键和GridView可访问性

无法对标准Button控件中的文本加下划线,因为HTML元素(<u> 和</u> 标记)在按钮上只显示为文本。不过,如果使用HTML风格的<button>元素,可以要求IE显示一个带下划线的按钮标题:

<button id="mybutton" runat="server"><u>S</u>how</button>

这个控件存在一个问题,它不产生回送,所以必须将其绑定到一个调用表单submit方法的客户端函数。

如果访问这个页面会看到,可以使用快捷键把输入焦点移到任何文本框或表格。还可以在控件间按跳格键(tab键)移动输入焦点,跳格顺序如下:首先是Product Name文本框,再是表格,然后回到Name和Email文本框,这种顺序与默认的跳格顺序有所不同。代码清单14-10显示了Label、TextBox和Button控件的声明,其中都设置了相关的AccessKey和TabIndex属性。

注意Panel控件中可选控件的分组。指定GroupingText属性会导致这个控件生成一个HTML <fieldset>和嵌套的<legend>元素,这是一种推荐做法,可以为专用用户代理简化复杂表单的布局,以便更好地理解内容。

代码清单14-10  Label、TextBox和Button控件的声明

<asp:Label id="lblProduct" runat="server"

           Text="<u>P</u>roduct Name:"

           AccessKey="P" AssociatedControlID="txtProduct"

           TabIndex="0" />

<asp:TextBox id="txtProduct" runat="server" Text="C"

             TabIndex="1" />

<asp:Button runat="server" id="btnGo" Text="Show"

            ToolTip="Start search for matching products"

            <TabIndex="2" AccessKey="S" /><p />

<asp:Panel GroupingText="Optional details" runat="server">

  <asp:Label id="lblName" Text="<u>N</u>ame:" runat="server"

             AccessKey="N" AssociatedControlID="txtName" />

  <asp:TextBox id="txtName" runat="server" TabIndex="4" />

  <br />

  <asp:Label id="lblEmail" Text="<u>E</u>mail:" runat="server"

             AccessKey="E" AssociatedControlID="txtEmail" />

  <asp:TextBox id="txtEmail" runat="server" TabIndex="5" />

</asp:Panel>

代码清单14-11显示了代码清单14-10生成的HTML。在此可以看到accesskey和tabindex属性,以及提交按钮的title(还可以设置TextBox控件的Tooltip属性,为文本框增加title属性)。

代码清单14-11  Label、TextBox和Button控件生成的HTML

<label for="txtProduct" id="lblProduct"

       accesskey="P"><u>P</u>roduct Name:</label>

<input name="txtProduct" type="text" value="C" id="txtProduct"

       tabindex="1" />

<input type="submit" name="btnGo" value="Show" id="btnGo"

       accesskey="S" tabindex="2"

       title="Start search for matching products" />

<fieldset>

  <legend>

    Optional details

  </legend>

  <label for="txtName" id="lblName"

         accesskey="N"><u>N</u>ame:</label>

  <input name="txtName" type="text" id="txtName" tabindex="4" />

  <label for="txtEmail" id="lblEmail"

         accesskey="E"><u>E</u>mail:</label>

  <input name="txtEmail" type="text" id="txtEmail" tabindex="5" />

</fieldset>

此例的GridView控件声明(参见代码清单14-12)还包含一些额外的属性设置来提高可访问性。Caption属性会在表格上面生成标题(这个例子中使用的Caption属性值为"<u>G </u>ridView Control"),这将导致在GridView的AccessKey属性指定的热键(“G”)加下划线(从图14-15可以看到其结果)。还可以看到这里设置了TabIndex属性,使表格能在它上面的两个文本框之前得到输入焦点。在此各列的声明并不重要,所以代码清单中未显示列声明。

代码清单14-12  GridView控件的声明

<asp:GridView id="MyGrid" runat="server"

     Caption="<u>G</u>ridView Control" CaptionAlign="Top"

     AccessKey="G" TabIndex="3"

     RowHeaderColumn="ProductName"

     UseAccessibleHeader="True"

     DataKeyNames="ProductID" DataSourceID="ds1"

     BorderWidth="1px" BorderColor="#E7E7FF" BorderStyle="None"

     BackColor="White" CellPadding="3" PagerSettings-Mode="Numeric"

     AutoGenerateColumns="False"

     OnRowDataBound="AddHeadersAttr">

     . . .

     . . .

</asp:GridView>

8. 向GridView控件增加可访问的标题

注意,在代码清单14-12中,RowHeaderColumn属性设置为源行集中一个列的列名,这个列中包含各行中最有用而且最有描述性的值,即产品描述,而且UseAccessibleHeader属性设置为True。这些属性设置可以为生成的表格增加可访问性。

RowHeaderColumn属性使ASP.NET为各行中此列的单元格生成一个<th>元素,而不是一个<td>元素,使之在图形化浏览器中以粗体文本出现。更重要的是,这意味着页面读取器或非图形化用户代理可以区分出哪个列包含的信息最有用,最能用来向用户描述各行。UseAccessibleHeader属性使ASP.NET向各列和行标题增加scope属性,指定该单元格中的标题或值应用于它所在的行还是列。

不过,这些属性设置不会在各个单元格中提供headers属性,这与第8章中的Table控件示例不同,但是可以使用代码自行增加。可以看到,在代码清单14-12中,OnRowDataBound属性指定了一个名为AddHeadersAttr的事件处理器,ASP.NET生成HTML表中的各行时就会运行这个事件处理器。

代码清单14-13完整地显示了这个事件处理器。这段代码首先检查ASP.NET在创建哪种类型的行(标题行还是数据行)。对于一个标题行,它会向各个单元格增加一个id属性,其中包含文本"ColumnHeader_"和该列的列名。对于构成表体的数据行,代码会迭代处理该行中的单元格,查看各个单元格是一个行标题(<th>元素在控件树中表示为一个DataControlField- HeaderCell控件),还是一个正常的数据单元格(<td>元素在控件树中表示为一个DataControlFieldCell控件)。

代码清单14-13  为GridView控件增加headers属性的代码

protected void AddHeadersAttr(Object sender, GridViewRowEventArgs e)

{

  if (e.Row.RowType == DataControlRowType.Header)

  {

    // this is the column header row so add ID to each

    // column using column name. NOTE: cannot set ID property

    // because this includes the ID of all parent controls as well

    // for example "MyGrid_ctl1_3" instead of just "3"

    for (Int32 i = 0; i < e.Row.Cells.Count; i++)

    {

      e.Row.Cells[i].Attributes.Add("id",

            "ColumnHeader_" + MyGrid.Columns[i].HeaderText);

    }

  }

  else if (e.Row.RowType == DataControlRowType.DataRow)

  {

    // this is a data row

    for (Int32 i = 0; i < e.Row.Cells.Count; i++)

    {

      Object oCell = e.Row.Cells[i];

      if (oCell is DataControlFieldHeaderCell)

      {

        // this is the row header so add an ID using the ProductID

        DataControlFieldHeaderCell oHeaderCell

            = oCell as DataControlFieldHeaderCell;

        oHeaderCell.Attributes.Add("id", "RowHeader_"

            + MyGrid.DataKeys[e.Row.RowIndex].Value.ToString());

      }

      else

      {

        // this is a data cell so add appropriate headers attribute

        DataControlFieldCell oFieldCell

            = oCell as DataControlFieldCell;

        oFieldCell.Attributes.Add("headers", "ColumnHeader_"

            + MyGrid.Columns[i].HeaderText + ",RowHeader_"

            + MyGrid.DataKeys[e.Row.RowIndex].Value.ToString());

      }

    }

  }

}

对于行标题单元格,代码将向该单元格增加一个id属性,其中包含文本"RowHeader_" 和该行主键的值(取自GridView的 DataKeys集合,GridView的DataKeyNames属性设置为ProductID列,如代码清单14-12所示)。对于数据单元格,代码会在该单元格上生成一个headers属性,指定当前列和行标题单元格的id值。

代码清单14-14显示了此例所生成HTML的一部分。可以看到<table>元素上的accesskey和tabindex属性,还可以看到嵌套<caption>元素中有带下划线的字母。表的第一行中是列标题单元格,每个列标题单元格的id都设置为AddHeadersAttr事件处理器中增加的值,另外还有scope="col"属性,这是通过设置RowHeaderColumn和UseAccessibleHeader属性增加的。

代码清单14-14  可访问GridView控件生成的HTML

<table accesskey="G" tabindex="3" cellspacing="0" ... >

  <caption align="Top"><u>G</u>ridView Control</caption>

  <tr style="color:#F7F7F7;...">

    <th id="ColumnHeader_ProductID" scope="col">ProductID</th>

    <th id="ColumnHeader_ProductName" scope="col">ProductName</th>

    <th id="ColumnHeader_QuantityPerUnit"

        scope="col">QuantityPerUnit</th>

    <th id="ColumnHeader_UnitPrice" scope="col">UnitPrice</th>

    <th id="ColumnHeader_UnitsInStock" scope="col">UnitsInStock</th>

  </tr>

  <tr align="left" style="color:#4A3C8C;...">

    <td headers="ColumnHeader_ProductID,RowHeader_60">60</td>

    <th id="RowHeader_60" scope="row">Camembert Pierrot</th>

    <td headers="ColumnHeader_QuantityPerUnit,RowHeader_60">15</td>

    <td headers="ColumnHeader_UnitPrice,RowHeader_60">34.0000</td>

    <td headers="ColumnHeader_UnitsInStock,RowHeader_60">19</td>

  </tr>

  ...

  ...

</table>

在下一行中,第二个单元格是行标题(产品名),同样,它也有一个id属性。另外4个单元格都是数据单元格,其中包含headers属性,设置为相应的列和行标题id值。由此可以看到,合适的页面读取器或文本用户代理可以通过读出或显示列和行标题单元格的内容,在用户浏览表时提供单元格描述。

如果列标题的文本(HeaderText属性)不足以描述一个列的内容,或者如果出于审美原因在一个特定的列标题中忽略了文本,还可以为列内容提供更有描述性的解释。各个列对象(如BoundField、HyperlinkField和ButtonField)的 AccessibleHeaderText属性为列标题行中的相应<th>元素设置abbr属性,这对专用用户代理可用,而在图形化浏览器中不可见。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论