您的位置:首页 > 运维架构 > 网站架构

CSS :focus伪类和JS focus事件提高网站键盘可访问性

2017-04-27 12:42 1006 查看

键盘访问网站的常用操作包括:

Tab键索引控件元素;

Enter键触发当前处于focus状态的点击行为;

上下键上下滚动网页;

Space空白键滚动一屏网页;

Home键返回顶部;

End键滚动到底部;

一般的操作行为是这样的,先Tab键按次序不断focus控件元素,包括
链接,按钮,输入框等表单元素或者focus设置了tabindex的普通元素
,处于focus状态元素,浏览器一般会通过虚框或者外发光的形式进行区分和提示,此时我们在按下Enter回车键,就相当于鼠标点击了这个元素,从而可以前往我们想去的目的地,或者执行我们想要的交互效果。

而focus状态元素的标记默认全部都是使用outline属性

我们只要平时注意HTML语义化,例如按钮不要使用,
等标签,不要重置outline,基本上键盘可访问性就已经及格了。

一、label标签和表单元素之间的键盘可访问性

对于表单元素,如果里面有type为submit类型的按钮,则浏览器天然支持单行输入框的回车提交行为。
然而原生的按钮有一个问题,那就是UI样式控制存在兼容性差异,尤其是桌面端网页项目
可以借助<label>元素实现按钮样式的移花接木


:focus伪类和outline都是IE8浏览器开始支持的。





html:

<form>
<p>用户名:<input></p>
<p>
<input id="t" type="submit">
<label class="btn" for="t">提交</label>
</p>
</form>


css:

[type="submit"] {
position: absolute;
clip: rect(0 0 0 0);
}
.btn {
display: inline-block;
padding: 2px 12px;
background-color: #cd0000;
color: #fff;
font-size: 14px;
cursor: pointer;
}
:focus + label.btn {
outline: 1px solid Highlight;
outline: 5px auto -webkit-focus-ring-color;
}


二、CSS hover显示隐藏内容的键盘可访问性

我列表元素信息很多,为了防止视觉干扰,一些操作按钮在鼠标hover当前列表的时候才显示





很多小伙伴在实现的时候,并没有考虑很多,就直接使用display:none隐藏,或者visibility:hidden隐藏,于是会导致隐藏的控件元素压根没法通过键盘让其显示,因为这两种隐藏方式会让元素无法被focus,那该怎么办呢?可以试试使用透明度opacity控制内容的显隐,于是,我们就可以通过:focus伪类让按钮focus时候可见,

html:

<table width="300px" border="1">
<tr>
<td>栏目1</td>
<td>栏目2</td>
<td>
<a href="https://www.baidu.com" class="btn1">删除</a>
</td>
</tr>
<tr>
<td>栏目1</td>
<td>栏目2</td>
<td>
<a href="javascript:;" class="btn1">删除</a>
</td>
</tr>
<tr>
<td>栏目1</td>
<td>栏目2</td>
<td>
<a href="javascript:;" class="btn1">删除</a>
</td>
</tr>
</table>


css:

table {
border-spacing: 0;
}
.btn1 {
display: inline-block;
padding: 2px 12px;
background-color: #cd0000;
color: #fff;
font-size: 14px;
cursor: pointer;
}
tr .btn1 {
opacity: 0;
filter: alpha(opacity=0);
}
tr:hover .btn1,
tr .btn1:focus {
opacity: 1;
filter: none;
}


三、CSS hover显示下拉内容的键盘可访问性

首先一定要有键盘可访问的触发源,也就是无论是点击区还是hover区域,一定要有个
<a>
标签,或者原生按钮,或者设置了tabindex的普通元素。

把交互形式和实现原理,分为下面四类:

列表HTML结构依赖,使用CSS定位,hover显示;

列表HTML结构依赖,使用CSS定位,click显示;

列表HTML结构不依赖,使用JS定位,hover显示;

列表HTML结构不依赖,使用JS定位,click显示;

例如,导航上的二级菜单常使用CSS进行定位,对HTML结构有要求;而搜索的自动下拉提示列表则几乎都使用JS进行定位,列表直接创建于标签下,对HTML结构无依赖。

