资讯专栏INFORMATION COLUMN

【译】渐进增强的键盘导航

Juven / 1428人阅读

摘要:键盘访问的升级提供快捷方式不过,在访问网格时,通过键盘进行两个方向的移动会不会更好呢使用一点做渐进增强,我们做到了,可以使用鼠标或方向键访问网格。在本例总共,查找表名字是。

厚着脸做推广,个人网站 http://www.wemlion.com/。

本文转载自:众成翻译
译者:文蔺
链接:http://www.zcfy.cc/article/1179
原文:https://www.christianheilmann.com/2016/08/15/better-keyboard-navigation-with-progressive-enhancement/

创建界面时很重要的一点是,要考虑到那些只依赖键盘来使用产品的用户。这对可访问性来说是基本要求,在多数情况下,通过键盘操作访问也并非难事。这意味着首先,也是最重要的,是使用键盘可访问元素进行交互。

如果希望用户跳转到其他地方,使用带有有效的 href 属性的锚点连接

如果希望用户执行你自己的代码,并在当前文档中停留,使用按钮

通过流动 tabIndex 技术几乎可以使所有内容都能通过键盘访问,不过,既然已经有 HTML 元素可以做同样的事情,又何必再麻烦呢。

效果可视化

不过,使用恰当的元素并不那么简单;用户键盘在元素集合中所处的位置,也要显眼一些。给激活的元素加上轮廓(outline),浏览器解决了这个问题。这虽然超有用,但却是一些人的眼中钉,他们希望由自己控制所有交互的视觉展现。在 CSS 中将 outline 属性设置为 none,就能移除这个视觉辅助功能;不过这会带来不小的可访问性问题,除非你提供一个别的替代。

使用最显眼的 HTML 元素;加上一些 CSS,确保除 hover 之外 focus 状态同样也被定义。这样就可以使用户在列表中的一个个项目间,轻松地通过 tab 来切换了。Shift + Tab 允许回退。可以看下 demo,HTML 挺简单粗暴的。

使用列表,为我们的元素赋予了层次结构,以及普通浏览器所没有的可访问性技术的导航方式。它还带来很多 HTML 元素,我们可以自己添加样式。通过一点样式,我们可以将其转换为网格,占用更少的垂直空间,容纳更多内容。

ul, li {
  margin: 0;
  padding: 0;
  list-style: none;
}
button {
  border: none;
  display: block;
  background: goldenrod;
  color: white;
  width: 90%;
  height: 30px;  
  margin: 5%;
  transform: scale(0.8);
  transition: 300ms;
}
button:hover, button:focus {
  transform: scale(1);
  outline: none;
  background: powderblue;
  color: #333;
}

li {
  float: left;
}

/* 
  grid magic by @heydonworks 
  https://codepen.io/heydon/pen/bcdrl
*/

li {
  width: calc(100% / 4);
}
li:nth-child(4n+1):nth-last-child(1) {
  width: 100%;
}
li:nth-child(4n+1):nth-last-child(1) ~ li {
  width: 100%;
}
li:nth-child(4n+1):nth-last-child(2) {
  width: 50%;
}
li:nth-child(4n+1):nth-last-child(2) ~ li {
  width: 50%;
}
li:nth-child(4n+1):nth-last-child(3) {
  width: calc(100% / 4);
}
li:nth-child(4n+1):nth-last-child(3) ~ li {
  width: calc(100% / 4);
}

结果看起来非常棒,在查看列表的过程中,我们能清楚地看到自己所处的位置。

键盘访问的升级 —— 提供快捷方式

不过,在访问网格时,通过键盘进行两个方向的移动会不会更好呢?

使用一点 JavaScript 做渐进增强,我们做到了,可以使用鼠标或方向键访问网格。

不过记着,这仅仅只是一个增强。假设 JavaScript 因为各种可能的原因执行失败,依然可以通过 tab 来访问列表,我们失去的只是便利,但至少还有可用的界面。

我将这个打包成了一个小巧、无依赖的开源 JavaScript 项目 gridnav,可以在 GitHub 上获取代码。你要做的就是调用脚本,传给它一个选择器以获取元素列表。




 

通过列表元素的 data- 属性,可以自己定义每行元素的数量以及键盘可访问的元素。这些是可选的,但设置之后会让代码更快,出错可能性更小。README 文件更详细地解释了如何使用。

工作原理

开始考虑如何做的时候,像任何开发者一样,抓到了最复杂的方式。我以为,需要对父节点、兄弟节点的大量定位比较,使用上 getBoundingClientRect,进行大量的 DOM 访问。

之后我往回走了一步,意识到如何展示列表并不重要。最终不过是一个列表,我们要访问它而已。甚至不需要访问 DOM,因为我们所做的不过是从一堆按钮或锚点连接中的一个切换到另一个。我们要做的就是:

找到当前所在元素(event.target)。

获取按下的键。

根据键向前向后移动,或跳过一些元素到下一行。

就像这样(点击这里试试看):

我们需要跳过的元素数量是由每行的元素数量决定的。向上等同于向前 n 个元素,向下相当于向后 n 个元素。

使用一些小技巧,完整代码非常简短:

