如何在模态对话框中保持焦点? [英] How to keep focus within modal dialog?

查看:34
本文介绍了如何在模态对话框中保持焦点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 AngularSemantic-UI 开发一个应用程序.该应用程序应该是可访问的,这意味着它应该符合 WCAG 2.0.为了达到这个目的,模态应该将焦点保持在对话框内,并防止用户离开或在位于模态下方的页面元素之间使用标签"移动.

我找到了一些有效的例子,如下所示:

这是我尝试使用 Semantic-UI 创建可访问的模态:https://plnkr.co/edit/HjhkZg

如您所见,我使用了以下属性:

role="dialog"

aria-labelledby="modal-title"

aria-modal="true"

但他们没有解决我的问题.你知道有什么方法可以让我的模态保持焦点并只有在用户点击取消/确认按钮时才会失去焦点吗?

解决方案

目前没有简单的方法可以实现这一点.提出了 inert 属性 来尝试通过使任何具有该属性的元素以及所有这是儿童无法进入的.然而,采用速度缓慢,直到最近才在 Chrome Canary 的旗帜后面登陆.>

另一个提议的解决方案是制作一个可以跟踪模态堆栈的本机 API,基本上使当前不是堆栈顶部的所有内容都处于惰性状态.我不确定提案的状态,但看起来不会很快实施.

那么,这让我们何去何从?

不幸的是没有好的解决方案.一种流行的解决方案是创建一个查询选择器已知可聚焦元素,然后通过向模态中的最后一个和第一个元素添加 keydown 事件来将焦点捕获到模态.然而,随着 web 组件和 shadow DOM 的兴起,这个解决方案可以不再找到所有可聚焦的元素.

如果您始终控制对话框中的所有元素(并且您没有创建通用对话框库),那么最简单的方法可能是在第一个和最后一个可聚焦元素上为 keydown 添加一个事件侦听器,请检查如果使用了 tab 或 shift tab,则聚焦第一个或最后一个元素以捕获焦点.

如果你正在创建一个通用的对话框库,我发现唯一可行的方法是使用惰性 polyfill 或者让模态之外的所有东西都有一个 tabindex=-1.

var nonModalNodes;函数 openDialog() {var modalNodes = Array.from( document.querySelectorAll('dialog *') );//通过只找到没有 tabindex="-1" 的元素,我们确保我们没有//如果模态已经打开,则破坏元素的先前状态nonModalNodes = document.querySelectorAll('body *:not(dialog):not([tabindex="-1"])');for (var i = 0; i < nonModalNodes.length; i++) {var node = nonModalNodes[i];如果(!modalNodes.includes(节点)){//保存之前的 tabindex 状态,以便我们可以在关闭时恢复它node._prevTabindex = node.getAttribute('tabindex');node.setAttribute('tabindex', -1);//tabindex=-1 不会阻止鼠标聚焦节点(它//将在元素周围显示焦点轮廓).通过禁用来防止这种情况//模式打开时的轮廓样式//@see https://www.sitepoint.com/when-do-elements-take-the-focus/node.style.outline = '无';}}}函数 closeDialog() {//关闭模态并恢复tabindexif (this.type === 'modal') {document.body.style.overflow = null;//从节点恢复或删除 tabindexfor (var i = 0; i < nonModalNodes.length; i++) {var node = nonModalNodes[i];如果(节点._prevTabindex){node.setAttribute('tabindex', node._prevTabindex);node._prevTabindex = null;}别的 {node.removeAttribute('tabindex');}node.style.outline = null;}}}

I'm developing an app with Angular and Semantic-UI. The app should be accessible, this means it should be compliant with WCAG 2.0. To reach this purpose the modals should keep focus within the dialog and prevents users from going outside or move with "tabs" between elements of the page that lays under the modal.

I have found some working examples, like the following:

Here is my try to create an accessible modal with Semantic-UI: https://plnkr.co/edit/HjhkZg

As you can see I used the following attributes:

role="dialog"

aria-labelledby="modal-title"

aria-modal="true"

But they don't solve my issue. Do you know any way to make my modal keeping focus and lose it only when user click on cancel/confirm buttons?

解决方案

There is currently no easy way to achieve this. The inert attribute was proposed to try to solve this problem by making any element with the attribute and all of it's children inaccessible. However, adoption has been slow and only recently did it land in Chrome Canary behind a flag.

Another proposed solution is making a native API that would keep track of the modal stack, essentially making everything not currently the top of the stack inert. I'm not sure the status of the proposal, but it doesn't look like it will be implemented any time soon.

So where does that leave us?

Unfortunately without a good solution. One solution that is popular is to create a query selector of all known focusable elements and then trap focus to the modal by adding a keydown event to the last and first elements in the modal. However, with the rise of web components and shadow DOM, this solution can no longer find all focusable elements.

If you always control all the elements within the dialog (and you're not creating a generic dialog library), then probably the easiest way to go is to add an event listener for keydown on the first and last focusable elements, check if tab or shift tab was used, and then focus the first or last element to trap focus.

If you're creating a generic dialog library, the only thing I have found that works reasonably well is to either use the inert polyfill or make everything outside of the modal have a tabindex=-1.

var nonModalNodes;

function openDialog() {    
  var modalNodes = Array.from( document.querySelectorAll('dialog *') );

  // by only finding elements that do not have tabindex="-1" we ensure we don't
  // corrupt the previous state of the element if a modal was already open
  nonModalNodes = document.querySelectorAll('body *:not(dialog):not([tabindex="-1"])');

  for (var i = 0; i < nonModalNodes.length; i++) {
    var node = nonModalNodes[i];

    if (!modalNodes.includes(node)) {

      // save the previous tabindex state so we can restore it on close
      node._prevTabindex = node.getAttribute('tabindex');
      node.setAttribute('tabindex', -1);

      // tabindex=-1 does not prevent the mouse from focusing the node (which
      // would show a focus outline around the element). prevent this by disabling
      // outline styles while the modal is open
      // @see https://www.sitepoint.com/when-do-elements-take-the-focus/
      node.style.outline = 'none';
    }
  }
}

function closeDialog() {

  // close the modal and restore tabindex
  if (this.type === 'modal') {
    document.body.style.overflow = null;

    // restore or remove tabindex from nodes
    for (var i = 0; i < nonModalNodes.length; i++) {
      var node = nonModalNodes[i];
      if (node._prevTabindex) {
        node.setAttribute('tabindex', node._prevTabindex);
        node._prevTabindex = null;
      }
      else {
        node.removeAttribute('tabindex');
      }
      node.style.outline = null;
    }
  }
}

这篇关于如何在模态对话框中保持焦点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