本文将向您展示您现在可以执行的一些简单操作,以使您的JavaScript更加易于使用。 这不是尖端技术,而是我们多年来一直在做的事情。 这篇文章扩展了我们的介绍性文章“ JavaScript Accessibility 101” 。
三大核心原则
JavaScript的可访问性归结为三个核心原则:
- 所有JavaScript功能都必须采用可解释为文本的形式。
- 键盘必须访问所有JavaScript功能。
- JavaScript中基于时间的活动应该是用户可控制的,除非那样会改变其含义。
让我们采用这些原则中的每一个,然后看看我们可以做些实际的事情来实现它。
文字解释
该原理的重点是将信息表示为具有有意义结构的文本,可以通过编程确定该结构 。 实体元素语义应该是所有交互内容的基础,但是对于辅助技术尤其如此。
有视力的用户并不总是会受到语义的影响–菜单是菜单,因为它看起来像菜单,无论它是基于列表还是<div>
构建的。 但是盲人用户只能理解其屏幕阅读器可以解释的内容–菜单是菜单,因为它是结构化链接的层次结构。 例如,如果我们正在构建一个选项卡框,它可能类似于以下示例。 注意如何使用各种语义标记。
<div class="box">
<section class="panel">
<h3 class="tab" tabindex="0">Section 1</h3>
<p class="content">This is the content for Section 1</p>
</section>
<section class="panel">
<h3 class="tab" tabindex="0">Section 2</h3>
<p class="content">This is the content for Section 2</p>
</section>
</div>
使信息以编程方式可访问也很重要。 这意味着使用标准DOM函数向页面添加新内容,而不是使用document.write()
或innerHTML
。
innerHTML
无疑是方便的,并且通常比逐节点添加新内容要快得多。 innerHTML
的问题在于浏览器经常更改标记,因此生成的DOM与您指定的DOM不同。 在极少数情况下,以这种方式添加的内容根本不会出现在DOM中。
要解决此问题,请通过中间未添加的元素添加HTML,如下所示。 要使用此功能,请向其传递HTML字符串和目标元素引用。 该函数创建一个虚拟DOM,然后将其节点附加到目标。
function appendHTML(target, html)
{
var fragment = document.createElement('div');
fragment.innerHTML = html;
while(fragment.hasChildNodes())
{
target.appendChild(fragment.firstChild);
}
return target;
}
键盘辅助功能
使键盘可以访问交互式内容时,请坚持使用一组核心键: Tab , Enter ,四个箭头键和Escape 。 这些键应按原样使用,即不必按住Shift键或其他修饰键。 如果确实需要使用其他键或修饰键击,则应向用户提供说明。 但是,仍然最好避免使用Alt组合,因为它们用于本机菜单快捷方式。
大多数键还具有默认的浏览器操作,有时必须阻止默认键,以防止行为冲突。 例如,在下拉菜单中使用向上键和向下键时,您不希望它们同时滚动页面。 执行此操作的标准方法是使用preventDefault()
,如以下示例所示,该示例来自菜单脚本:
menu.addEventListener('keydown', function(e)
{
if(/^(3[789]|40)$/.test(e.keyCode.toString()))
{
switch(e.keyCode)
{
case 37:
//... handle left-arrow
break;
case 38:
//... handle up-arrow
break;
case 39:
//... handle right-arrow
break;
case 40:
//... handle down-arrow
break;
}
e.preventDefault();
}
}, false);
如果keyCode
与箭头键匹配,则该函数将视情况处理该键,然后调用preventDefault()
。 如果按下任何其他键,则事件将被忽略,并保持默认行为。 注意不要挡住Tab键,否则将引起用户关注!
请注意,上面的示例使用keydown
而不是keypress
,因为大多数浏览器不会(也不应该)触发控制键的keypress
事件。 如果按住键,则keydown
事件也会连续触发,因此在某些情况下,您可能更喜欢使用keyup
,这种情况不会重复出现,但不能阻止默认操作。
另一个重要的考虑因素是确保我们保持逻辑的内容顺序 。 例如,当使用丰富的工具提示时,必须将它们直接插入触发它们的元素之后,以便您可以使用Tab键切换至它们,以便屏幕阅读器接下来可以阅读它们。 例如,一个简单的工具提示可能看起来像这样:
您可以看到主要工具提示文本的周围带有方括号,而底部的链接具有方括号和定界管道符号。 文本也包裹在<em>
以增加语义重点。 禁用CSS时,内容如下所示:
这是该示例的HTML:
<blockquote>
<p>
Assistive technologies can derive information
from their attributes and text; for example, a
dynamic menu would be made using links organised
into nested lists, in which the menu levels are
denoted by the hierarchy, and by the use of
<span id="context">
<a href="http://www.maxdesign.com.au/2006/01/17/about-structural-labels/"
title="descriptive headings used to indicate the main components of a web page, such as global site navigation, local navigation and footer information">
structural labels
</a>
</span>
around each top-level link.
</p>
</blockquote>
链接周围的<span>
提供了插入目标,因此可以在链接后直接添加工具提示。 它还为工具提示的绝对位置提供了相对上下文:
#context
{
position:relative;
}
#context > span.tooltip
{
position:absolute;
bottom:1.7em;
right:0;
}
#context > span.tooltip
{
width:18em;
padding:6px 8px;
white-space:normal;
border:1px solid #555;
font:normal normal normal 0.85em/1.4 arial,sans-serif;
text-align:right;
background:#ffd;
box-shadow:1px 2px 3px -1px rgba(0,0,0,0.5);
}
#context > span.tooltip > em
{
display:block;
padding:4px 4px 8px 4px;
text-align:left;
font-style:normal;
}
这是该示例的JavaScript:
var
infotext,
tooltip = null,
context = document.getElementById('context'),
trigger = context.getElementsByTagName('a').item(0);
trigger.addEventListener('click', function(e)
{
if(tooltip === null)
{
infotext = trigger.getAttribute('title');
trigger.removeAttribute('title');
tooltip = document.createElement('span');
tooltip.className = 'tooltip';
var info = tooltip.appendChild(document.createElement('em'));
info.appendChild(document.createTextNode(' (' + infotext + ') '));
tooltip.appendChild(document.createTextNode('[ '));
var more = tooltip.appendChild(document.createElement('a'));
more.setAttribute('href', trigger.getAttribute('href'));
more.appendChild(document.createTextNode('reference'));
tooltip.appendChild(document.createTextNode(' | '));
var google = tooltip.appendChild(document.createElement('a'));
google.setAttribute('href', 'http://www.google.com/search?q=Structural+Labels');
google.appendChild(document.createTextNode('search'));
tooltip.appendChild(document.createTextNode(' ]'));
tooltip = context.appendChild(tooltip);
}
else
{
trigger.setAttribute('title', infotext);
context.removeChild(tooltip);
tooltip = null;
}
e.preventDefault();
}, false);
在这种情况下,将使用preventDefault()
来阻止单击链接触发(或删除)工具提示时的链接。 这就是为什么工具提示还包括原始参考链接的原因,因此与静态标记相比,内容不会丢失。
控制基于时间的活动
JavaScript使用的最常见的基于时间的活动是内容动画。 重要的是要确保动画具有暂停按钮,并且在理想情况下,这是查看完整显示内容的方法。 例如,可以这样构建滚动的新闻自动收录器:
<div id="ticker">
<ol>
<li><a href="...">This is the first news item</a></li>
<li><a href="...">This is the second news item</a></li>
</ol>
</div>
<div id="buttons">
<button id="pause-button" type="button">Pause</button>
<button id="expand-button" type="button">Expand</button>
</div>
标记是有序列表,因为新闻项目通常是按时间排序的,最新的标题在顶部。 该示例的CSS将如下所示:
#buttons
{
display:none;
}
#buttons.script-enabled
{
display:block;
}
#ticker.script-enabled,
#ticker.script-enabled > ol
{
position:relative;
}
#ticker
{
white-space:nowrap;
overflow:hidden;
margin:5px 0 0 0;
padding:5px 10px;
border:2px solid #555;
background:#f2f2f2;
}
#ticker.script-enabled > ol,
#ticker.script-enabled > ol > li
{
margin:0;
padding:0;
list-style-type:none;
}
#ticker.script-enabled > ol > li
{
display:inline-block;
margin-right:20px;
}
#ticker.script-enabled.expanded,
#ticker.script-enabled.expanded > ol
{
position:static;
}
#ticker.script-enabled.expanded
{
white-space:normal;
overflow:hidden;
}
尽管以下JavaScript将所有内容整合在一起:
var
container = document.getElementById('ticker'),
list = container.getElementsByTagName('ol').item(0),
buttons = document.getElementById('buttons'),
pauser = document.getElementById('pause-button'),
expander = document.getElementById('expand-button');
buttons.className = 'script-enabled';
container.className = 'script-enabled';
var scrollwidth = 0;
var items = list.getElementsByTagName('li');
for(var i = 0; i < items.length; i ++)
{
scrollwidth += items[i].offsetWidth + 20;
}
var scrollstart = container.offsetWidth;
list.style.left = scrollstart + 'px';
var
timer = null,
moving = false,
scrolloffset = scrollstart;
function pause()
{
moving = false;
window.clearInterval(timer);
timer = null;
}
function resume()
{
moving = true;
timer = window.setInterval(function()
{
scrolloffset -= 5;
if(scrolloffset < (0 - scrollwidth))
{
scrolloffset = scrollstart;
}
list.style.left = scrolloffset + 'px';
}, 100);
}
pauser.addEventListener('click', function()
{
if(moving)
{
pause();
pauser.firstChild.nodeValue = 'Resume';
}
else
{
resume();
pauser.firstChild.nodeValue = 'Pause';
}
}, false);
expander.addEventListener('click', function()
{
pause();
container.className = 'expanded';
pauser.parentNode.removeChild(pauser);
expander.parentNode.removeChild(expander);
}, false);
resume();
需要注意的重要一点是, buttons
容器默认情况下是隐藏的,仅在添加了script-enabled
类时才显示。 这样一来,当在不使用JavaScript的情况下查看页面时,不会为用户留下无法执行任何操作的按钮。
同样,仅当添加了script-enabled
类时,适用于overflow
的规则和将列表转换为水平滚动条的其他属性才生效。 这是必要的,因为默认情况下,这些样式会遮挡大多数内容,因此,除非脚本存在,否则我们需要确保不会发生这种情况。
“ Pause
按钮将停止滚动,因此您可以在自己的时间内阅读每个项目,然后更改为“ Resume
以便再次启动它。 Expand
按钮也将停止滚动,但是会添加一个expanded
类,该类将覆盖overflow
和其他布局样式。 这使内容变成了静态链接列表。
结论
这是实际的JavaScript可访问性的旋风之旅! 但是,如果有一件我想让您拿走的东西,那就是JavaScript的可访问性并不难 。 它只需要注意三个核心原则。 下次,我将继续使用更高级的技术,例如漫游tabindex
,拖放和可访问的Ajax!
From: https://www.sitepoint.com/practical-javascript-accessibility/