aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/accessibility/keyboard-navigable_javascript_widgets/index.html
blob: 303570acbe0877e125ed65595aff208e00ca3603 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
---
title: 键盘导航的 JavaScript 组件
slug: Web/Accessibility/Keyboard-navigable_JavaScript_widgets
translation_of: Web/Accessibility/Keyboard-navigable_JavaScript_widgets
---
<p>键盘导航 JavaScript 组件</p>

<h3 id="概况">概况</h3>

<p>网页应用经常使用 JavaScript 来模拟桌面组件,比如菜单、树状视图、富文本以及标签面板等。网页中这些组件通常是由 {{ HTMLElement("div") }}和 {{ HTMLElement("span") }} 等元素组合而成,并不是原生的,也不会提供对应的桌面组件所拥有的键盘功能。本文档主要描述能让  JavaScript 组件通过键盘 access 的技术。</p>

<h3 id="利用_tabindex">利用 tabindex</h3>

<p>最初 tabindex 出现是做为 HTML4的一部分,提供一种手段让开发人员定义元素的顺序,可以让用户通过键盘按此顺序来获取焦点。最新在 HTML5 草案里面针对 tabindex 的具体行为表现做了改动。所有主流浏览器都已经实现了这个修改后的设计。</p>

<p>下面表格阐述在主流浏览器里面 <code>tabindex</code> 的表现:</p>

<table class="fullwidth-table" style="width: 75% !important;">
 <tbody>
  <tr>
   <th><code>tabindex</code> 属性</th>
   <th>可用鼠标或者 用 JavaScript 语句 <code>element.focus()获取焦点</code></th>
   <th>可使用Tab 键导航获取焦点</th>
  </tr>
  <tr>
   <td>
    <p>not present</p>

    <p>(未设置)</p>
   </td>
   <td>遵循所在平台针对此元素的默认行为 (yes for form controls, links, etc.).</td>
   <td>遵循所在平台针对此元素的默认行为</td>
  </tr>
  <tr>
   <td>Negative (i.e. <code>tabindex="-1"</code>)</td>
   <td>Yes</td>
   <td>No; 必须通过  <code><a class="external text" href="../../../../En/DOM/Element.focus" rel="nofollow" title="https://developer.mozilla.org/En/DOM/Element.focus">focus()</a></code> 来聚焦该元素。可以在方向键或者其他键的响应里面做。</td>
  </tr>
  <tr>
   <td>Zero (i.e. <code>tabindex="0"</code>)</td>
   <td>Yes</td>
   <td>tab顺序对应于该元素在文档中的位置</td>
  </tr>
  <tr>
   <td>Positive (e.g. <code>tabindex="33"</code>)</td>
   <td>Yes</td>
   <td><code>tabindex</code> 值决定了元素的tab顺序:通常这些带有正数值的tabindex元素会排在tabindex="0"和原生可以tabable的元素前面;特别指出的是值越小越排在前面(举个例子,<code>tabindex="7"</code> 会排在<code>tabindex="11"前面</code>)</td>
  </tr>
 </tbody>
</table>

<h4 id="简单控件_Simple_Controls"><span style="color: #000000;"><span class="toctext">简单控件 Simple Controls</span></span></h4>

<p>可以在 {{ HTMLElement("div") }} or {{ HTMLElement("span") }} 元素上设置 tabindex="0" 使得此简单控件变得可 tab 导航.这里有个 span-based checkbox 使用这个方法的例子。</p>

<p><em>Example 1: A simple image-based checkbox widget using tabindex to allow keyboard access</em></p>

<pre class="brush: html"><code>&lt;!-- 没有tabindex 属性的话, 这些 &lt;span&gt; 元素不会被键盘focus中 --&gt;
&lt;div&gt;
    &lt;span role="checkbox" aria-checked="true" tabindex="0"&gt;
        &lt;img src="checked.gif" role="presentation" alt="" /&gt;
        Include decorative fruit basket
    &lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;span role="checkbox" aria-checked="true" tabindex="0"&gt;
        &lt;img src="checked.gif" role="presentation" alt="" /&gt;
        Include singing telegram
    &lt;/span&gt;