针对上面四种情况,我需要额外进行的处理分别是:

增加:focus控制;

无需额外处理;

增加JS focus事件处理,处理细节同mouseenter;

无需额外处理;

这些浮层显示的时候,通过上下左右键进行控制



html:

<div class="trigger-container">
<a href="javascript:;" class="trigger" data-target="list">更多操作▾</a>
<div class="list" id="list">
<a href="https://www.baidu.com">编辑</a>
<a href="javascript:;">删除</a>
</div>
</div>
<div class="trigger-container">
<a href="javascript:;" class="trigger" data-target="list1">更多操作▾</a>
<div class="list" id="list1">
<a href="javascript:;">编辑</a>
<a href="javascript:;">删除</a>
</div>
</div>


css:

.trigger-container {
float: left;
}
.list {
position: absolute;
visibility: hidden;
}
.trigger:hover + .list,
.trigger:focus + .list {
visibility: visible;
}
.outline {
outline: 1px solid Highlight;
outline: 5px auto -webkit-focus-ring-color;
}


js:

(function (doc) {
if (doc.addEventListener) {
var keycode = {
37: 'left',
38: 'up',
39: 'right',
40: 'down',
13: 'enter',
9: 'tab'
};
// 键盘高亮类名
var className = 'outline';
// 高亮类名的添加与删除
var classList = {
add: function (ele) {
ele.className = ele.className + ' ' + className;
},
remove: function (ele) {
ele.className = ele.className.split(/\s+/).filter(function (cl) {
if (cl != className) {
return cl;
}
}).join(' ');
},
removeAll: function () {
[].slice.call(doc.querySelectorAll('.' + className)).forEach(function (ele) {
classList.remove(ele);
});
},
has: function (ele) {
return ele.className.split(/\s+/).filter(function (cl) {
if (cl == className) {
return cl;
}
}).length > 0;
}
};

//键盘事件
doc.addEventListener('keydown', function (event) {
// 是否是上下左右键
var direction = keycode[event.keyCode];
if (!direction) {
return;
}
if (direction == 'tab') {
classList.removeAll();
return;
}
// 当前激活元素
var trigger = doc.activeElement;
if (!trigger) {
return;
}
// 对应的面板
var attrTarget = trigger.getAttribute('target') || trigger.getAttribute('data-target');
var target = attrTarget && doc.getElementById(attrTarget);
if (!target) {
return;
}
// 需要是显示状态
if (target.clientWidth == 0 && target.clientHeight == 0) {
return;
}
// 如果是回车事件
if (direction == 'enter') {
var eleFocus = target.querySelector('.' + className);
if (eleFocus) {
// 阻止默认的回车
event.preventDefault();
eleFocus.click();
return;
}
}
// 如果都符合,同时有目标子元素
var arrEleFocusable = target.storeFocusableEle, index = target.storeIndexFocus;
if (!arrEleFocusable) {
arrEleFocusable = [].slice.call(target.querySelectorAll('a[href], button:not(:disabled), input:not(:disabled)'));
target.storeFocusableEle = arrEleFocusable;
target.storeIndexFocus = -1;
index = -1;
}
if (arrEleFocusable.length == 0) {
return;
}
// 先全部清除focus态
arrEleFocusable.forEach(function (ele) {
classList.remove(ele);
});
// 阻止默认的上下键滚屏
event.preventDefault();
// 索引加加减减
if (direction == 'left' || direction == 'up') {
index--;
if (index < 0) {
index = -1;
}
} else if (direction == 'right' || direction == 'down') {
index++;
if (index > arrEleFocusable.length - 1) {
index = arrEleFocusable.length;
}
}
// 如果有对应的索引元素
if (arrEleFocusable[index]) {
// 高亮对应的控件元素
classList.add(arrEleFocusable[index]);
}
// 记录索引
target.storeIndexFocus = index;
});

doc.addEventListener('mousedown', function (event) {
var target = event.target;
if (target && !classList.has(target)) {
classList.removeAll();
}
});
}
})(document);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: