Stephen Walther
SuperExpert.com (英語)
日本語版最終更新日 2006 年 7 月 26 日
適用対象 :
Microsoft ASP.NET 2.0
Microsoft Visual Studio 2005
Microsoft Visual Web Developer
概要 : Microsoft ASP.NET 2.0 には、XHTML 標準やアクセシビリティ標準に準拠する Web サイトの設計と構築を支援する機能が数多くあります。この記事では、そうした標準に準拠したサイトを構築する方法と、そうしたサイトを構築する理由について見ていきます
目次
はじめに
XHTML Web サイトの構築
XHTML 標準のバージョン
XHTML ページの作成
XHTML と ASP.NET コントロール
XHTML ページの検証
XHTML と DOCTYPE スイッチ
XHTML と MIME の種類
XHTML 準拠の構成
アクセシビリティ標準
ASP.NET 2.0 でのアクセシビリティの向上
アクセスしやすいイメージの作成
アクセスしやすいフォームの作成
アクセスしやすいナビゲーションの作成
アクセスしやすいデータの作成
アクセスしやすい XHTML の作成
アクセスしやすいスクリプトの作成
ページのアクセシビリティの検証
Amazon Web サービスへのアクセス
既定のページ
既定のページの XHTML 機能
既定のページのアクセシビリティ機能
検索ページ
検索ページの XHTML 機能
検索ページのアクセシビリティ機能
マスタ ページ
マスタ ページの XHTML 機能
マスタ ページのアクセシビリティ機能
はじめに
Web 標準を使用すると、最小限の作業で、可能な限り幅広いユーザーがアクセスすることができる Web サイトを構築できます。Web 標準によって約束されるのは、ページを一度設計すれば、そのページが今日のあらゆるブラウザでまったく同じ外観と機能を備えるようになるということです。たとえば、標準に照らして Web サイトを構築した場合、Microsoft Internet Explorer で一定の形式で表示されるように設計したページは、追加の作業を一切行わずに、Mozilla、Firefox、Netscape Navigator、Opera、Camino、Safari などの他のブラウザで同じように表示されます。
Web 標準のもう 1 つの利点は、障害を持つ人がよりアクセスしやすい Web サイトを作成できるということです。これには、中高年の視力が低下している人、最近スキーで腕を骨折した人、全盲の人など、幅広いユーザーが含まれます。標準を使用すると、一時的または永続的な障害を持つ人を、Web ページから図らずも遠ざけてしまうのを防ぐことができます。
Microsoft ASP.NET 2.0 フレームワークは、Web 標準を満たす Web サイトを構築するための最も優れたフレームワークとなるように設計されました。具体的には、ASP.NET 2.0 フレームワークのすべてのコントロールは、XHTML 標準とアクセシビリティ標準の両方に照らして徹底的に見直され、テストされました。さらに、Microsoft Visual Studio 2005 には、Web ページを XHTML 標準とアクセシビリティ標準の両方に照らして検証するための新しいツールが用意されています。
この記事の目的は、XHTML 標準とアクセシビリティ標準の概要を示し、ASP.NET 2.0 と Visual Studio 2005 を利用してそれらの標準を満たす方法を説明することです。この記事の最後の部分では、XHTML 標準とアクセシビリティ標準の両方を満たす ASP.NET 2.0 Web サイトを作成するための手順を示しています。
XHTML Web サイトの構築
HTML は、正式には時代遅れです。World Wide Web Consortium (W3C) は、2000 年 1 月 26 日に XHTML の最初のバージョンを推奨として公開しました。XHTML 標準は、HTML に代わることを目的としたものです。W3C によると、「XHTML は HTML の後継である」とされています (http://www.w3.org/MarkUp/) (英語) 。
XHTML 標準の立案者たちは、2 つの大きな目標を持っています。
- ドキュメントの構造とプレゼンテーションを明確に分離する。
- HTML を XML に適応するように再構築する。
最初の目標を達成するために、W3C はプレゼンテーションのみを目的とした要素および属性を HTML から着実に取り除いてきました (この作業は HTML 4.0 で始まりました)。たとえば、XHTML 1.0 Strict には、<font> タグなどの要素や、bgcolor 属性などの属性は含まれません。これらの要素や属性はドキュメントの外観を記述する目的でのみ使用され、ドキュメントの構造とは何の関係もないからです。
W3C は、Web サイトの設計者や開発者に、特定のタグには特定の外観を持たせるべきだという考え方を捨てさせようとしています。たとえば、<h1> タグ (見出しタグ) の目的は、大きくて太いテキストをページに表示することだと思っている人がいるかもしれませんが、それは間違いです。<h1> タグは、ドキュメント内の見出しを示すために使用するものであり、それ以外のものではありません。この見出しタグをどのように表示するかを決定するのは、ブラウザしだいです。視力が低下した人が使用するスクリーン リーダーが、見出しタグのコンテンツを、大きくて堂々とした声で読み上げることもあるでしょう。複数のフォント サイズをサポートしていない PDA が、見出しタグのコンテンツを、点滅するテキストで表示することもあるかもしれません。
<h1> タグなどのページ要素を使用して Web ページの外観を制御しないようにしてください。代わりに、カスケード スタイル シートを使用して Web ページの外観を指定する必要があります。カスケード スタイル シートは、できれば外部カスケード スタイル シートにすることをお勧めします。ドキュメントの構造を示すにはタグと属性を使用し、ドキュメントのプレゼンテーションを制御するにはスタイル シートを使用するようにします。
XHTML の 2 つ目の目標は、XML のより厳密なルールを HTML 開発者に守らせることです。W3C によると、「XHTML 1.0 は HTML 4.01 を XML 1.0 に適応するよう再構築したものである」とされています (http://www.w3.org/MarkUp/) (英語) 。つまり、XHTML を使用して Web ページを構築するということは、実際には XML ドキュメントを作成することになります。
XML ドキュメントの構文は、HTML ドキュメントよりもはるかに厳密です。たとえば、XML では大文字と小文字が区別されます。また、すべての XML 属性は引用符で囲む必要があります。さらに、XML タグはオーバーラップできません。Web サイトの開発者と設計者に、より要求の厳しい言語のルールを守らせることには、多くの利点があります。
利点の 1 つは、XHTML マークアップを使用して記述されたページは、ブラウザ間、デバイス間、およびオペレーティング システム間での互換性が高まるということです。従来の HTML ページをブラウザで開いた場合、ブラウザはページを表示するためにあらゆる手を尽くします。ブラウザは、HTML の記述が完全に誤っている場合でも、ページを表示しようとします。たとえば、Internet Explorer (および Firefox と Opera) は、次の HTML ページを正しく表示します。
<i><B>this is bold and italic</I> and this is bold
</body></HTML>
このページでは、<html> タグと <body> タグの開始タグがなく、<b> タグに対応する終了タグがなく、<i> タグの開始タグと終了タグで大文字と小文字が統一されていません。それでも、Internet Explorer はこのページを正しく表示します。すべての主要なブラウザは、HTML タグの "タグのごった煮" をほぼすべて受け入れ、なんらかのものを表示しようと試みます。
こうしたブラウザの寛大な動作は危険です。別のブラウザ (または同じブラウザの将来のバージョンや、別のオペレーティング システムで実行されている同じブラウザ) は、誤って記述された HTML を異なる方法で表示する可能性があるためです。実際には、Internet Explorer、Mozilla Firefox、および Opera の最新バージョンでは、無効な HTML の表示方法が驚くほど似ています。ただし、いったんルールから外れると、同じように表示されるという保証はありません。
しかし、XHTML のより厳密なルールを使用して Web ページを記述すると、Web ページが今日ある複数のブラウザで同様に動作し、またそれらのブラウザの将来登場する新しいバージョンでも引き続き動作する可能性は高くなります。自社の Web サイトを、あらゆるオペレーティング システムとあらゆるデバイスで実行される、あらゆるブラウザでテストするだけのリソースを保持している企業は、ほとんどありません。Web 標準に照らしてページを記述すれば、そうしたテストを行う必要はありません。
XHTML 標準のバージョン
XHTML 1.0 には 3 つのバージョンがあります。これらは HTML 4.01 の 3 つのバージョンに対応しています。
- XHTML 1.0 Transitional
- XHTML 1.0 Strict
- XHTML 1.0 Frameset
XHTML 1.0 Transitional には、HTML 4.01 Transitional のすべてのタグと属性が含まれています。XHTML 1.0 Transitional 標準は、既存の HTML 設計者と HTML 開発者が、多くの衝撃と苦痛を被ることなく XHTML に移行できるようにするために導入されました。
XHTML 1.0 Strict は、ドキュメントの構造とプレゼンテーションを明確に分離している点で、XHTML 1.0 Transitional と異なっています。XHTML 1.0 Transitional と異なり、XHTML 1.0 Strict では、カスケード スタイル シートを使用してページの外観を制御する必要があります。
XHTML 1.0 Frameset ドキュメントは、<frameset> タグを使用してブラウザを複数のフレームに分割するドキュメントとなることを意図したものです (XHTML 1.0 Transitional ページと XHTML 1.0 Strict ページに <frameset> タグを含めることはできません)。
また W3C は、2001 年 5 月 31 日に XHTML 1.1 を推奨として公開しています。XHTML 1.1 は XHTML 1.0 Strict に非常によく似ています。主な違いは、XHTML 1.1 は追加のモジュールによって拡張し、新しい要素をサポートすることができるということです。たとえば、MathML (Mathmatical Markup Language)、SVG (Scalable Vector Language)、または独自に作成したカスタム モジュールの要素も含む XHTML 1.1 ページを構築することができます。
さらに、W3C は XHTML 2.0 の推奨事項 (英語) の作成を進めています。XHTML 2.0 はまだドラフト段階であり、この標準をサポートする Web ブラウザは現時点で存在しないため、ここでは取り上げません。
ASP.NET 2.0 フレームワークと Visual Studio 2005 は、XHTML 1.0 Transitional を対象にしています。XHTML 1.0 Transitional は、XHTML 標準の中で最も制約がゆるやかで、既存の HTML ページとの互換性が最も高い標準です。ただし、XHTML 1.0 Strict 標準や XHTML 1.1 標準を対象とした ASP.NET 2.0 ページを構築することも可能です (後の「XHTML 準拠の構成」を参照してください)。
XHTML ページの作成
HTML ページと異なり、XHTML ページは正しい形式の有効な XML ドキュメントである必要があります。HTML と XHTML の違いは、XHTML 1.0 の推奨事項 (英語) の第 4 節にまとめられています。有効な XHTML ページを構築するための最も重要な要件の一覧を次に示します。
- ページに有効な XHTML DOCTYPE が含まれている必要がある。
有効な XHTML ページでは、すべてのコンテンツの前に XHTML DOCTYPE が含まれている必要があります。Visual Studio 2005 または Microsoft Visual Web Developer で新しい ASP.NET ページを作成すると、XHTML 1.0 Transitional 用の適切な DOCTYPE がページに自動的に含められます。4 つの標準 XHTML DOCTYPE を次に示します。
XHTML 1.0 Transitional
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
XHTML 1.0 Strict
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
XHTML 1.0 Frameset
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
XHTML 1.1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
ページに DOCTYPE を追加すると、ブラウザでのページの表示方法に影響を与えます。詳細については、後の「XHTML と DOCTYPE スイッチ」を参照してください。
- ルート要素は XHTML 名前空間を参照している必要がある。
XHTML ページの <html> 開始タグで、既定の名前空間 http://www.w3.org/1999/xhtml (英語) を指定する必要があります。XHTML 1.0 Transitional ページの有効な <html> 開始タグの例を以下に示します。
<html xml:lang="en" lang="en">
- すべての要素名と属性名は小文字で記述する必要がある。
XML では大文字と小文字が区別されます。したがって、<p> タグと <P> タグは異なります。XHTML の有効な段落タグは前者のみです。
- 属性値は常に引用符で囲む必要がある。
属性値は常に二重引用符または単一引用符で囲みます。たとえば、次の XHTML は無効です。
<a href=SomePage.aspx>Next</a>
ここでは、href 属性に引用符がありません。次の XHTML は有効です。
<a href="SomePage.aspx">Next</a>
[ツール] - [オプション] - [書式] のメニュー オプションをクリックして、属性値を自動的に引用符で囲むように Visual Studio 2005 および Visual Web Developer を構成することができます。
- 空でない要素の開始タグには、必ず対応する終了タグが存在する必要がある。
開始の <p> タグがある場合は、終了の </p> タグを含めて、段落の終わりを示す必要があります。<br> タグなど、コンテンツを含むことがないタグの場合は、<br></br> のように開始タグと終了タグを両方記述するか、または空要素の短縮形である <br /> を使用することができます。
XHTML ページで既存の HTML ブラウザとの下位互換性を実現するには、タグの開始方法と終了方法に注意する必要があります。たとえば、既存の HTML ブラウザは、開始と終了を示す <br></br> タグを、2 つの <br> 要素と誤って解釈する傾向があります。そのため、空要素の短縮形である <br /> を使用する必要があります。
さらに、終了のスラッシュの前にスペースを追加するよう注意しないと、既存の HTML ブラウザが空要素の短縮形である <br /> を解釈するときに問題が発生します。したがって、ページに <br> 要素を追加するときは、<br/> ではなく、<br [スペース] /> を使用する必要があります。
- タグはオーバーラップできない。
タグを入れ子にすることは可能ですが、タグをオーバーラップさせることはできません。たとえば、次の XHTML は有効です。
<b><i>This is bold and italic</i></b>
しかし、次の XHTML は無効です。
<i><b>This is bold and italic</i></b>
- 属性の値を省略しない。
多少おかしく見える場合でも、すべての属性に値を指定する必要があります。たとえば、<input type="checkbox" checked /> というタグは、checked 属性に値が指定されていないため、有効な XHTML ではありません。このタグは、<input type="checkbox" checked="checked" /> と記述する必要があります。
-
name 属性の代わりに id 属性を使用する必要がある。
HTML では、name 属性を使用して、<a>、<applet>、<form>、<frame>、<iframe>、<img>、および <map> の各要素を識別します。XHTML 1.0 Transitional ページを構築する際に name 属性を使用することは可能ですが、name 属性は XHTML 1.0 Strict 標準および XHTML 1.1 標準からは削除されています。これらの要素を識別するには、代わりに id 属性を使用する必要があります。
-
<script> 要素と <style> 要素のコンテンツは、CDATA セクションにラップする必要がある。
< や & などの特殊文字、または < や & などのエンティティ参照をスクリプトまたはスタイル シートで使用する場合は、次に示すように、スクリプトまたはスタイル シートのコンテンツを CDATA (文字データ) セクションとしてマークする必要があります。
<script type="text/javascript">
<![CDATA[
function isLess(a, b) {
if (a < b)
return true;
}
]]>
</script>
スクリプトに含まれている JavaScript 関数に、< 文字が含まれていることに注目してください。スクリプトを CDATA セクションにラップしない場合は、< 文字は XHTML タグの開始を示していると解釈されます。
CDATA セクションの使用は、すべてのブラウザに対応しているわけではありません。たとえば、Internet Explorer は <script> タグ内の CDATA セクションを構文エラーと見なします。この問題を回避するには、次のように JavaScript コメントを追加します。
<script type="text/javascript">
/* <![CDATA[ */
function isLess(a, b) {
if (a < b)
return true;
}
/* ]]> */
</script>
JavaScript では、/* と */ を使用して、コメントの開始と終了を示します。したがって、CDATA セクションは JavaScript からは見えなくなりますが、ページを解析するブラウザからは見えています。一般に、スタイル ルールとスクリプトは外部ファイルに配置し、そのファイルを XHTML ページから参照する方が好ましいとされています。外部のスタイル シートおよびスクリプトを使用すると、これらの問題をすべて回避できます。
XHTML と ASP.NET コントロール
ASP.NET 2.0 フレームワークに含まれているすべての ASP.NET コントロールは、有効な XHTML を既定で表示します。つまり、ページに ASP.NET コントロールを追加するときに、有効な XHTML マークアップを生成するために特別な作業は必要ありません。たとえば、ページに GridView コントロールを追加すると、GridView コントロールによって有効な XHTML マークアップが生成されます。
ここで 3 つの点を明らかにしておく必要があります。第 1 に、ASP.NET コントロールを含むページのソース コードは、XHTML としては検証されません。ASP.NET ページを検証するときは、ページのソースではなく、ページの表示されたコンテンツ (Internet Explorer で [ソースの表示] をクリックしたときに表示されるすべてのもの) を検証する必要があります。
第 2 に、ASP.NET ページを作成するときに、無効な XHTML を記述するのを防ぐ仕組みは存在しません。当然ながら、あらゆるタグを ASP.NET ページに追加することができます。たとえば、<font> タグをページに追加すると、そのページは XHTML 1.0 Strict としては検証されません。
最後に、カスタム ASP.NET コントロールを使用する場合には、動作は保証されません。サードパーティの ASP.NET コントロール (たとえば、大幅に機能強化された DataGrid コントロールなど) を購入した場合、そのコントロールが有効な XHTML を表示するとは限りません。適切な作業を行うのは、コントロール ベンダの責任です。
XHTML ページの検証
Visual Studio 2005 と Visual Web Developer では、Web ページを構築すると、ページが自動的に検証されます。検証の問題は [ソース] ビューの該当するコンテンツの下に緑または赤の波線が表示されます。赤の波線は、終了タグがないなどの検証エラーを表します。緑の波線は、古いタグが使用されているなどの検証の警告を表します。
任意の波線の上にマウスを移動すると、検証エラーまたは警告メッセージを含むヒントが表示されます (図 1 を参照)。また、[Error List] ウィンドウで検証エラーおよび警告の一覧を表示することもできます ([表示] - [その他のウィンドウ] - [Error List] をクリック)。
図 1. XHTML ドキュメントの検証 (拡大するには画像をクリックしてください)
既定では、Visual Studio 2005 と Visual Web Developer は Internet Explorer 6.0 スキーマに照らしてページを検証するように構成されます。XHTML スキーマに対してページを検証する場合は、ツール バーのドロップダウン リストから、XHTML スキーマを 1 つ選択する必要があります。または、[ツール] - [オプション] - [Validation] をクリックして、対象のスキーマを選択することも可能です。
また、W3C 検証サービス (英語) を使用して ASP.NET ページを検証することもできます。W3C 検証サービスを使用すると、URL を指定するか、XHTML ページのソースをアップロードすることにより、ページを検証することができます。
XHTML と DOCTYPE スイッチ
Web ページの DOCTYPE を指定すると、ブラウザによるページの表示方法に影響を与えます。Internet Explorer、Mozilla Firefox、および Opera は、いずれも DOCTYPE スイッチ (DOCTYPE スニッフとも呼ばれます) と呼ばれる機能をサポートしています。
DOCTYPE スイッチは、ブラウザが標準に準拠した Web サイトと従来の Web サイトの両方を正しく表示できるようにするために導入されました。ほとんどの Web サイトは、HTML ページを表示するように開発されており、XHTML ページを表示するようには開発されていません。ブラウザは DOCTYPE の存在を利用し、標準を使用してページを表示するかどうかを判断します。
Internet Explorer 6+ は、Quirks モードおよび Standards モードと呼ばれる 2 つの表示モードをサポートしています。有効な XHTML (または HTML 4.0) DOCTYPE を含むページを表示するとき、Internet Explorer は Standards モードでページを表示します。それ以外の場合は、Quirks モードでページを表示します (詳細については、「Internet Explorer 6 における CSS の拡張」を参照してください)。
Opera ブラウザ (Opera 7+) は、Internet Explorer と同じ 2 つの表示モード (Quirks と Standards) をサポートします (詳細については、http://www.opera.com/docs/specs/doctype/ (英語) を参照してください)。
Mozilla Firefox 1+ は、Quirks モード、Almost Standards モード、および Standards モードの 3 つの表示モードをサポートします。Firefox の Almost Standards モードは、Internet Explorer および Opera の Standards モードに対応します。ページに有効な XHTML 1.0 Transitional DOCTYPE が含まれている場合 (かつ、そのページが text/html という MIME の種類で提供されている場合)、Firefox はそのページを Almost Standards モードで表示します。ページに XHTML 1.0 Strict または XHTML 1.1 の DOCTYPE が含まれている場合 (またはそのページが XML という MIME の種類で提供されている場合)、そのページは Standards モードで表示されます (詳細については、http://www.mozilla.org/docs/web-developer/quirks/doctypes.html (英語) を参照してください)。
ブラウザの現在の表示モードを判断するには、次のクライアント側スクリプトをページに一時的に追加します。このスクリプトは、Internet Explorer、Firefox、および Opera の最新バージョンで使用できます。
<script type="text/javascript">
alert( document.compatMode );
</script>
ブラウザの表示モードに注意する必要があるのは、それがカスケード スタイル シートをページに適用する方法に影響を与えるからです。既存の HTML ページを XHTML ページに変換した場合、それらのページをブラウザで開くと、表示が大幅に変わっている可能性があります。
たとえば、Internet Explorer は、ページ要素のサイズを表示モードによって異なる方法で計算します (異なる CSS ボックス モデルを使用します)。Quirks モードの場合、要素の幅は、要素のコンテンツ、パディング、境界線、および余白を合計することによって計算されます。Standards モードの場合、要素の幅は、要素のコンテンツの幅のみを考慮して計算されます。
例として、次のような 2 つの <div> タグを考えてみてください。
<div style="width:400px;border:solid 1px black">
First Box
</div>
<div style="width:400px;border:solid 1px black;padding:10px">
Second Box
</div>
2 つの <div> 要素は、2 つ目の <div> 要素にパディングが追加されていること以外は同じです。Quirks モード (図 2 を参照) では、2 つの <div> 要素は同じサイズで表示されます。これは、2 つ目の <div> 要素に追加されているパディングが要素の幅を計算するときに考慮されるからです (幅の合計は、どちらの要素でも 400 ピクセルになります)。Standards モード (図 3 を参照) では、2 つ目の <div> 要素は最初の <div> 要素よりも幅が広く表示されます。これは、要素の幅を計算するときにパディングが考慮されないからです (幅の合計は、どちらの要素でも 400 ピクセルより大きくなります)。
図 2. Quirks モード
図 3. Standards モード
これは Quirks モードでのブラウザの違いを示す一例にすぎません。Quirks モードでは、各ブラウザによる W3C カスケード スタイル シート標準の実装方法が大きく異なります。Standards モードに切り替えることの利点は、このモードでは、今日のほぼすべてのブラウザに対し、W3C 標準をよく似た方法で実装することが求められるということです (まったく同じではありませんが、はるかに近くなっています)。
Web ページがさまざまなブラウザで同じように表示されるようにする場合は、XHTML 1.0 Transitional DOCTYPE を含めることにより、Standards モード (Internet Explorer と Opera の場合) および Almost Standards モード (Firefox の場合) に切り替えることをお勧めします。さいわい、Visual Studio 2005 と Visual Web Developer では、既定でこの DOCTYPE がすべての新規 ASP.NET ページに自動的に追加されます。
XHTML と MIME の種類
Web ブラウザが Web サーバーのページを要求すると、Web サーバーは特定の MIME の種類 (コンテンツの種類とも呼ばれます) を使用してページを提供します。たとえば、HTML ページは text/html、GIF イメージは image/gif、Microsoft Word 文書は application/msword という MIME の種類を使用してそれぞれ提供されます。
ブラウザは MIME の種類を使用して、ページ (およびその他のリソース) の処理方法を決定します。たとえば、認識可能なイメージの MIME の種類を持つファイルを Web サーバーから取得した場合、ブラウザはそのファイルをイメージとして解釈して表示します。application/msword という MIME の種類のファイルを取得した場合、ブラウザは Microsoft Word を自動的に起動してドキュメントを表示します (実際の動作は、ブラウザおよびブラウザの構成によって異なります)。
W3C は XHTML ドキュメント用の MIME の種類を導入しました。この新しい MIME の種類は、application/xhtml+xml です。W3C では、XHTML ドキュメントを提供するときは、application/xhtml+xml という MIME の種類を使用するよう推奨しています。これは、XHTML ページは従来の HTML ページよりも厳密に解釈する必要があるからです。
ページ ディレクティブに ContentType 属性を含めることにより、ASP.NET ページを特定の MIME の種類を使用して提供することができます。たとえば、次のディレクティブを ASP.NET ページの先頭に含めると、ページは application/xhtml+xml として提供されます。
<%@ ContentType="application/xhtml+xml" %>
この W3C の推奨には、大きな問題が 1 つあります。それは、すべてのブラウザが application/xhtml+xml を認識するわけではないということです。具体的には、Internet Explorer (最も普及している Web ブラウザ) は application/xhtml+xml という MIME の種類を認識しません。したがって、推奨されている application/xhtml+xml という MIME の種類を使用した XHTML ページの提供は、現実的な選択肢ではありません。
この問題を解決する方法は 3 つあります。text/html という MIME の種類を使用して XHTML ページを提供する方法、application/xml (または text/xml) という MIME の種類を使用して XHTML ページを提供する方法、およびコンテンツ ネゴシエーションを使用する方法です。これら 3 つの方法をそれぞれ見ていきましょう。
最初の方法、つまりページを text/html として提供する方法は、最も簡単です。ASP.NET ページは、既定ではこの MIME の種類で提供されます。さらに W3C では、既存の HTML ブラウザにページを提供するときは、この方法を使用するよう推奨しています (http://www.w3.org/TR/xhtml-media-types/ (英語) を参照)。XHTML 1.0 Transitional ページを作成していて、Web アプリケーションの主要なユーザーが application/xhtml+xml という MIME の種類を認識しないブラウザを使用している場合、ページを text/html として提供するのは完全に理にかなった方法に思えます。XHTML 1.0 Transitional 標準は、開発者が既存の HTML ページを XHTML に簡単に移行できるようにするために導入されたものです。
ただし、この考え方には異論もあります。たとえば、Ian Hickson は、XHTML ページを text/html として提供すべきではないと主張しています。この方法により、ずさんで壊れた XHTML ページが助長されるためです (http://hixie.ch/advocacy/xhtml (英語) を参照)。Ian は、XHTML 標準を完全にサポートするブラウザが増えるまで、Web ページの作成者は HTML 4.0 を使用するよう推奨しています。
2 つ目の方法は、application/xml または text/xml という MIME の種類を使用し、XHTML ページを XML として提供する方法です。Internet Explorer が XML ドキュメントを受け取ると、そのドキュメントは XML ドキュメントとして解析され、ブラウザに表示されます (ドキュメントは document.XMLDocument オブジェクトによって公開される XML DOM によって表されます)。
XHTML ドキュメントを XML として提供することの利点は、XHTML ドキュメントの問題が Internet Explorer の XML パーサーによってすべてキャッチされるということです。たとえば、ドキュメント内でタグがオーバーラップしていたり、属性の値が引用符で囲まれていなかったりすると、そのドキュメントは表示されず、エラー メッセージが表示されます (図 4 を参照)。XHTML 純粋主義者は、不正な XHTML の記述が回避されることから、この動作を好ましいものと考えています。
図 4. Internet Explorer での XML の表示
この方法の問題は、Internet Explorer は既定では XML ドキュメントのソースを表示するということです。したがって、XHTML ドキュメントを XML として提供すると、Web サイトを訪れた人には XHTML ドキュメントのソースが表示され、意図した出力は表示されないことになります。W3C では、この問題を解決するための "裏技" を提案しています (http://www.w3.org/MarkUp/2004/xhtml-faq#ie (英語) を参照)。XSLT 変換を使用して XHTML ドキュメントを HTML に変換すれば、ドキュメントは XML として解析され、HTML として表示されます。
たとえば、リスト 1 に示す ASP.NET ページは、XML ドキュメントとして提供されますが、HTML ドキュメントに変換されます。結果として、ページは Internet Explorer、Opera、および Firefox で正しく表示されます。
リスト 1. XMLPage.aspx
<%@ Page Language="VB" ContentType="text/xml" %>
<?xml-stylesheet type="text/xsl" href="copy.xsl"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>My Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtFirstName" runat="server" />
</div>
</form>
</body>
</html>
ページ ディレクティブにより、このページは text/xml として表示されます。リストの 2 行目では、copy.xsl という名前の XSLT スタイル シートを参照しています。このスタイル シートは、現在のドキュメントに対して恒等変換を実行します。つまり、このスタイル シートは元の XML ドキュメントのすべての要素を新しい HTML ドキュメントにコピーするだけで、それ以外はまったく何も行いません。copy.xsl のソースをリスト 2 に示します。
リスト 2. Copy.xsl
<stylesheet version="1.0"
xmlns="http://www.w3.org/1999/XSL/Transform">
<template match="/">
<copy-of select="."/>
</template>
</stylesheet>
この方法はうまく機能しますが、あまり洗練された方法ではありません。XML ドキュメントを解析するときに追加の検証が行われるのは確かですが、Visual Studio 2005 または Visual Web Developer で ASP.NET ページを構築しているのであれば、同じ検証が開発環境により [ソース] ビューで実行されます。結局、Internet Explorer が受け取るドキュメントは、text/html を送信したときと同じものになります。
3 つ目の方法であるコンテンツ ネゴシエーションは、W3C の推奨の考え方と、最大限のブラウザ互換性を最もうまく組み合わせたものです (http://www.w3.org/2003/01/xhtml-mimetype/content-negotiation (英語) を参照)。コンテンツ ネゴシエーションを使用する際は、異なるブラウザに対し、異なる MIME の種類を使用して ASP.NET ページを提供します。ブラウザが XHTML をサポートしている場合、そのブラウザには XHTML を提供します。サポートしていない場合は、MIME の種類を text/html にしたページをブラウザに提供します。
リスト 3 に示す Global.asax には、異なるブラウザに異なる MIME の種類を提供するために必要なコードが含まれています。このファイルを Web プロジェクトに追加すると、各 ASP.NET ページの MIME の種類は、要求ごとに変更されます。Firefox または Opera にページを提供する場合、ページは application/xhtml+xml として提供されます。一方、Internet Explorer 6 は text/html ページを受け取ります。
リスト 3. Global.asax
<script runat="server">
Sub Application_PreSendRequestHeaders(ByVal s As Object, _
ByVal e As EventArgs)
If Array.IndexOf(Request.AcceptTypes, _
"application/xhtml+xml") > -1 Then
Response.ContentType = "application/xhtml+xml"
End If
End Sub
</script>
XHTML 準拠の構成
ASP.NET 2.0 フレームワークの既定の動作は、XHTML 1.0 Transitional に照らして検証されるページを表示するというものです。XHTML 1.0 Transitional は既存の HTML ページとの互換性が最も高い標準であるため、Web サイトを構築する開発者のほとんどは、この標準を対象にします。ただし、この標準では制約がゆるすぎたり、厳しすぎたりすることもあります。
たとえば、意欲的な人は、XHTML 1.0 Strict の Web サイトや、場合によっては XHTML 1.1 の Web サイトを構築する可能性があります。つまり、XHTML 1.0 Transitional 標準の目標は、こうした制約の厳しい標準に移行するための足がかりとなることです。ASP.NET 2.0 フレームワークは既定で XHTML 1.0 Transitional を対象にしているため、ASP.NET コントロールの中には、XHTML 1.0 Strict または XHTML 1.1 と互換性のない属性を表示するものもあります。
一方で、XHTML 1.0 Transitional 標準は制約が厳しすぎると感じる場合もあるでしょう。XHTML 1.0 Transitional 標準に準拠するため、マイクロソフトは既存の ASP.NET 1.1 コントロールにいくつかの変更を加えました。これらの変更の中には、既存の ASP.NET 1.1 Web サイトを破壊する可能性があるものもあります。
すべての人を満足させるために、マイクロソフトは xhtmlConformance という名前の新しい構成オプションを作成しました。このオプションは、Web サイトの構成ファイルで設定できます。新しい構成オプションを使用すると、Web ページの XHTML 準拠レベルを指定することができます。コードは次のようになります。
<configuration>
<system.web>
<xhtmlConformance
mode="transitional" />
</system.web>
</configuration>
既定では、xhtmlConformance の値は transitional に設定されますが、strict または legacy に設定することもできます。
xhtmlConformance オプションを strict に設定すると、一部の属性は標準の ASP.NET コントロールによって表示されなくなります。たとえば、ASP.NET <form> コントロールは、name 属性を表示しなくなります。ASP.NET ページに (標準に準拠していない) クライアント側スクリプトが含まれている場合を除き、transitional モードから strict モードに切り替えたときに変更に気付くことはありません。
xhtmlConformance オプションを legacy に設定すると、ASP.NET フレームワークは、一部の要素および属性 (すべてではありません) については ASP.NET 1.1 の表示動作に戻ります。この場合、ASP.NET フレームワークは XHTML 標準と互換性のないコンテンツを表示し、ページは XHTML 標準に照らして検証されなくなります。たとえば、legacy モードでは、<br> タグは XHTML で必須の終了スラッシュ (<br />) と共に表示されません。xhtmlConformance を legacy モードに設定する必要があるのは、既存の ASP.NET 1.1 アプリケーションを ASP.NET 2.0 に移行する際に問題に直面した場合のみです。
アクセスしやすい ASP.NET Web サイトの構築
Web 標準に従うことの利点は、最小限の労力で、最大限のユーザーにとってアクセスしやすい Web ページを作成できることです。具体的には、アクセシビリティ標準を使用すると、障害を持つ人がアクセスしやすい Web サイトを構築することができます。
繰り返しになりますが、Web サイトのユーザーの中には、なんらかの障害を持つ人も数多くいます。自分の家族の中に、Web ページとやり取りするのが困難な人が何人いるかを考えてみてください。筆者にも、視覚や運動能力が低下している高齢の親族がいます。この記事を読んでいる読者の多くにも、ほとんどの Web サイトの使用を困難に感じる高齢の父母や祖父母がいるはずです。
アクセスしやすい Web サイトを構築する明確な理由は数多くあります。たとえば、財政的理由、道義的理由、法的理由などです。ただしここでは、法的な理由に着目します。米国では、リハビリテーション法の第 508 条により、連邦政府機関によって開発されたすべての Web サイトは、障害を持つ人がアクセスしやすいものでなければならないとされています。この法律は、連邦政府機関および連邦政府機関と契約を交わしている企業に適用されます (http://www.section508.gov (英語) を参照)。
他の国でも同じような要件が存在します。たとえば、カナダでは、Treasury Board Common Look and Feel Standards により、連邦政府機関によって開発された Web サイトはアクセスしやすくなければならないとされています。オーストラリアでは、障害者差別禁止法 (Disability Discrimination Act) により、オーストラリア国内のサーバーでホストされているすべての Web サイトは、政府の Web サイトであるかどうかに関係なく、アクセスしやすくなければならないとされています (アクセシビリティに関する法律の詳細については、http://www.w3.org/WAI/Policy (英語) を参照)。
障害を持つ人がアクセスしにくい Web サイトを意図的に構築する Web サイト開発者はいないでしょう。問題は、ほとんどの開発者が、さまざまなアクセシビリティ標準をよく知らないことです。
以降の節では、最も重要なアクセシビリティ標準である WCAG 標準と第 508 条標準の 2 つについて簡単に説明します。また、ASP.NET コントロールを使用してアクセスしやすい Web ページを構築する方法も学習します。最後に、Web ページのアクセシビリティを "検証" する方法を示します。
アクセシビリティ標準
アクセシビリティ標準および法律は、ほとんどすべて W3C Web Content Accessibility 1.0 Guidelines (WCAG) から派生しています。これらのガイドラインは、1999 年 5 月 5 日に World Wide Web Consortium の推奨として初めて公開されました (http://www.w3.org/TR/WCAG10 (英語) を参照)。
WCAG は 14 のガイドラインで構成されています。さらに各ガイドラインは、ガイドラインをさらに明確にする 1 つ以上のチェックポイントで構成されています。各チェックポイントは、1 から 3 までの優先順位でランク付けされています。ガイドラインの実装を容易にするために、W3C ではガイドラインに従うためのテクニックを含む一連のドキュメントを発行しています (http://www.w3.org/TR/WCAG10-TECHS/ (英語) を参照)。
WCAG ガイドラインへの準拠はさまざまなレベルで示すことができます。Web サイトが優先順位 1 のチェックポイントをすべて満たしていることを示す場合は、準拠レベル A を表すロゴを表示することができます。Web サイトが優先順位 1 および 2 のチェックポイントをすべて満たしている場合は、Web サイトに準拠レベル AA のロゴを表示することができます。さらに、すべてのチェックポイントを満たしている Web サイトでは、準拠レベル AAA のロゴを表示することができます (http://www.w3.org/WAI/WCAG1-Conformance.html (英語) を参照)。
第 508 条ガイドラインは、WCAG ガイドラインから派生しています。米国ではこれらのガイドラインが法的な効力を持つため、連邦政府機関 (および連邦政府機関と契約を交わしている企業) はこの一連のガイドラインを最重視する必要があります。第 508 条ガイドラインの全文は、第 508 条の Web サイト (英語) で読むことができます。
ASP.NET 2.0 フレームワークは、WCAG の優先順位 1 および 2 のすべてのチェックポイントと、第 508 条のすべてのガイドラインを満たすことができるように設計されています。これらのガイドラインはきわめて重要視されました。ASP.NET 2.0 フレームワークに関わるすべての開発者は、すべての ASP.NET コントロールのアクセシビリティを見直してテストするよう求められました。さらに、ガイドラインに照らしてページをテストすることができるよう、すべての開発者のデスクトップにスクリーン リーダーがインストールされました。
ASP.NET 2.0 でのアクセシビリティの向上
ここでは、ASP.NET 2.0 フレームワークにおけるアクセシビリティの向上の 6 つの領域に着目します。以降の節では、ASP.NET コントロールを使用して、アクセスしやすいイメージ、フォーム、ナビゲーション、データ、および XHTML を表示する方法を学びます。最後に、ASP.NET ページでのクライアント側スクリプトの使用に関連するアクセシビリティの問題についても考えます。
アクセスしやすいイメージの作成
Web サイトとやり取りするすべてのユーザーが、実際に Web サイトを見ることができると仮定しないでください。目の見えない人や、視力の弱い人であれば、スクリーン リーダーや点字ディスプレイを使用して Web サイトを訪問する必要がある可能性があります。スクリーン リーダーは、スピーチ シンセサイザを使用して Web ページ内のテキストを読み上げます。点字ディスプレイは、ページ内のテキストを点字の表現に変換します。
イメージや、Java、Shockwave、Flash コンテンツなどのその他の非テキスト ページ要素は、Web ページを見ることができない人にとっては役に立たないコンテンツです。視力の弱い人や、目の見えない人がアクセスしやすい Web サイトを作成するには、Web ページ内のすべての非テキスト コンテンツについて、それらと同等のテキストを提供する必要があります。
Web ページ内のすべてのイメージには、alt 属性を含める必要があります。alt 属性は、スクリーン リーダーやその他の支援デバイスによって読み上げられる代替テキストを表すために使用されます。alt 属性の使用方法を以下に示します。
<img src="Products23.gif" alt="Image of Products" />
alt 属性には、イメージについての説明を含める必要があります。どのような場合でも、単にイメージのファイル名を含めるだけではいけません。alt 属性の目的は、目の見える人にイメージが伝える情報を、目の見えない人にも同じように伝えることです。alt 属性の値を記述するには、要素の意味を人間が解釈できる必要があります。そのため、alt 属性を作成するプロセスを自動化することはできません。
イメージを表示するすべての ASP.NET コントロールには、イメージの代替テキストを指定するメソッドが含まれています。たとえば、ASP.NET Image コントロールには AlternateText プロパティがあります。Image コントロールを使用する場合は、AlternateText 属性に意味のある値を設定する必要があります。
<asp:Image ImageUrl="Products23.gif"
AlternateText="Image of Products" Runat="Server" />
イメージをデザイン要素としてのみ使用している場合、そのイメージの alt 属性には空の文字列を設定する必要があります。イメージが有益な情報を提供しないのであれば、スクリーン リーダーによるページの読み上げを混乱させる理由はありません。
<img src="PageDivider.gif" alt="" />
ASP.NET 2.0 フレームワークでは、空の AlternateText を表示できるようにするために、特殊な方法を使う必要がありました。ASP.NET コントロールの属性に空のテキストを割り当てると、ASP.NET コントロールはその属性を一切表示しません。たとえば、次の ASP.NET Image コントロールをページに追加する場合を考えてみてください。
<asp:Image ImageUrl="PageDivider.gif" AlternateText=""
Runat="Server" />
この場合、次のタグが表示されます。
<img src="PageDivider.gif" style="border-width:0px;" />
alt 属性が消えていることに注目してください。これは ASP.NET コントロールのすべての属性の既定の動作です。属性に値を割り当てない場合、その属性は表示されません。しかしここでは、alt 属性の空の値をどうしても表示する必要があります。
この問題を解決するために、Image コントロールで空の代替テキストを表示できるようにするための新しいプロパティが、ASP.NET 2.0 フレームワークで導入されました。それが GenerateEmptyAlternateText プロパティです。
<asp:Image ImageUrl="PageDivider.gif"
GenerateEmptyAlternateText="true" Runat="Server" />
GenerateEmptyAlternateText プロパティを使用すると、alt="" 属性が正しく表示されます。
イメージが、組織図などの非常に複雑なものを表している場合、alt 属性を使用して代替テキストによる説明を提供することはできません。イメージの意味について長い説明を提供する必要があるときは、longdesc 属性を使用する必要があります。
longdesc 属性は、値の相対 URL または絶対 URL を受け入れます。この URL は、イメージのコンテンツについてのテキストによる説明を含むページにリンクしている必要があります。この属性を <img> ダグで使用する例を次に示します。
<img src="OrgChart.gif" alt="Company Organization Chart"
longdesc="/OrgChartDescription.aspx" />
ASP.NET Image コントロールには、HTML の longdesc 属性に対応する DescriptionUrl というプロパティがあります。このプロパティの使用方法を示す例を次に示します。
<asp:Image
ImageUrl="OrgChart.gif"
AlternateText="Company Organization Chart"
DescriptionUrl="/OrgChartDescription.aspx"
Runat="server" />
アクセスしやすいフォームの作成
Web ページのフォームは、視覚や運動機能の低下している人にとって問題になることがあります。スクリーン リーダーを使用して Web ページのフォームにアクセスした場合、フォームのフィールドを対応するラベルに関連付けるのが難しい場合があります。たとえば、Web ページに次のフォームが含まれているとします。
<table>
<tr>
<td>First Name:</td>
<td><input name="txtFirstName" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="txtLastName" /></td>
</tr>
</table>
このフォームは、ユーザーの名前と名字を入力するフィールドを表示します。この例では、フォームはテーブル内に表示されるため、スクリーン リーダーを使用しているユーザーにとっては、適切なラベルを適切なフォーム フィールドに関連付けるのが難しい可能性があります。HTML 4.0 では、フォーム フィールド ラベルをフォーム フィールドに関連付けるための新しいタグが導入されました。それが <label> タグです。先ほどのフォームを、<label> タグを使用して記述する方法を次に示します。
<table>
<tr>
<td><label for="txtFirstName">First Name:</label></td>
<td><input name="txtFirstName" id="txtFirstName" /></td>
</tr>
<tr>
<td><label for="txtLastName">Last Name:</label></td>
<td><input name="txtLastName" id="txtLastName" /></td>
</tr>
</table>
<label> タグは、フォーム フィールド ラベルを対応するフォーム フィールドに明示的に関連付けます。<input> フィールドに id 属性が含まれていることに注目してください。これは、for 属性の値は入力フィールドの name 属性ではなく、id 属性である必要があるからです。
通常、ASP.NET Label コントロールは <span> タグを生成します。ただし、ASP.NET Label コントロールを宣言するときに AssociatedControlId プロパティを指定すると、コントロールは <label> タグを表示します。ASP.NET の Label コントロールと TextBox コントロールを使用してアクセスしやすいフォームを生成する方法を次に示します。
<table>
<tr>
<td><asp:Label AssociatedControlID="txtFirstName"
runat="server">First Name:</asp:Label></td>
<td><asp:TextBox ID="txtFirstName" runat="server" /></td>
</tr>
<tr>
<td><asp:Label AssociatedControlID="txtLastName"
runat="server">Last Name:</asp:Label></td>
<td><asp:TextBox ID="txtLastName" runat="server" /></td>
</tr>
</table>
ASP.NET コントロールのラベルを指定するときは、HTML の <label> タグではなく、ASP.NET の Label コントロールを使用する必要があります。TextBox コントロールなどの ASP.NET コントロールに ID を割り当てた場合、ブラウザに表示される ID は、コントロールに割り当てた ID と異なる ID になる可能性があります。そのため、<label> タグを使用すると、<label> タグ内の ID は、表示された TextBox コントロールの ID と一致しない可能性があります。一方、ASP.NET の Label コントロールを使用する場合は、この問題に注意する必要はありません。
ASP.NET コントロールの CheckBox、RadioButton、CheckBoxList、および RadioButtonList は、<label> タグを自動的に表示します。これらのコントロールを使用するときは、Text 属性を使用してコントロールのテキストを設定するよう注意してください。次のように記述しないでください。
<asp:CheckBox Runat="Server" /> Include Gift Wrap
代わりに、次のように記述します。
<asp:CheckBox Text="Include Gift Wrap" Runat="Server" />
大きなフォームも、スクリーン リーダーを使用して Web ページとやり取りする人にとって問題になる場合があります。大きなフォームが読み上げられるのを聞いていると、今聞いているのがフォームのどの部分なのかわからなくなりがちです。大きなフォームを表示するときは、フォームを小さなかたまりに分割することをお勧めします。フォームを複数のセクションに分割するには、<fieldset> タグを使用します。このタグの使用方法を示す例を次に示します。
<form id="form1" runat="server">
<div>
<fieldset>
<legend>Contact Information</legend>
... フォーム フィールド
</fieldset>
<fieldset>
<legend>Payment Information</legend>
... フォーム フィールド
</fieldset>
</div>
</form>
このフォームは、<fieldset> タグを使用して 2 つのサブフォームに分割されています。<legend> タグは、サブフォームの目的を示すために使用されています。Internet Explorer、Firefox、および Opera で表示した場合、サブフォームは境界線を使用することにより、別々の領域に視覚的に分割されます (図 5 を参照)。ただし重要なのは、<fieldset> タグの主要な目的は、アクセシビリティだということです。<fieldset> タグの外観が気にいらない場合は、スタイル シート ルールを使用してタグの外観を変更するか、CSS の display 属性または visibility 属性を使用してタグを完全に隠すことができます。
図 5. <fieldset> タグ
Web フォームを使いにくいと感じる可能性がある Web ページ ユーザーは、視力の弱い人だけではありません。運動機能の低下している人も、フォームとのやり取りを困難に感じることがあります。
Web フォームを構築するときは、フォームの各フィールドに accesskey 属性と tabindex 属性を必ず含めることをお勧めします。accesskey 属性を使用すると、マウスを使用することができない人でも、フォームの任意のフィールドに直接移動できます。tabindex 属性を使用すると、フォーム フィールドのタブ オーダーを制御することができます。いずれの属性も、ページとのやり取りにキーボード (またはキーボードのように機能する支援デバイス) を使用する必要がある人にとって役立ちます。
accesskey 属性と tabindex 属性を使用するサンプル フォームを次に示します。
<asp:Label
AssociatedControlID="txtFirstName"
AccessKey="f"
runat="server"><u>F</u>irst Name</asp:Label>
<asp:TextBox
id="txtFirstName"
TabIndex="1"
Runat="server" />
<br />
<asp:Label
AssociatedControlID="txtLastName"
AccessKey="l"
runat="server"><u>L</u>ast Name</asp:Label>
<asp:TextBox
id="txtLastName"
TabIndex="2"
Runat="server" />
tabindex 属性は、フォーム フィールドのタブ オーダーを制御するために使用します。最初のフォーム フィールドの tabindex 値は 1 であるため、ユーザーが最初に Tab キーを押すと、ページ内のフォームの前にあるその他の要素はすべてスキップされます。
Internet Explorer または Firefox を使用している場合、Alt キーを押しながら F キーを押すと、フォーカスが [First Name] ボックスに自動的に移動します。Alt キーを押しながら L キーを押すと、フォーカスが [Last Name] ボックスに自動的に移動します。Opera を使用している場合は、アクセス キーを選択する前に、まず Shift キーを押しながら Esc キーを押す必要があります。
[First Name] ラベルと [Last Name] ラベルの最初の文字に下線が引かれていることに注目してください。文字に下線を引くと、Web サイトのユーザーにアクセス キーを視覚的に示すことができます。これは Microsoft Windows アプリケーションでアクセス キーを示すための標準的な方法です。ただし、フォームでアクセス キーを示すための別の方法も提案されています (http://www.cs.tut.fi/~jkorpela/forms/accesskey.html (英語) を参照)。
下線を使用してアクセス キーを示す方法の問題の 1 つは、ボタンの中の文字に下線を引くことができず、またハイパーリンクには既に下線が引かれているということです。たとえば、次の Button コントロールは意図したとおりには機能しません。
<asp:Button
Text="<u>S</u>ubmit"
Runat="server" />
この ASP.NET Button コントロールが表示されると、S という文字に下線が引かれるのではなく、<u>S</u>ubmit というテキストがそのまま表示されます。ASP.NET Button コントロールは HTML <input type="submit"> タグを表示しますが、残念ながら <input type="submit"> タグは下線をサポートしていません。
スタイル ルールを使用すれば、この問題を解決できるはずだと思うかもしれません。残念ながら、カスケード スタイル シートを使用して <input type="submit"> タグ内の単一の文字に下線を引くための、ブラウザ間で互換性のある方法は、現時点で存在しません。
ページでクライアント側 JavaScript を使用すれば、この問題を解決することができます。リスト 4 に示すページには、Alt キーが押されたままであるかどうかに基づいて、すべてのアクセス キーを表示したり隠したりする JavaScript が含まれています。Alt キーを押している間は、ボックスが表示され、アクセス キーの組み合わせが示されます (図 6 を参照)。このスクリプトは、Internet Explorer と Firefox で機能します (Opera では、アクセス キーの選択に Alt キーを使用しません)。
図 6. AccessKeys.aspx
リスト 4. AccessKeys.aspx
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head id="Head1" runat="server">
<title>Contact Form</title>
<style type="text/css">
.accessKey
{
display:none;
position:absolute;
z-index:5000;
padding:3px;
border:solid 1px black;
background-color: #ffffe0
}
</style>
<script type="text/javascript">
/* <![CDATA[ */
window.onload = function()
{
document.onkeydown = displayAccessKeys;
}
function displayAccessKeys(e)
{
if (!e) e = window.event;
if (e.keyCode == 18)
{
toggleAccessKeys();
document.onkeydown = null;
document.onkeyup = hideAccessKeys;
}
}
function hideAccessKeys(e)
{
if (!e) e = window.event;
if (e.keyCode == 18)
{
toggleAccessKeys();
document.onkeyup = null;
document.onkeydown = displayAccessKeys;
}
}
function toggleAccessKeys()
{
var spans = document.getElementsByTagName('span');
for (var k=0;k<spans.length;k++)
if (spans[k].className == 'accessKey' )
{
if ( 'inline' != spans[k].style.display)
spans[k].style.display = 'inline';
else
spans[k].style.display = 'none';
}
}
/* ]]> */
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<table>
<tr>
<td>
<asp:Label
ID="lblFirstName"
AssociatedControlID="txtFirstName"
AccessKey="f"
runat="server">First Name</asp:Label>
</td>
<td>
<asp:TextBox ID="txtFirstName" runat="server" />
<span class="accessKey">access key is f</span>
</td>
</tr>
<tr>
<td>
<asp:Label
ID="lblLastName"
AssociatedControlID="txtLastName"
AccessKey="l"
runat="server">Last Name:</asp:Label>
</td>
<td>
<asp:TextBox ID="txtLastName" runat="server" />
<span class="accessKey">access key is l</span>
</td>
</tr>
<tr>
<td colspan="2">
<asp:Button Text="Submit" runat="server" />
<span class="accessKey">access key is s</span>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
リスト 4 に示すページには、スタイル シートとクライアント側 JavaScript が含まれています。スタイル シートは、accessKey クラスによって識別されるすべての <span> タグのコンテンツを隠します。JavaScript は、Alt キーが押された状態であることを検出し、<span> タグのコンテンツを表示します。
このページは、Web ブラウザでスタイル シートと JavaScript が無効になっている場合でも機能します。その場合、アクセス キーのヘルプは常に表示されます (図 7 を参照)。
図 7. 機能が適切に抑えられた AccessKeys.aspx
アクセスしやすいナビゲーションの作成
筆者はカスタマ サポートに電話をかけ、自動応答システムの指示に従うのが嫌いです。コンピュータが単調な音声ですべての選択肢をいちいち案内しているのを聞いていると、自分が少しずつ歳をとっていくような気がします。キーを 1 つ押し間違えると、自動コンピュータ システムの深みの中で、自分がどこにいるのか永遠にわからなくなってしまいます。
残念なことに、スクリーン リーダーを使用せざるを得ない場合は、ほぼすべての Web ページを訪れたときに、これと同じ経験をすることになります。ほとんどの Web サイトでは、Web サイトのさまざまなセクションへのリンクの一覧を含むナビゲーション バーを、すべてのページに用意しています。スクリーン リーダーを使用している場合は、ページを開くたびに、これらのナビゲーション リンクが 1 つずつ読み上げられるのを聞かなければならないのです。
ナビゲーション バーに簡単な修正を 1 つ加えるだけで、Web ページのアクセシビリティを劇的に向上させることができます。必要なのは、すべてのナビゲーション リンクをスキップするためのメソッドを追加することだけです。それには、"ナビゲーション リンクのスキップ" を使用します。
たとえば、CNN の Web サイト (英語) には、このサイトのさまざまなセクション (World、U.S.、Weather など) を表示するナビゲーション バーがあります。ただし、CNN Web サイトの設計者は、ある賢明な処置を施しています。このページのソースを見ると、ナビゲーション バーの上に次のリンクがあることがわかります。
<a HREF="#ContentArea" "><img src="http://i.cnn.net/cnn/images/1.gif" alt="Click here to skip to main content." width="10" height="1" border="0" align="right"></a>
CNN Web サイトのホームを表示したとき、このリンクは表示されません。リンクに含まれているイメージは、透明な 1 ピクセルのイメージです。しかし、このページにスクリーン リーダーでアクセスすると、イメージに関連付けられている代替テキストが読み上げられます。目の見えない人は、すべてのナビゲーション リンクをスキップし、Web ページの主要なコンテンツ領域に直接移動することができます (自動音声システムで 0 を押してオペレータと直接話すのと同じです)。
ナビゲーション リンクのスキップは、標準の ASP.NET 2.0 コントロールの一部に組み込まれています。具体的には、Menu、TreeView、SiteMapPath、Wizard、および CreateUserWizard の各コントロールで、ナビゲーション リンクのスキップをサポートしています。
たとえば、リスト 5 に示すページには、ASP.NET Menu コントロールが含まれています。このコントロールは、Web サイト内の他のページへのリンクの一覧を表示するために使用されています。
リスト 5. SiteMenu.aspx
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>Skip Navigation</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Menu
id="Menu1"
Runat="server">
<Items>
<asp:MenuItem Text="Home" NavigateUrl="Home.aspx" />
<asp:MenuItem Text="Products" NavigateUrl="Products.aspx" />
<asp:MenuItem Text="Services" NavigateUrl="Services.aspx" />
<asp:MenuItem Text="About" NavigateUrl="About.aspx" />
</Items>
</asp:Menu>
<hr />
ページのメイン コンテンツがここに入ります...
</div>
</form>
</body>
</html>
リスト 5 に示すページのソースを見ると、メニューの先頭に次のリンクがあるのがわかります。
<a HREF="#Menu1_SkipLink" "><img alt="Skip Navigation Links"
src="/WebResource.axd?d=ChXz41GuDxNm-7TcWyCl_w2&t=632495684475122400"
width="0" height="0" style="border-width:0px;" />
このリンクには、幅がゼロで高さがゼロのイメージが含まれていますが、ページを表示したときには、このイメージは表示されません。ただし、スクリーン リーダーを使用してこのページにアクセスした人は、ナビゲーション リンクのスキップを選択し、メニューの末尾にスキップすることができます。
既定では、ナビゲーション リンクのスキップにはナビゲーション リンクのスキップというテキストが含まれています。この値は、Menu コントロールの SkipLinkText プロパティを変更することにより変更できます。
アクセスしやすいデータの作成
ASP.NET 2.0 フレームワークには、データベースのデータを表示するためのコントロールのセットが豊富に含まれています。これらのコントロールには、GridView、DetailsView、DataList、FormView、Repeater などがあります。既定では、GridView、DetailsView、および DataList の各コントロールは、データベース レコードを HTML テーブルに表示します。
情報を HTML テーブルで表示する際、正しく行わなければ、アクセシビリティの問題が生じることがあります。HTML テーブルのコンテンツが読み上げられると、テーブル内の現在の位置がわからなくなりがちです。たとえば、HTML テーブルを使用して製品情報の一覧を表示するとします。このテーブルのコンテンツがスクリーン リーダーによって読み上げられると、特定のテーブル セルが表している情報が製品名に関するものなのか、注文された製品の数に関するものなのか、製品を格納する倉庫用のコードに関するものなのかが、すぐにわからなくなってしまいます。
HTML テーブルを見れば、行または列の見出しを確認することにより、特定のセルの意味を判断することができます。スクリーン リーダーを使用している人がアクセスしやすいテーブルを作成するには、テーブルの見出しを明示的に示し、それらの見出しと各セルを明示的に関連付ける必要があります。
テーブルを作成してデータを表示するときは、常に適切なタグを使用して、列と行の見出しを示す必要があります。テーブルの見出しは、次のように、常に <th> タグで示す必要があります。
<table>
<thead>
<tr>
<th>Product Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Milk</td>
<td>$2.33</td>
</tr>
<tr>
<td>Cereal</td>
<td>$5.61</td>
</tr>
</tbody>
</table>
この例では、<th> タグを使用して、Product Name と Price の 2 つの列見出しを示しています。
既定の外観が気にいらないという理由で、<th> タグを使用しない設計者もいます。ほとんどのブラウザでは、<th> タグのコンテンツは、太字の中央揃えで表示されます。ただし重要なのは、プレゼンテーションを制御するためにタグを使用すべきではないということです。列見出しを通常のテーブル セルのように表示する場合は、次のようなスタイル ルールを追加する必要があります。
<style type="text/css">
th {text-align:left;font-weight:normal}
</style>
テーブルをアクセスしやすくするには、各セルに関連する見出しを明示的に示す必要もあります。そのために使用できる属性はいくつかあります。たとえば、scope、headers、axis などです。
scope 属性を使用すると、テーブル見出しが列見出しなのか、行見出しなのかを示すことができます。たとえば、次のテーブルには、列見出しと行見出しが両方含まれています。これらの見出しは、scope 属性を使用した <th> タグで示されています。
<table>
<thead>
<tr>
<th></th>
<th scope="col">First Train</th>
<th scope="col">Last Train</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Alewife</th>
<td>5:24am</td>
<td>12:15am</td>
</tr>
<tr>
<th scope="row">Braintree</th>
<td>5:15am</td>
<td>12:18am</td>
</tr>
</tbody>
</table>
このテーブルには、ボストンの地下鉄 Red Line の時刻表が含まれています (図 8 を参照)。各列見出しに scope="col" 属性が含まれ、各行見出しに scope="row" 属性が含まれていることに注目してください。
図 8. 単純な地下鉄時刻表
scope 属性は、単純なテーブルではすばらしい働きをします。ただし、より複雑なテーブルでは、headers 属性を使用する必要があります。たとえば、入れ子になったテーブルでは、単一のセルに 3 つ以上の見出しが関連付けられることがあります。headers 属性を使用すると、関連する見出しを使用して各セルを示すことができます。
axis 属性を使用すると、テーブル見出しを分類することができます。たとえば、地下鉄時刻表テーブルでは、場所を表す見出し (Alewife と Braintree) のそれぞれに、属性 axis="location" を追加することができます。axis 属性には、カンマ区切りのカテゴリの一覧を指定することができます。
リスト 6 に示すページには、ボストン地下鉄時刻表のより複雑なバージョンが含まれています。ここでは、headers 属性と axis 属性の両方を使用しています (図 9 を参照)。
図 9. 複雑な地下鉄時刻表
リスト 6. Subway.aspx
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>Red Line Subway Schedule</title>
<style type="text/css">
caption {color:white;background-color:red;font-size:xx-large}
table {width:500px;border-collapse:collapse}
td,th {padding:5px}
td {border:1px solid black}
tbody th {text-align:right}
.headerRow th {font-size:x-large;text-align:left}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<table
summary="This table contains the schedule of train
departures for the Red Line">
<caption>Red Line Schedule</caption>
<thead>
<tr>
<th></th>
<th id="hdrFirstTrain" axis="train">First Train</th>
<th id="hdrLastTrain" axis="train">Last Train</th>
</tr>
</thead>
<tbody>
<tr class="headerRow">
<th id="hdrWeekday" axis="day" colspan="3">Weekday</th>
</tr>
<tr>
<th id="hdrAlewife1" axis="location">Alewife</th>
<td headers="hdrAlwife1 hdrWeekday hdrFirstTrain">5:24am</td>
<td headers="hdrAlwife1 hdrWeekday hdrLastTrain">12:15am</td>
</tr>
<tr>
<th id="hdrBraintree1" axis="location">Braintree</th>
<td headers="hdrBraintree1 hdrWeekday hdrFirstTrain">5:15am</td>
<td headers="hdrBraintree1 hdrWeekday hdrLastTrain">12:18am</td>
</tr>
<tr class="headerRow">
<th id="hdrSaturday" axis="day" colspan="3">Saturday</th>
</tr>
<tr>
<th id="hdrAlewife2" axis="location">Alewife</th>
<td headers="hdrAlewife2 hdrSaturday hdrFirstTrain">8:24am</td>
<td headers="hdrAlewife2 hdrSaturday hdrLastTrain">11:15pm</td>
</tr>
<tr>
<th id="hdrBraintree2" axis="location">Braintree</th>
<td
headers="hdrBraintree2 hdrSaturday hdrFirstTrain">7:16am</td>
<td
headers="hdrBraintree2 hdrSaturday hdrLastTrain">10:18pm</td>
</tr>
</tbody>
</table>
</div>
</form>
</body>
</html>
各テーブル セルに headers 属性が含まれていることに注目してください。headers 属性は、列および行の見出しに対応する ID の、スペース区切りの一覧を表します。地下鉄時刻表テーブルの各セルには、場所、曜日、および列車を表す見出しが関連付けられています。
また、各 <th> タグに axis 属性があることにも注目してください。このタグは、見出しに関連付けられるカテゴリを表すために使用されます。たとえば、Weekday 見出しと Saturday 見出しは、いずれも day 軸に関連付けられています。First Train 見出しと Last Train 見出しは、train 軸に関連付けられています。
さらに、リスト 6 に示すテーブルに summary 属性と <caption> タグが含まれていることに注目してください。summary 属性は、alt 属性と非常によく似た働きをします。summary 属性を使用すると、ブラウザには表示されない、テーブルについての説明を提供することができます。一方、<caption> タグのコンテンツは、ブラウザによって表示されます。テーブルの目的を表すには、<caption> タグを使用する必要があります。
ASP.NET 2.0 の GridView コントロールまたは DetailsView コントロールを使用してデータベースのデータを HTML テーブルに表示すると、生成された HTML テーブルは、既定でアクセスしやすいものになります。たとえば、リスト 7 に示す ASP.NET ページでは、GridView コントロールを使用することにより、Titles データベース テーブルのコンテンツを表示しています。
リスト 7. DisplayTitles.aspx
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>Display Titles</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView
id="grdTitles"
DataSourceId="srcTitles"
Runat="server" />
<asp:SqlDataSource
id="srcTitles"
ConnectionString=
"Server=localhost;Trusted_Connection=true;Database=Pubs"
SelectCommand="Select * FROM Titles"
Runat="server" />
</div>
</form>
</body>
</html>
リスト 7 では、Titles データベース テーブルのレコードを表す SqlDataSource コントロールに、GridView コントロールをバインドしています。リスト 7 に示す ASP.NET ページをブラウザで開くと、Titles データベース テーブルのコンテンツが HTML テーブルに表示されます (図 10 を参照)。
図 10. DisplayTitles.aspx (拡大するには画像をクリックしてください)
GridView コントロールにより、各列見出しの <th> タグが自動的に生成されることに注目してください。さらに、ブラウザで [ソースの表示] を選択すると、各列見出しに対して scope="col" 属性が自動的に生成されることがわかります。
GridView コントロールは、アクセシビリティに関連するプロパティを他にもいくつかサポートします。
-
Caption および CaptionAlign ? これらのプロパティは、GridView コントロールによって生成された HTML テーブルにキャプションを追加するために使用します。
-
RowHeaderColumn ? このプロパティは、列見出しではなく、行見出しを示すために使用します。このプロパティには、データ ソースから返された列の名前 (title_id など) を設定します。
-
UseAccessibleHeader ? このプロパティは、列見出しを <th scope="col"> タグまたは <td> タグを使用して表示するかどうかを示すために使用します。既定では、このプロパティの値は true です。
GridView コントロールに Summary プロパティがないことに注目してください。ただし、ほとんどの ASP.NET コントロールと同じく、GridView コントロールは expando 属性をサポートします。GridView コントロールを宣言するときに任意の属性を宣言するとことができ、宣言した属性はブラウザに表示されます。したがって、GridView に概要を追加する場合は、次のように summary 属性を宣言します。
<asp:GridView
id="grdTitles"
DataSourceId="srcTitles"
summary="Displays the contents of the Titles database table"
Runat="server" />
単純なデータ テーブルをアクセスしやすくなるように表示するには、GridView コントロールの既定の動作で十分です。ただし、入れ子になったテーブルなど、より複雑なテーブルを表示する必要がある場合は、追加の作業を行う必要があります。
たとえば、製品カテゴリの一覧を表示し、それれぞれのカテゴリの下に、そのカテゴリに一致する製品の一覧を表示するとします。つまり、単一ページのマスタ/詳細フォームを作成するということです (図 11 を参照)。この場合、各テーブル セルに headers 属性を含める必要があります。
図 11. 入れ子になった Repeater コントロール
リスト 8 に示すページは、Repeater コントロールを別の Repeater コントロール内に入れ子にし、アクセシビリティ ガイドラインの要件を満たす複雑なテーブルを生成する方法を示しています。
リスト 8. NestedRepeaters.aspx
<%@ Page Language="VB"%>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
Private dtblProducts As New DataTable
Sub Page_Load()
Dim dad As New SqlDataAdapter("SELECT * FROM PRODUCTS", _
"Server=localhost;Trusted_Connection=true;Database=Northwind")
dad.Fill(dtblProducts)
End Sub
Function GetProducts(ByVal CategoryID As Integer) As DataView
Dim view As DataView = dtblProducts.DefaultView
view.RowFilter = "CategoryID=" & CategoryID
Return view
End Function
Function GetCategoryHeader(ByVal index As Integer) As String
Return "hdrCategory" & index.ToString()
End Function
Function GetProductHeader(ByVal productID As Integer) As String
Return "hdrProduct" & productID.ToString()
End Function
Function GetHeaders(ByVal item As RepeaterItem, _
ByVal columnHeader As String) As String
Dim parent As RepeaterItem = _
item.NamingContainer.NamingContainer
Dim categoryHeader As String = _
GetCategoryHeader(parent.ItemIndex)
Dim productHeader As String = _
GetProductHeader(item.DataItem("ProductID"))
Return String.Format("{0} {1} {2}", categoryHeader, _
productHeader, columnHeader)
End Function
</script>
<html >
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
.categoryRow {background-color:yellow}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Repeater
id="grdCategories"
DataSourceId="srcCategories"
Runat="server">
<HeaderTemplate>
<table>
<thead>
<th id="hdrID">ID</th>
<th id="hdrName">Name</th>
<th id="hdrPrice">Price</th>
</thead>
<tbody>
</HeaderTemplate>
<ItemTemplate>
<tr class="categoryRow">
<th colspan="3"
id='<%# GetCategoryHeader(Container.ItemIndex) %>'>
<%# Eval("CategoryName") %>
</th>
</tr>
<asp:Repeater
id="grdProducts"
DataSource='<%# GetProducts(Eval("CategoryID")) %>'
Runat="server">
<ItemTemplate>
<tr>
<th
id='<%# GetProductHeader(Eval("ProductID")) %>'>
<%# Eval("ProductID") %>
</th>
<td
headers='<%# GetHeaders(Container, "hdrName") %>'>
<%#Eval("ProductName")%>
</td>
<td headers=
'<%# GetHeaders(Container, "hdrPrice") %>'>
<%#Eval("UnitPrice", "{0:c}")%>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
<FooterTemplate>
</tbody>
</table>
</FooterTemplate>
</asp:Repeater>
<asp:SqlDataSource
id="srcCategories"
ConnectionString=
"Server=localhost;Trusted_Connection=true;Database=Northwind"
SelectCommand="SELECT * FROM Categories"
Runat="server" />
</div>
</form>
</body>
</html>
リスト 8 では、外側の Repeater コントロールを使用して製品カテゴリの一覧を表示し、内側の Repeater コントロールを使用してカテゴリに一致する製品の一覧を表示しています。また、GetProductHeader と GetCategoryHeader の 2 つのヘルパ関数を使用して、Category Name 見出しと Product ID 見出しの id 値を生成しています。さらに、GetHeaders という名前の別のヘルパ関数を使用し、headers 属性で使用する値を生成しています。
リスト 8 に示す ASP.NET ページは、次のような HTML テーブルを生成します。
<table>
<thead>
<th id="hdrID">ID</th>
<th id="hdrName">Name</th>
<th id="hdrPrice">Price</th>
</thead>
<tbody>
<tr class="categoryRow">
<th colspan="3" id='hdrCategory0'>
Beverages
</th>
</tr>
<tr>
<th id='hdrProduct1'>
1
</th>
<td headers='hdrCategory0 hdrProduct1 hdrName'>
Chai 2
</td>
<td headers='hdrCategory0 hdrProduct1 hdrPrice'>
$18.55
</td>
</tr>
<tr>
<th id='hdrProduct2'>
2
</th>
<td headers='hdrCategory0 hdrProduct2 hdrName'>
Chang
</td>
<td headers='hdrCategory0 hdrProduct2 hdrPrice'>
$19.00
</td>
</tr>
.... テーブルの残りの部分
各 <td> タグに適切な headers 属性が含まれていることに注目してください。
アクセスしやすい XHTML の作成
多くのアクセシビリティ ガイドラインで共有されている共通テーマの 1 つは、Web ページをアクセスしやすくするには、標準に準拠する必要があるという考え方です。これらのガイドラインに従い、Web サイトを構築するときは、XHTML やカスケード スタイル シートの最新バージョンなど、W3C の最新の標準を使用するよう心がける必要があります。
具体的には、Web ページを設計するときは、ドキュメントの構造をプレゼンテーションから切り離す必要があります。Web ページの構造を表すにはタグを使用し、Web ページの外観を制御するにはカスケード スタイル シートを使用します。
たとえば、テキストのブロックをインデントするだけの目的で、<blockquote> 要素を使用しないでください。<blockquote> 要素の目的は、ソースの引用を作成することにあります。テキストをインデントする場合は、代わりにカスケード スタイル シートの margin 属性を使用する必要があります。
また、<table> タグは、データのテーブルを表す場合にのみ使用するよう心がけてください。<table> タグを使用して Web ページをレイアウトする方法は現時点では一般的ですが、代わりに <div> タグを使用するようにしてください。たとえば、リスト 9 に示すページは 3 つの列でレイアウトされていますが、<table> タグは 1 つも含まれていません (図 12 を参照)。
図 12. テーブルを使用しないページ レイアウト (拡大するには画像をクリックしてください)
リスト 9. Tableless.aspx
<%@ Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head runat="server">
<title>Tableless Layout</title>
<style type="text/css">
#content
{
margin-left:auto;
margin-right:auto;
width:800px;
}
#leftColumn
{
float:left;
width:150px;
border:1px solid black;
padding:10px;
}
#middleColumn
{
float:left;
width:430px;
padding:10px;
}
#rightColumn
{
float:right;
width:150px;
border:1px solid black;
padding:10px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div id="content">
<div id="leftColumn">
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
Left column contents...
</div>
<div id="middleColumn">
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
Middle column contents...
</div>
<div id="rightColumn">
Right column contents...
Right column contents...
Right column contents...
Right column contents...
Right column contents...
Right column contents...
Right column contents...
Right column contents...
</div>
</div>
</form>
</body>
</html>
リスト 9 に示すページには、4 つの <div> タグが含まれています。content という名前の最初の <div> タグは、ページのコンテンツ領域の幅を指定するために使用されています。残りの 3 つの <div> タグ (それぞれ left、middle、right という名前が付いています) は、コンテンツ領域を 3 つの列に分割しています。このページは、Internet Explorer 6、Firefox、および Opera 8 で正しく表示されます (レイアウトに HTML テーブルを使用していない、きわめて美しいページの例については、http://csszengarden.com (英語) を参照してください)。
WCAG ガイドラインでは、<table> タグを使用せずにページのレイアウトを作成することが常に可能なわけではないことを認めています。古いブラウザはカスケード スタイル シート標準を完全にはサポートしていないためです (WCAG ガイドライン 5 を参照)。やむを得ず、レイアウトにテーブルを使用する場合は、テーブルのコンテンツを線形化 (つまり、テーブルのセル順で読み取る) したときに、それらが理解できるものであるかどうかを確認する必要があります。
ASP.NET フレームワーク は古いブラウザと新しいブラウザの両方と互換性を備えている必要があるため、一部の ASP.NET コントロールは、実際にはレイアウトに <table> タグを使用します。たとえば、ASP.NET 2.0 の Login コントロールは、ユーザー名とパスワードの入力フィールドのレイアウトを制御するために <table> タグを使用します。
アクセスしやすいスクリプトの作成
WCAG ガイドラインと第 508 条ガイドラインの両方に含まれている非常に厳しい制約の 1 つは、クライアント側スクリプトに関するものです。WCAG 1.0 ガイドラインの優先順位 1 のチェックポイントには、次のように書かれています。
6.3 スクリプト、アプレット、またはその他のプログラミング オブジェクトがオフになっているか、またはサポートされていない場合でも、そのページを使用できるようにすること。それができない場合は、アクセスしやすい代替ページで同等の情報を提供すること。[優先順位 1]
第 508 条ガイドラインにも同様の要件が含まれています。
(l) ページで、コンテンツの表示やインターフェイス要素の作成のためにスクリプト言語を使用する場合、スクリプトによって提供される情報は、支援技術によって読み取ることができる機能的なテキストによって識別可能でなければならない。
問題は、いくつかの ASP.NET コントロールにはクライアント側 JavaScript が必要だということです。代表的な例として、ASP.NET LinkButton コントロールがあります。LinkButton コントロールは、JavaScript を使用して、このコントロールを含むフォームを Web サーバーに送信します。
この問題に対する有効なソリューションはありません。すべてのアクセシビリティ ガイドラインを満たす Web サイトを構築する必要がある場合は、クライアント側スクリプトの使用に十分注意する必要があります。LinkButton コントロールなど、JavaScript に依存する特定の ASP.NET コントロールの使用は避ける必要がある場合があります。
残念ながら、今日の Web サイトを構築するときにこのガイドラインに従うのは困難です。このガイドラインでは、Web サイトはアプリケーションというよりも雑誌に近いものだと仮定しているようです。今日の Web サイトには、動的なクライアント側コンテンツが含まれる傾向があります。たとえば、不動産関連の Web サイトの多くには、JavaScript の住宅ローン計算機能が含まれています。JavaScript の住宅ローン計算機能と同等のテキストがどんなものであるかは、わかっていません。
ページのアクセシビリティの検証
XHTML を検証するすべて自動の機能が存在しないのと同様に、アクセシビリティを検証するすべて自動の機能も存在しません。ページのアクセシビリティを判断するには、人間による解釈が必要なため、アクセシビリティを自動的に検証することはできません。
たとえば、Web ページをアクセスしやすくするには、ページ内のすべてのイメージに意味のある代替テキストを含める必要があります。今のところ、テキストがイメージと同じ意味を持っているかどうかを判断できるコンピュータは存在しません。アクセシビリティ検証コントロールが実行できることは、せいぜいチェックする必要がある事項の一覧を提供することぐらいです。
Visual Studio 2005 には、アクセシビリティ チェッカーが含まれています (Visual Web Developer には含まれていません)。アクセシビリティ チェッカーを開くには、ツール バーを使用するか、または [ツール] メニューの [Check Accessibility] をクリックします (図 13 を参照)。
図 13. Visual Studio 2005 のアクセシビリティ チェッカー (拡大するには画像をクリックしてください)
アクセシビリティ チェッカーには、WCAG 優先順位 1 のチェックポイント、WCAG 優先順位 2 のチェックポイント、または第 508 条ガイドラインに照らして Web サイトを検証する方法があります。Web サイトの検証結果を表示するには、エラーの一覧を開きます ([表示] - [その他のウィンドウ] - [Error List] メニュー オプションをクリック)。
Visual Studio 2005 のアクセシビリティ チェッカーには、アクセシビリティの問題に関する "マニュアル チェックリスト" を表示するオプションも用意されています。このオプションを選択すると、Web サイトのアクセシビリティを検証するたびに、アクセシビリティの問題についての同一の静的な一覧が [Error List] ウィンドウに表示されます。このチェックリストには、アクセシビリティ チェッカーで自動的に検証することができない問題が含まれています。
Visual Web Developer で Web サイトを構築している場合でも、Web サイトのアクセシビリティをチェックすることができます。それには、オンライン アクセシビリティ チェッカーのいずれかを使用する必要があります。最も一般的な 2 つのオンライン アクセシビリティ チェッカーへのリンクを次に示します。
サンプル アプリケーション : アクセスしやすい XHTML ASP.NET Web サイト
最後に、ASP.NET 2.0 Web サイトを最初から最後まで構築してみましょう。このサンプル Web サイトのソース コードは、このホワイトペーパーに含まれています。サンプル Web サイトのソース コードをダウンロードし、Visual Web Developer または Visual Studio 2005 で Web サイトを開くことができます。
目標は、標準に完全に準拠した Web サイトを作成することです。この Web サイトは、XHTML 1.0 Strict (および XHTML 1.1) として検証されます。さらにこの Web サイトは、障害を持つ人がアクセスしやすいものになります。この Web サイトは、第 508 条と WCAG (優先順位 1 および優先順位 2) のアクセシビリティ要件を満たします。
ここでは、Super Super Bookstore というオンライン書店の Web サイトを構築します。この書店のすべての書籍リストは、Amazon E-Commerce Web サービスを通じて取得します。Amazon E-Commerce Web サービスは、無料サンプル データを豊富に提供しています (Amazon Web サービスの詳細については、http://www.amazon.com/gp/aws/landing.html (英語) を参照してください)。
作業を単純にするために、2 つの ASP.NET ページのみで Web サイトを構成します。
- Default.aspx このページは、指定されたカテゴリの書籍の一覧を表示します。
- Search.aspx このページを使用すると、特定の検索条件を満たす書籍をすべて検索できます。
内部的には、ASP.NET 2.0 フレームワークの新機能をいくつか使用します。たとえば、この Web サイトではマスタ ページを使用して共通のページ レイアウトを作成し、テーマを使用して共通のページ スタイルを作成します。さらに、このサンプル サイトでは新しい GridView コントロールと ObjectDataSource コントロールを使用してデータにアクセスします。
Amazon Web サービスへのアクセス
Super Super Bookstore では、Amazon という名前の共通クラスを使用して書籍の情報を取得し、Amazon の書籍カタログに対して検索を実行します。このクラスをリスト 10 に示します。
リスト 10. Amazon.vb
Imports Microsoft.VisualBasic
Public Class Amazon
Const SubscriptionId As String = "1CD1NYF3YQ830DG7AM02"
''' <summary>
''' カテゴリの書籍をキャッシュから取得します。
''' キャッシュ内に存在しない場合は、Amazon Web サービスを呼び出します
''' </summary>
Public Function GetBooks(ByVal CategoryId As String) _
As AmazonServices.Item()
Dim context As HttpContext = HttpContext.Current
Dim Books As AmazonServices.Item()
If IsNothing(context.Cache(CategoryId)) Then
Books = GetBooksFromAmazon(CategoryId)
context.Cache(CategoryId) = Books
Else
Books = CType(context.Cache(CategoryId), _
AmazonServices.Item())
End If
Return Books
End Function
''' <summary>
''' 特定のカテゴリの書籍を Web サービスから取得します
''' </summary>
Public Function GetBooksFromAmazon(ByVal CategoryId As String) _
As AmazonServices.Item()
Dim service As New AmazonServices.AWSECommerceService()
' 要求を初期化します
Dim searchRequest As New AmazonServices.ItemSearchRequest
With searchRequest
.SearchIndex = "Books"
.Sort = "salesrank"
.ResponseGroup = New String() {"Medium"}
.BrowseNode = CategoryId
End With
Dim search As New AmazonServices.ItemSearch
With search
.SubscriptionId = SubscriptionId
.Request = New AmazonServices.ItemSearchRequest() _
{searchRequest}
End With
' 応答を取得します
Dim response As AmazonServices.ItemSearchResponse = Nothing
Try
service.Timeout = 5000
response = service.ItemSearch(search)
Catch
End Try
If IsNothing(response) Then
Return Nothing
End If
Return response.Items(0).Item
End Function
''' <summary>
''' Amazon Web サービスを呼び出して書籍を検索します
''' </summary>
Public Function SearchBooksFromAmazon(ByVal Author As String, _
ByVal Title As String, ByVal Keywords As String, _
ByVal PowerSearch As String) As AmazonServices.Item()
' 検索するものがない場合は、検索しません
If IsNothing(PowerSearch) And IsNothing(Author) And _
IsNothing(Title) And IsNothing(Keywords) Then
Return Nothing
End If
' 要求を初期化します
Dim service As New AmazonServices.AWSECommerceService()
Dim searchRequest As New AmazonServices.ItemSearchRequest
With searchRequest
.SearchIndex = "Books"
.ResponseGroup = New String() {"Medium"}
If Not IsNothing(PowerSearch) Then
.Power = PowerSearch
Else
If Not IsNothing(Author) Then
.Author = Author
End If
If Not IsNothing(Title) Then
.Title = Title
End If
If Not IsNothing(Keywords) Then
.Keywords = Keywords
End If
End If
End With
Dim search As New AmazonServices.ItemSearch
With search
.SubscriptionId = SubscriptionId
.Request = New AmazonServices.ItemSearchRequest() _
{searchRequest}
End With
' 応答を取得します
Dim response As AmazonServices.ItemSearchResponse
Try
service.Timeout = 5000
response = service.ItemSearch(search)
Catch
End Try
If IsNothing(response) Then
Return Nothing
End If
Return response.Items(0).Item
End Function
''' <summary>
''' Amazon の Author プロパティは著者の一覧を表します。
''' そのため、カンマ区切りのリストを作成します
''' </summary>
Public Shared Function FormatAuthor(ByVal Authors As String()) _
As String
If Not IsNothing(Authors) Then
Return String.Join(", ", Authors)
Else
Return "Not Listed"
End If
End Function
''' <summary>
''' Amazon の ListPrice を米国通貨として書式設定します
''' </summary>
Public Shared Function FormatPrice(ByVal Price As String) As String
If Not IsNothing(Price) Then
Return "$" & Price.Insert(Price.Length - 2, ".")
Else
Return "Not Listed"
End If
End Function
''' <summary>
''' 書籍の詳細情報へのリンクのヒントを書式設定します
''' </summary>
Public Shared Function _
FormatDetailsTooltip(ByVal Title As String) As String
If Not IsNothing(Title) Then
Return String.Format("Link to {0} details", Title)
Else
Return "Link to details"
End If
End Function
''' <summary>
''' 本の表紙が存在しない場合は、既定のイメージを表示します
''' </summary>
Public Shared Function FormatBookCover(ByVal Url As String) _
As String
If Not IsNothing(Url) Then
Return Url
Else
Return "Images/NoBookCover.gif"
End If
End Function
End Class
このクラスで最も重要な関数は、GetBooksFromAmazon と SearchBooksFromAmazon の 2 つです。最初の関数は、カテゴリ別の書籍リストを表示するために、Default.aspx ページから呼び出されます。2 つ目の関数は、ユーザーが書籍を検索できるようにするために、Search.aspx ページから呼び出されます。
どちらの関数も、AmazonServices という名前の Web サービス プロキシ クラスを使用します。このプロキシ クラスは、[Web サイト] - [Web 参照の追加] メニュー オプションをクリックし、http://soap.amazon.com/onca/soap?Service=AWSECommerceService (英語) という URL を入力することによって作成されました。これは米国 Amazon のデータにアクセスするための適切な URL です。
既定のページ
Default.aspx ページは、書籍のカテゴリの一覧と、選択されたカテゴリに一致する書籍の一覧を表示します (図 14 を参照)。Default.aspx ページをリスト 11 に示します。
図 14. 既定のページ (拡大するには画像をクリックしてください)
リスト 11. Default.aspx
<%@ Page Language="VB" MasterPageFile="~/SiteMaster.master"
Title="Super Super Books" %>
<script runat="server">
Sub Page_Load()
Dim categoryIndex As Integer = 0
If Not IsNothing(Request("index")) Then
categoryIndex = Int32.Parse(Request("index"))
End If
MenuCategories.Items(categoryIndex).Selected = True
End Sub
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentBody"
Runat="Server">
<h1>Book Listings</h1>
<hr />
<div id="leftColumn">
<asp:Menu
id="MenuCategories"
ToolTip="Book categories menu"
StaticMenuItemStyle-CssClass="menuNormal"
StaticSelectedStyle-CssClass="menuSelected"
Runat="server">
<Items>
<asp:MenuItem
Text="Arts and Photography"
Value="1"
NavigateUrl="~/Default.aspx?index=0" />
<asp:MenuItem
Text="Biographies and Memoirs"
Value="2"
NavigateUrl="~/Default.aspx?index=1" />
<asp:MenuItem
Text="Children's Books"
Value="4"
NavigateUrl="~/Default.aspx?index=2" />
<asp:MenuItem
Text="Computers and Internet"
Value="5"
NavigateUrl="~/Default.aspx?index=3" />
<asp:MenuItem
Text="Cooking, Food and Wine"
Value="6"
NavigateUrl="~/Default.aspx?index=4" />
<asp:MenuItem
Text="Science Fiction and Fantasy"
Value="25"
NavigateUrl="~/Default.aspx?index=5" />
</Items>
</asp:Menu>
</div>
<div id="middleColumn">
<asp:GridView
id="grdBooks"
DataSourceID="srcBooks"
AutoGenerateColumns="false"
CssClass="books"
HeaderStyle-CssClass="booksHeader"
EmptyDataText="No matching results"
Runat="server">
<Columns>
<asp:TemplateField HeaderText="Book Cover Image">
<ItemTemplate>
<asp:Image
id="imgBook"
ImageUrl='<%#Amazon.FormatBookCover(Eval("SmallImage.Url"))%>'
AlternateText="Book cover image"
Runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Book Information">
<ItemTemplate>
<h2><%#Server.HtmlEncode(Eval("ItemAttributes.Title"))%></h2>
Authors:
<%#Amazon.FormatAuthor(Eval("ItemAttributes.Author"))%>
<br />Price:
<%#Amazon.FormatPrice(Eval("ItemAttributes.ListPrice.Amount"))%>
<br />Sales Rank:
<%#Eval("SalesRank")%>
<br />
<asp:HyperLink
id="lnkDetails"
NavigateUrl='<%#Eval("DetailPageURL")%>'
Text="View Details"
Tooltip=
'<%#Amazon.FormatDetailsTooltip(Eval("ItemAttributes.Title"))%>'
Runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource
id="srcBooks"
TypeName="Amazon"
SelectMethod="GetBooks"
Runat="server">
<SelectParameters>
<asp:ControlParameter
Name="CategoryId"
ControlId="menuCategories"
DefaultValue="1" />
</SelectParameters>
</asp:ObjectDataSource>
</div>
</asp:Content>
このページでは、Menu コントロールと GridView コントロールの 2 つの ASP.NET コントロールを使用して、書籍リストを表示します。Menu コントロールは書籍のカテゴリの一覧を表示するために使用し、GridView コントロールは書籍の一覧を表示するために使用します。
GridView コントロールは ObjectDataSource コントロールにバインドされています。一方、ObjectDataSource コントロールは、Amazon クラスの GetBooks() メソッドを呼び出して、書籍の一覧を取得します。
既定のページの XHTML 機能
XHTML ページを構築するときの目標の 1 つは、ドキュメントの構造をプレゼンテーションから切り離すことです。この目標を達成するために、Default.aspx ページのどの ASP.NET コントロールでも、書式設定用のプロパティが設定されていません。ページの書式設定は、ASP.NET テーマを通じて関連付けられる外部スタイル シートにカプセル化されています。
ASP.NET 2.0 テーマを使用すると、Web 標準に容易に従うことができます。これにより、すべてのプレゼンテーション用コンテンツをページから切り離すことができるからです。サンプル サイトには、単一のスタイル シートを含む SiteTheme という名前のテーマが含まれています。このテーマは、Web.Config ファイル内の次の構成設定を使用して、すべてのページに自動的に関連付けられます。
<pages
styleSheetTheme="SiteTheme"
masterPageFile="SiteMaster.master" />
ページ レイアウトの作成に HTML テーブルが使用されていないことに注目してください。XHTML 標準もアクセシビリティ標準も、ページ レイアウトにテーブルを使用することを禁止しているわけではありません。ただしどちらの標準も、ページ レイアウトにテーブルを使用しないよう推奨しています。このサンプル サイトでは、ページ レイアウトは外部スタイル シートによってすべて決定されます。ページ自体は、2 つの <div> 要素によって 2 つの列に分割されます。外部スタイル シートには、2 つの <div> 要素の位置を設定するルールが含まれています。
最後にサンプル サイトでは、ページを提供する際に、コンテンツ ネゴシエーションを使用します。application/xhtml+xml という MIME の種類を理解するブラウザを使用して Web サイトのページを要求すると、ページは application/xhtml+xml として提供されます。それ以外の場合は、text/html として提供されます。
コンテンツ ネゴシエーションは、Global.asax ファイル内の次のイベント ハンドラによって行われます。
Sub Application_PreSendRequestHeaders(ByVal s As Object, _
ByVal e As EventArgs)
If Array.IndexOf(Request.AcceptTypes, _
"application/xhtml+xml") > -1 Then
Response.ContentType = "application/xhtml+xml"
End If
End Sub
既定のページのアクセシビリティ機能
WCAG および第 508 条のアクセシビリティ ガイドラインでは、クライアント側 JavaScript と同等のテキストを提供できない場合に、クライアント側 JavaScript を使用することを禁止しています。このガイドラインを満たすため、Default.aspx ページはクライアント側 JavaScript に依存していません。このページは、ブラウザで JavaScript をオフにしても機能します。
この要件を満たすには、メニューを実装するときに追加の作業を行う必要があります。既定では、ASP.NET Menu コントロールはクライアントのクリック イベントを処理するために、各メニュー項目用の JavaScript を表示します。ただし、メニュー項目が NavigateUrl プロパティと共に指定されている場合、そのメニュー項目は JavaScript を使用しません。
サンプル サイトでは、各メニュー項目は Default.aspx ページを指す NavigateUrl プロパティと共に指定されています。メニュー項目をクリックすると、Default.aspx ページが再度読み込まれます。Page_Load イベント ハンドラを使用してクリックされたメニュー項目を検出し、このサブルーチンが現在のメニュー選択内容でメニューを更新します。
Menu コントロールを使用することの利点は、Menu コントロールがナビゲーション リンクのスキップを自動的に生成するということです。Default.aspx ページの各要素を Tab キーで移動し、ブラウザのステータス バーを見ると、メニューのコンテンツをスキップする隠れたリンクがあることがわかります。Menu コントロールを使用すると、繰り返し実行されるナビゲーション リンクをスキップする方法を提供するよう求める WCAG および第 508 条のガイドラインを自動的に満たすことができます。
検索ページ
検索ページには、Web サイトのユーザーが書籍の著者、書籍のタイトル、書籍のキーワード、または複雑なクエリを指定することによって書籍を検索できるフォームが含まれています (図 15 を参照)。クエリの結果は GridView コントロールに表示されます。Search.aspx ページをリスト 12 に示します。
図 15. 検索ページ (拡大するには画像をクリックしてください)
リスト 12. Search.aspx
<%@ Page Language="VB" MasterPageFile="~/SiteMaster.master"
Title="Search Books" %>
<script runat="server">
Protected Sub btnQuickSearch_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
txtPowerSearch.Text = String.Empty
End Sub
Protected Sub btnPowerSearch_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
txtAuthor.Text = String.Empty
txtTitle.Text = String.Empty
txtKeywords.Text = String.Empty
End Sub
</script>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentBody"
Runat="Server">
<h1>Search Books</h1>
<hr />
<div id="leftColumn">
<fieldset class="quickSearch">
<legend>Quick Search</legend>
<asp:Label
Text="Author:"
AssociatedControlID="txtAuthor"
AccessKey="a"
Runat="server" />
<asp:TextBox
id="txtAuthor"
ToolTip="Search by author"
Runat="server" />
<span class="accessKey">access key is a</span>
<br />
<asp:Label
Text="Title:"
AssociatedControlID="txtTitle"
AccessKey="t"
Runat="server" />
<asp:TextBox
id="txtTitle"
ToolTip="Search by title"
Runat="server" />
<span class="accessKey">access key is t</span>
<br />
<asp:Label
Text="Keywords:"
AssociatedControlID="txtKeywords"
AccessKey="k"
Runat="server" />
<asp:TextBox
id="txtKeywords"
ToolTip="Search by keywords"
Runat="server" />
<span class="accessKey">access key is k</span>
<br />
<asp:Button
id="btnQuickSearch"
Text="Quick Search Now"
ToolTip="Peform quick search"
AccessKey="s"
Runat="server" OnClick="btnQuickSearch_Click" />
<span class="accessKey">access key is s</span>
</fieldset>
<br />
<fieldset class="powerSearch">
<legend>Power Search</legend>
<asp:Label
Text="Query:"
AssociatedControlID="txtPowerSearch"
AccessKey="q"
Runat="server" />
<asp:TextBox
id="txtPowerSearch"
ToolTip="Power search query text"
TextMode="MultiLine"
Columns="20"
Rows="3"
Runat="server" />
<span class="accessKey">access key is q</span>
<br />
<asp:Button
id="btnPowerSearch"
Text="Power Search Now"
ToolTip="Perform power search"
AccessKey="p"
Runat="server" OnClick="btnPowerSearch_Click" />
<span class="accessKey">access key is p</span>
</fieldset>
</div>
<div id="middleColumn">
<asp:GridView
id="grdBooks"
DataSourceID="srcBooks"
AutoGenerateColumns="false"
CssClass="books"
HeaderStyle-CssClass="booksHeader"
EmptyDataText="No matching results"
Runat="server">
<Columns>
<asp:TemplateField HeaderText="Book Cover Image">
<ItemTemplate>
<asp:Image
id="imgBook"
ImageUrl=
'<%#Amazon.FormatBookCover(Eval("SmallImage.Url"))%>'
AlternateText="Book cover image"
Runat="server" />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Book Information">
<ItemTemplate>
<h2><%#Server.HtmlEncode(Eval("ItemAttributes.Title"))%></h2>
Authors:
<%#Amazon.FormatAuthor(Eval("ItemAttributes.Author"))%>
<br />Price:
<%#Amazon.FormatPrice(
Eval("ItemAttributes.ListPrice.Amount"))%>
<br />Sales Rank:
<%#Eval("SalesRank")%>
<br />
<asp:HyperLink
id="lnkDetails"
NavigateUrl='<%#Eval("DetailPageURL")%>'
Text="View Details"
Tooltip=
'<%#Amazon.FormatDetailsTooltip(Eval("ItemAttributes.Title"))%>'
Runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource
id="srcBooks"
TypeName="Amazon"
SelectMethod="SearchBooksFromAmazon"
Runat="server">
<SelectParameters>
<asp:ControlParameter
Name="Author"
ControlId="txtAuthor"
ConvertEmptyStringToNull="true" />
<asp:ControlParameter
Name="Title"
ControlId="txtTitle"
ConvertEmptyStringToNull="true" />
<asp:ControlParameter
Name="Keywords"
ControlId="txtKeywords"
ConvertEmptyStringToNull="true" />
<asp:ControlParameter
Name="PowerSearch"
ControlId="txtPowerSearch"
ConvertEmptyStringToNull="true" />
</SelectParameters>
</asp:ObjectDataSource>
</div>
</asp:Content>
検索ページの XHTML 機能
既定のページと同様、検索ページにも、プレゼンテーション用の要素や属性は含まれていません。検索ページのスタイルとレイアウトは、ASP.NET テーマを通じてページに関連付けられる外部スタイル シートにすべてカプセル化されています。
さらに既定ページと同様に、検索ページでもコンテンツ ネゴシエーションが使用されます。ユーザーが application/xhtml+xml という MIME の種類を認識するブラウザで検索ページを要求すると、ページはこの MIME の種類で提供されます。それ以外の場合、text/html として提供されます。
検索ページのアクセシビリティ機能
検索ページにはフォームが含まれています。正確には、このページには 2 つのサブフォームに分割された 1 つのフォームが含まれています。このフォームには、Quick Search フォームと Power Search フォームが含まれています。
このフォームが HTML <fieldset> タグによってサブフォームに分割されていることに注目してください。<fieldset> タグを使用すると、論理的に関連するフォーム要素をグループ化することができます。アクセシビリティ ガイドラインでは、複雑なフォームを扱う際に <fieldset> タグを使用するよう求めています (WCAG 12.3 を参照)。
さらに、各フォーム フィールドにそのラベルが明示的に関連付けられていることに注目してください。各 ASP.NET コントロールには、対応するフォーム フィールドを指す AssociatedControlID プロパティが含まれています。このようなラベルとフィールドの明示的な関連付けは、スクリーン リーダーを使用しているユーザーが特定のフォーム フィールドの目的を判断するのに役立ちます。
最後に、各 Label コントロールにアクセス キーが割り当てられていることに注目してください。これらのアクセス キーを使用すると、マウスを使用せずにフォーム フィールド間を簡単に移動することができます。たとえば、Alt キーを押しながら A キーを押すと、著者の名前を入力することができます。Alt キーを押しながら S キーを押すと、Quick Search フォームが送信され、結果が GridView に表示されます。つまり、マウスに触れることなく、検索を簡単に実行することができます。
Alt キーを押すと、アクセス キーが自動的に表示されます (図 16 を参照)。この機能は、JavaScript によって実現されています。各フォーム フィールドの後ろに <span> タグがあることに注目してください。たとえば、Title 検索フィールドは次のコードで実装されています。
<asp:Label
Text="Title:"
AssociatedControlID="txtTitle"
AccessKey="t"
Runat="server" />
<asp:TextBox
id="txtTitle"
ToolTip="Search by title"
Runat="server" />
<span class="accessKey">access key is t</span>
Alt キーを押すと、クライアント側 JavaScript が実行され、<span> タグのコンテンツが表示されます。
図 16. 検索フォームのアクセス キー
アクセシビリティ ガイドラインでは、ページは JavaScript とスタイル シートがオフになっているときでも機能しなければならないとされているため (WCAG ガイドライン 6)、この機能について心配になる人もいるかもしれません。さいわい、このページは JavaScript とスタイル シートの両方を無効にしたときでも機能します。その場合、<span> タグの内容は隠されず、アクセス キーが常に表示されます (図 17 を参照)。
図 17. 機能が適切に抑えられた検索フォーム
マスタ ページ
サンプル Web サイトでは、SiteMaster.master という名前の ASP.NET 2.0 マスタ ページを内部的に使用しています。マスタ ページを使用すると、Web サイトの複数のページで同じコンテンツを含んだり同じレイアウトを作成することができます。このマスタ ページは、Web.Config ファイルの次の構成設定を通じて、サンプル Web サイトのすべてのページに関連付けられています。
<pages
styleSheetTheme="SiteTheme"
masterPageFile="SiteMaster.master" />
このマスタ ページの内容をリスト 13 に示します。
リスト 13. SiteMaster.master
<%@ Master Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<script runat="server">
''' <summary>
''' 表示するスタイル シートを選択します
''' </summary>
Sub Page_Load()
If Not IsNothing(Request("large")) Then
Profile.AccessibleStyleSheet = True
End If
If Not IsNothing(Request("normal")) Then
Profile.AccessibleStyleSheet = False
End If
If Profile.AccessibleStyleSheet Then
lnkAccessibleStyle.Visible = True
lnkStyle.Text = "Normal Text Version"
lnkStyle.NavigateUrl = Request.Path & "?normal=1"
Else
lnkAccessibleStyle.Visible = False
lnkStyle.Text = "Large Text Version"
lnkStyle.NavigateUrl = Request.Path & "?large=1"
End If
End Sub
</script>
<html >
<head runat="server">
<title>Super Super Books</title>
<script type="text/javascript"
src="Scripts/AccessKeys.js"></script>
<link id="lnkAccessibleStyle"
type="text/css"
rel="Stylesheet"
href="~/Styles/Accessible.css"
runat="server" />
</head>
<body>
<form id="form1" runat="server">
<div id="content">
<img id="SiteLogo" src="Images/SiteLogo.png"
alt="SSB Web site logo image" />
<div id="banner">
<asp:HyperLink
id="lnkStyle"
ToolTip="Modify the size of all text in this Web site"
Runat="server" />
<br />
<asp:Menu
id="MenuSite"
ToolTip="Web site navigation menu"
CssClass="menuSite"
Orientation="Horizontal"
StaticTopSeparatorImageUrl="Images/bullet.gif"
Runat="server">
<Items>
<asp:MenuItem
Text="Home"
ToolTip="Navigate to home page"
NavigateUrl="~/Default.aspx" />
<asp:MenuItem
Text="Search"
Tooltip="Navigate to search page"
NavigateUrl="~/search.aspx" />
</Items>
</asp:Menu>
</div>
<hr />
<asp:contentplaceholder id="ContentBody" runat="server" />
<hr />
<a href="http://validator.w3.org/check?uri=referer"
title="Explanation of XHTML 1.0 Conformance">
<img
src="http://www.w3.org/Icons/valid-xhtml10"
alt="Valid XHTML 1.0 icon" class="icon"/></a>
<a href="http://jigsaw.w3.org/css-validator/"
title="Explanation of CSS Conformance">
<img
src="http://jigsaw.w3.org/css-validator/images/vcss"
alt="Valid CSS icon" class="icon" /></a>
<a href="http://www.w3.org/WAI/WCAG1AA-Conformance"
title="Explanation of Level Double-A Conformance">
<img height="32" width="88"
src="http://www.w3.org/WAI/wcag1AA"
alt="Level Double-A conformance icon,
W3C-WAI Web Content Accessibility Guidelines 1.0"
class="icon" /></a>
</div>
</form>
</body>
</html>
マスタ ページの XHTML 機能
マスタ ページを利用することにより、Web サイトのすべてのページの適切な DOCTYPE を簡単に指定することができます。SiteMaster.master ページには、XHTML 1.0 Strict DOCTYPE が含まれています。マスタ ページで DOCTYPE を指定することの利点は、将来 Web サイトのすべてのページの DOCTYPE を変更することが必要になった場合に、1 つの場所で変更するだけで済むということです。たとえば、近い将来、XHTML 1.1 Web サイトに移行し、その変更を反映するように DOCYTYPE を変更することが必要になる可能性があります。
マスタ ページには、一連の準拠ロゴも含まれています。これらのロゴは、サンプル Web サイトのすべてのページのフッターに表示されます。準拠ロゴは、Web サイトが XHTML、CSS、および WCAG 1.0 の各 Web 標準に準拠していることを示します (図18 を参照)。
図 18. 準拠ロゴ
マスタ ページのアクセシビリティ機能
サンプル Web サイトの各ページの先頭には、ページの表示に使用するスタイル シートを切り替えるために使用できるリンクが含まれています。各ページは、2 つのバージョン、つまり Normal Text バージョンと Large Text バージョンのいずれかで表示できます。Large Text バージョンを選択した場合、Web サイト内のすべてのテキストのサイズは、より読みやすいサイズに拡大されます (図 19 を参照)。
図 19. 大きなテキスト サイズ
ユーザーはこの選択を 1 回だけ行う必要があります。ユーザーが Web サイトの Large Text バージョンを選択した場合、その設定は自動的に記録され、そのユーザーが Web サイトに戻るたびに使用されます。この機能は、ASP.NET 2.0 フレームワークのもう 1 つの新機能であるプロファイルを利用して実装されています。プロファイルを使用すると、Web サイトに次回以降訪れた場合にもユーザーの設定を使用することができます。
このプロファイルは Web.Config ファイルで定義されています。
<anonymousIdentification enabled="true"/>
<profile>
<properties>
<add
name="AccessibleStyleSheet"
type="Boolean"
defaultValue="false" />
</properties>
</profile>
このプロファイルでは、AccessibleStyleSheet という名前のブール型プロパティを定義しています。このプロパティを Web.Config ファイルで定義すると、任意の ASP.NET ページでこのプロパティを読み取ったり、設定したりすることができます。それには、Page クラスによって公開される Profile プロパティを使用します。たとえば、AccessibleStyleSheet プロパティの値を True に設定し、Web サイトの Large Text バージョンを表示するには、次のように記述します。
Profile.AccessibleStyleSheet = true
マスタ ページには、Web サイトの Normal Text バージョンまたは Large Text バージョンを選択するためのロジックがすべて含まれています。HyperLink コントロールを使用し、Web サイトの訪問者がこの選択を行えるようにします。HyperLink をクリックすると、Page_Load イベント ハンドラによってユーザーの選択内容が検出され、AccessibleStyleSheet
Profile プロパティが設定されます。
HyperLink コントロールの代わりに LinkButton コントロールを使用できれば、コードはもっと単純になります。ただし、繰り返しになりますが、LinkButton コントロールはクライアント側 JavaScript に依存するため、アクセシビリティ ガイドラインでは使用が禁じられています。
Large Text バージョンが選択されると、追加のスタイル シートへの参照がページに追加されます。このスタイル シートには単一のルールが含まれています。
body
{
font-size: x-large;
}
このルールは、本文のフォント サイズの値を x-large に設定します。プライマリ スタイル シート (SiteTheme フォルダに含まれています) で指定されているすべてのフォントは相対サイズを使用しているため、本文要素のフォント サイズを変更すると、Web サイトのすべての要素のフォント サイズが自動的に拡大されます。
まとめ
Web 標準はすばらしいものです。Web 標準に従うことにより、最小限の作業で、可能な限り幅広いユーザーにとってアクセスしやすい Web サイトにすることができます。Web サイトはより多くのブラウザと互換性を持つようになり、将来も引き続き機能する可能性が高くなります。
ASP.NET 2.0 フレームワークは、Web 標準を満たす Web サイトを簡単に構築できるようにするために設計されました。このフレームワークを使用すると、XHTML Web サイトを簡単に構築できます。ASP.NET 2.0 フレームワークでは、すべての ASP.NET コントロールが既定で XHTML の要素と属性を表示します。さらに、Visual Studio 2005 および Visual Web Developer を使用すると、ページの構築時に、ページを XHTML 標準に照らして自動的に検証することができます。
ASP.NET 2.0 フレームワークを使用すると、障害を持つ人がアクセスしやすい Web サイトをより簡単に構築することもできます。ASP.NET 2.0 フレームワークのコントロールには、アクセシビリティを考慮して設計された新しいプロパティが多数含まれています。たとえば、イメージを表示するすべての ASP.NET コントロールでは、イメージの代替テキストを表示することができます。さらに、すべての新しいナビゲーション コントロールには、障害を持つ人が Web サイト内をより簡単に移動できるようにする、ナビゲーション リンクのスキップが含まれています。
執筆者紹介
Stephen Walther は、ASP.NET に関するベストセラー書籍である『ASP.NET Unleashed』 (英語) の著者です。また、マイクロソフトが作成した ASP.NET のサンプル アプリケーションである ASP.NET Community Starter Kit の設計者であり開発リーダーでした。氏の経営する Superexpert (英語) 社を通じて、NASA やマイクロソフトをはじめとする全米各地の企業を対象に ASP.NET のトレーニングを実施しています。