&lt;/div&gt;
&lt;div&gt;
    &lt;span role="checkbox" aria-checked="false" tabindex="0"&gt;
        &lt;img src="unchecked.gif" role="presentation" alt="" /&gt;
        Require payment before delivery
    &lt;/span&gt;
&lt;/div&gt;</code>
</pre>

<h4 id="组合控件_Grouping_controls"><span style="color: #000000;"><span class="toctext">组合控件 Grouping controls</span></span></h4>

<p>针对像菜单、标签面板、树等这些组合控件,父元素应该在 tab 序列里面(tabindex="0") 而每个后代选择/标签/单元/行应该从 tab 序列里面移除(tabindex="-1")。用户应该可通过方向键来操控这些后代元素。(关于组件一般预期的键盘支持,可以查看 <a class="external text" href="http://access.aol.com/dhtml-style-guide-working-group/" rel="nofollow" title="http://access.aol.com/dhtml-style-guide-working-group/">DHTML Style Guide</a>.)</p>

<p>下面这个组合菜单的例子展示了这种技术的使用。 一旦键盘 focus 中容器 ul 元素,Javascript  开发人员需要在键盘事件里针对方向键的响应里管理里面元素的 focus 顺序。在组件里管理 focus 的办法,看下面这个<span style="line-height: 1.5;"> "在组合控件里管理 focus " 例子.</span></p>

<p><em>范例 2: 一个使用 tabindex 控制键盘 access 的菜单控件</em></p>

<pre class="brush: html"><code>&lt;ul id="mb1" tabindex="0"&gt;
  &lt;li id="mb1_menu1" tabindex="-1"&gt; Font
    &lt;ul id="fontMenu" title="Font" tabindex="-1"&gt;
      &lt;li id="sans-serif" tabindex="-1"&gt;Sans-serif&lt;/li&gt;
      &lt;li id="serif" tabindex="-1"&gt;Serif&lt;/li&gt;
      &lt;li id="monospace" tabindex="-1"&gt;Monospace&lt;/li&gt;
      &lt;li id="fantasy" tabindex="-1"&gt;Fantasy&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li id="mb1_menu2" tabindex="-1"&gt; Style
    &lt;ul id="styleMenu" title="Style" tabindex="-1"&gt;
      &lt;li id="italic" tabindex="-1"&gt;Italics&lt;/li&gt;
      &lt;li id="bold" tabindex="-1"&gt;Bold&lt;/li&gt;
      &lt;li id="underline" tabindex="-1"&gt;Underlined&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li id="mb1_menu3" tabindex="-1"&gt; Justification
    &lt;ul id="justificationMenu" title="Justication" tabindex="-1"&gt;
      &lt;li id="left" tabindex="-1"&gt;Left&lt;/li&gt;
      &lt;li id="center" tabindex="-1"&gt;Centered&lt;/li&gt;
      &lt;li id="right" tabindex="-1"&gt;Right&lt;/li&gt;
      &lt;li id="justify" tabindex="-1"&gt;Justify&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;</code></pre>

<h4 id="失效控件"><span class="toctext">失效控件</span></h4>

<p>当一个自定义控件变成失效(disabled)状态时,通过设置 tabindex="-1" 把它从 tab 序列里面移除。注意在一个组合组件里面失效状态的子项(比如菜单控件里面某一个菜单项)应该在方向键的响应事件里面跳过它不让 focus 中。</p>

<h3 id="在组合控件里面管理_focus">在组合控件里面管理 focus</h3>

<p>当用户从一个组件 tab 离开之后 focus 回来,焦点应该回到离开之时正被 focus 中的元素上,比如某个树节点或者网格单元. 有两种办法完成这一点:</p>

<ol>
 <li><code><font face="Open Sans, sans-serif"><span style="line-height: 21px;">流动 </span></font>tabindex</code>: 通过编程移动 focus</li>
 <li><code>aria-activedescendent</code>: 管理一个“虚拟” focus</li>
</ol>

<h4 id="方法_1_流动_tabindex"><span style="color: #000000;"><span class="toctext">方法 1: 流动 tabindex</span> </span></h4>

<p>在被 focus 中元素上设置 <code>tabindex</code> 为 "0",这样可以保证在用户在 tab 离开又返回后仍然选中组合组件中之前被选中的那项。注意在更改 tabindex 为“0”同时需要把之前选中过的那项设置<code>tabindex="-1"</code>。这个方法包含在键盘事件里面通过程序移动焦点以及更改 <code>tabindex</code> 到当前焦点中的那项上。需要做以下几步:</p>

<p>在每个元素上绑定 key down 事件,当捕捉到控制移动到另外元素的方向键时:</p>

<ol>
 <li>通过编码把 focus 应用到新元素上,</li>
 <li>更改被 focus 中元素上的 tabindex 为“0”</li>
 <li>更改之前被 focus 中元素的 tabindex 为“-1”.</li>
</ol>

<p>这里有个 WAI-ARIA tree view 的例子是使用这种方案的。</p>

<h5 id="提示">提示</h5>

<h6 id="用_element.focus()_来设置焦点_focus">用 element.focus() 来设置焦点 focus</h6>

<p>不要使用 <code>createEvent()</code>, <code>initEvent()</code><code>dispatchEvent()</code> 来发送焦点事件到元素上。DOM的focus事件被认为仅是一种信息:当有事物被focus中的时候系统自动产生的,但是实际上没有用来设置焦点。用 <code>element.focus()</code> 来替代。</p>

<h6 id="用_onfocus_来追踪当前_focus">用 onfocus 来追踪当前 focus</h6>

<p>不要认为所有的焦点变化都来自于键盘和鼠标事件:像屏幕阅读器之类的辅助技术可以设置焦点到任意一个可focus 中的元素上。用 onfocus 和 onblur来追踪焦点。</p>

<p><code>onfocus</code><code>onblur</code> 现在可以在每个元素上使用。没有标准的 DOM 接口来获取当前文档的focus,所以如果你想追踪就需要保存追踪数据在 JavaScript 变量里。</p>

<h4 id="方法_2_aria-activedescendant">方法 2: aria-activedescendant</h4>

<p>这个办法包含绑定一个单独的事件句柄到容器窗口组件上,运用 <code>aria-activedescendent属性</code>来追踪一个 "虚拟" 焦点。(关于ARIA更多的信息, 查看 <a class="external text" href="../../../../An_Overview_of_Accessible_Web_Applications_and_Widgets" rel="nofollow" title="https://developer.mozilla.org/An_Overview_of_Accessible_Web_Applications_and_Widgets">overview of accessible web applications and widgets</a>.)</p>

<p><code>aria-activedescendant</code> 属性用来标识拥有虚拟焦点的后代元素的 ID。在窗口容器的事件句柄里面在键盘和鼠标事件响应更新 <span style="font-family: 'Courier New','Andale Mono',monospace; line-height: normal;">aria-activedescendant 值并且确保当前</span><span style="line-height: 1.5;">The event handler on the container must respond to key and mouse events by updating the value of </span><code style="font-size: 14px;">aria-activedescendant</code><span style="line-height: 1.5;"> and ensuring that the current item is styled appropriately (for example, with a border or background color). </span></p>

<h5 id="Tips">Tips</h5>

<h6 id="scrollIntoView">scrollIntoView</h6>

<p>Note that the use of this pattern requires the author to ensure that the current <em>focused</em> widget is scrolled into view. You should be able to use the {{ domxref("element.scrollIntoView()") }} function, but we recommend confirming this works for you in your target browsers using the <a class="external text" href="http://www.quirksmode.org/dom/tests/scrollintoview.html" rel="nofollow" title="http://www.quirksmode.org/dom/tests/scrollintoview.html">quirksmode test</a>.</p>