(function(){
  var list = document.querySelector("ul");
  var items = list.querySelectorAll("button");
  var amount = Math.floor(
        list.offsetWidth / list.firstElementChild.offsetWidth
      );
  var codes = {
    38: -amount,
    40: amount,
    39: 1,
    37: -1
  };
  for (var i = 0; i < items.length; i++) {
    items[i].index = i;
  }
  function handlekeys(ev) {
    var keycode = ev.keyCode;
    if (codes[keycode]) {
      var t = ev.target;
      if (t.index !== undefined) {
        if (items[t.index + codes[keycode]]) {
          items[t.index + codes[keycode]].focus();
        }
      }
    }
  }
  list.addEventListener("keyup", handlekeys);
})();

这里发生了什么?

首先我们获取到了列表元素,并缓存所有可通过键盘访问的元素:

  var list = document.querySelector("ul");
  var items = list.querySelectorAll("button");

计算每次上下移动需要跳过的元素数量,将列表的宽度除以列表第一个子元素(本例中是 LI)的宽度即可:

  var amount = Math.floor(
        list.offsetWidth / list.firstElementChild.offsetWidth
      );

相较于 switch 语句或者大量的 if 判断,我更乐意使用查找表。在本例总共,查找表名字是 codes。向上键值为 38,向下 40,向左 37,向右 39。假如我们拿到了 codes[37],值为 -1,也就是我们要在列表中移动的数量:

  var codes = {
    38: -amount,
    40: amount,
    39: 1,
    37: -1
  };

可以使用 event.target 获取按下键盘时列表中的选中元素,但我们不知道它在列表中的位置。为避免重复遍历列表,一次性遍历所有按钮,将它们在列表中的索引存储在按钮自身的 index 属性中。

  for (var i = 0; i < items.length; i++) {
    items[i].index = i;
  }

handlekeys() 完成剩余工作。读取所按按键的键值,然后到 codes 中查找。所以,我们只针对方向键做出响应。接着获取当前的元素,检查其是否有 index 属性。如果有,则检查我们将要移到的位置是否有元素存在。如果元素存在,则获得焦点。

  function handlekeys(ev) {
    var keycode = ev.keyCode;
    if (codes[keycode]) {
      var t = ev.target;
      if (t.index !== undefined) {
        if (items[t.index + codes[keycode]]) {
          items[t.index + codes[keycode]].focus();
        }
      }
    }
  }

给列表绑定一个 keyup 事件监听器,搞定 :)

  list.addEventListener("keyup", handlekeys);

如果你想看真实效果,这有一个讲述各个细节的快速视频教程。

视频在最后的代码部分有点 bug,因为我没将 count 属性和 undefined 对比,所以在第一个元素上,键盘功能没法正常工作(0 是 falsy)。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/111492.html

相关文章

  • 关于响应式设计一些关键点

    摘要:这里使用到的最多的中的属性,即媒体查询响应式设计实践原则渐进增强渐进增强英语是网页设计的一种策略,强调可访问性,语义标记,外部样式表和脚本技术。 1、响应式设计关键点在于: 媒体查询、流动网格、弹性图片,而不是flex布局或者是自适应布局 响应式和自适应的最直观的区别是:自适应是为了解决如何才能在不同大小的设备上呈现同样的网页,直观地来看就是盒子会根据屏幕分辨率的大小进行伸缩变换。所以...

    golden_hamster 评论0 收藏0
  • JavaScript 基础知识 - DOM篇(一)

    摘要:前言本篇文章是基础知识的篇,如果前面的基础知识入门篇看完了,现在就可以学习了。基本概念分为三个部分。在这个基础上使用一些新特性,高级浏览器支持,低级浏览器不支持。在对象中的属性是一个布尔值,只有和。 showImg(https://segmentfault.com/img/remote/1460000012581493?w=1920&h=1080); DOM 前言 本篇文章是JavaS...

    cuieney 评论0 收藏0
  • []如何构建自己Progressive Image Loader

    摘要:最后,我们必须调用函数来检查所有的渐进式图片容器在首次运行时是否在页面上可见。我们还必须在滚动页面或调整浏览器大小时调用函数,在一些旧的浏览器主要指可以非常迅速地对这些事件作出回应,所以我们需要限制回调,以确保它不能在毫秒内被再一次调用。 你可以在Facebook和Medium上遇到过渐进式图片,当页面滚动到视图时,模糊的低分辨率图像会被清晰的全分辨率版本替换。 showImg(htt...

    cartoon 评论0 收藏0
  • | Progressive Web AMPs

    摘要:小萝卜沪江前端开发工程师本文原创翻译,有不当的地方欢迎指出。简称就非常擅长做这些,事实这也是它们的宗旨。通过它精心设计的规则能保证优先显示页面的主要内容。原创新书移动前端高效开发实战已在亚马逊京东当当开售。 小萝卜(沪江前端开发工程师)本文原创翻译,有不当的地方欢迎指出。转载请指明出处。 如果你在过去几个月一直关注web开发社区,你很可能已经阅读了 progressive web ap...

    miracledan 评论0 收藏0
  • 】利用js原生方法替换react component实现性能提升

    摘要:原文链接原作者利用原生方法替换实现性能提升现在我们正在用实现一个具有邮件收发和即时聊天功能的客户端工具。为了防止用户感觉卡顿,所有的动作都需要在毫秒级完成。然而理想是丰满的,现实是残酷的。。。 原文链接 https://medium.com/missive-ap...原作者Philippe Lehoux 利用js原生方法替换react component实现性能提升 现在我们正在用Re...

    piapia 评论0 收藏0

发表评论

0条评论

Juven

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<