<h6 id="Issues">Issues</h6>

<ul>
 <li><a class="external text" href="http://www.quirksmode.org/dom/w3c_cssom.html" rel="nofollow" title="http://www.quirksmode.org/dom/w3c_cssom.html">quirksmode reports problems in Opera, and Konqueror</a></li>
 <li><a class="external text" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450405" rel="nofollow" title="https://bugzilla.mozilla.org/show_bug.cgi?id=450405">Firefox 3.0.1 introduced bug</a></li>
</ul>

<h3 id="一般准则">一般准则</h3>

<h4 id="使用_onkeydown_来捕获的关键事件,而不是_onkeypress_事件">使用 onkeydown 来捕获的关键事件,而不是 onkeypress 事件</h4>

<p>IE 不会触发非字母数字键的 <code>keypress </code>事件。使用 <code>onkeydown</code> 替代。</p>

<h4 id="确保键盘和鼠标有相同的体验">确保键盘和鼠标有相同的体验</h4>

<p>为了确保不同输入设备的用户体验一致,键盘和鼠标事件处理程序在适当的情况下应该共享代码。例如,当用户使用方向键导航的时候,一些代码会更新相应元素的 <code>tabindex</code><font face="Courier New, Andale Mono, monospace"> </font>或风格,用鼠标点击也应该有同样的变化。</p>

<h4 id="确保键盘可以激活元素">确保键盘可以激活元素</h4>

<p>要确保键盘可以激活元素,所有鼠标事件的绑定也同时要绑定到键盘事件上。例如,为了确保回车键能激活某个元素,如果你使用了 <code>onclick="DoSomething()",</code> 你需要把 <code>doSomething()</code> 绑定到 key down 事件上:<code>onkeydown="return event.keyCode != 13 || doSomething();"</code>.</p>

<h4 id="不要使用_focus_来设置焦点的样式(如果要兼容IE7及更早版本)">不要使用 :focus 来设置焦点的样式(如果要兼容IE7及更早版本)</h4>

<p>IE 7 及更早版本不支持 <code>:focus</code> 伪选择器,不要用它来设置焦点的样式。替代方法是在 <code>onfocus </code>事件处理程序中设置样式,例如,添加一个 CSS 样式到 <code>class</code> 属性中。</p>

<h4 id="始终用程序为_tabindex-1_的项和元素设置焦点样式"><span style="font-size: 1.28571428571429rem;">始终</span>用程序为 tabindex="-1" 的项和元素设置焦点样式</h4>

<p>IE 不会自动为<code> tabindex="-1" </code>的元素绘制聚焦框。可以选择一种方法解决,比如通过 <code>this.style.backgroundColor="gray"</code> 改变选中项目的背景颜色,或通过 <code>this.style.border="1px dotted invert"</code> 添加虚线边框。如果使用虚线边框的,需要确保这些元素有隐含的一象素边框,这样,当聚焦的边框样式应用上去的时候,元素的大小才不会改变(边框会占用空间,而 IE 没有实现 CSS outline)。 </p>

<h4 id="阻止浏览器的按键事件处理">阻止浏览器的按键事件处理</h4>

<p>如果你用自己的插件处理按键事件,可以通过事件处理程序的返回值来阻止浏览器的缺省处理(例如,响应方向键的滚动)。如果你的事件处理程序返回 <code>false</code>,事件就不会冒泡到处理程序外面。</p>

<p>例如:</p>

<pre><code>&lt;span tabindex="-1" onkeydown="return handleKeyDown();"&gt;</code></pre>

<p>如果 <code>handleKeyDown()</code> 返回 <code>false</code>,将会结束事件处理,阻止浏览器继续处理按键行为。</p>

<h4 id="不要认为按键连发(repeat)有一致性">不要认为按键连发(repeat)有一致性</h4>

<p>非常不幸,<code>onkeydown 连发或不连发</code>取决于代码执行的浏览器和操作系统。</p>