将tabindex限制为页面的一部分

时间:2011-03-05 21:14:09

标签: javascript html tabindex modal-popup

情况:

我有一个网页打开模态窗口(灯箱),其中包含用户可以输入数据的表单。用户通常使用键盘进行导航,从一个字段切换到下一个字段。

问题:

当模态窗口打开时,只有窗口处于活动状态,使用鼠标无法访问页面的其余部分,但可以通过从模式窗口中跳出来访问元素。

问题:

如何使用标签按钮仅限表单窗口中的元素来限制移动?

我唯一能想到的是在打开模态窗口时使用Javascript在所有表单元素(以及其他可聚焦元素)上设置tabindex=-1,然后将tabindex值设置回以前的值模态窗口关闭时的值。 有更简单/更好的方法吗?

5 个答案:

答案 0 :(得分:6)

不,这是唯一的方法。

  1. 查找tabIndex大于-1 并且不属于您的模态的所有元素。
  2. 创建一个数组并使用对每个元素及其原始tabIndex的引用填充它。
  3. 将每个元素的tabIndex设置为-1,使其无法再从键盘获得焦点。
  4. 关闭模式对话框后,遍历数组并恢复原始tabIndex
  5. 这是一个快速演示:

    function isDescendant(ancestor, descendant) {
      do {
        if (descendant === ancestor) return true;
      } while (descendant = descendant.parentNode);
      return false;
    }
    
    var tabIndexRestoreFunctions;
    var lastFocused;
    
    document.getElementById("btn-show-modal").addEventListener("click", function(e) {
      lastFocused = document.activeElement;
      var modal = document.querySelector(".modal");
      tabIndexRestoreFunctions = Array.prototype
      // get tabable items which aren't children of our modal
      .filter.call(document.all, o => o.tabIndex > -1 && !isDescendant(modal, o))
      // iterate over those items, set the tabIndex to -1, and 
      // return a function to restore tabIndex
      .map(o => {
        var oldTabIndex = o.tabIndex;
        o.tabIndex = -1;
        return () => o.tabIndex = oldTabIndex;
      });
      // show modal
      modal.classList.add("shown");
      // focus modal autofocus
      modal.querySelector("[autofocus]").focus();
    });
    
    document.getElementById("btn-close-modal").addEventListener("click", function(e) {
      // restore tabs
      tabIndexRestoreFunctions && tabIndexRestoreFunctions.forEach(f => f());
      tabIndexRestoreFunctions = null;
      // hide modal
      document.querySelector(".modal").classList.remove("shown");
      // restore focus
      lastFocused && lastFocused.focus();
    });
    .modal {
      display: none;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(128, 128, 128, .75);
    }
    .modal.shown {
      display: flex;
    }
    .modal-content {
      margin: auto;
      width: 500px;
      padding: 30px;
      border: 1px solid #333;
      background-color: #fdfdfd;
    }
    <label>test
      <input autofocus />
    </label>
    <button>dummy button</button>
    <hr/>
    <button id="btn-show-modal">open modal</button>
    <div class="modal">
      <div class="modal-content">
        <label>test
          <input autofocus />
        </label>
        <button id="btn-close-modal">close modal</button>
      </div>
    </div>

    我们寻找tabIndex > -1,以便我们可以专注于可选元素。您可以进一步限制该过滤器忽略隐藏的元素,但我会留给您。在任何一种情况下,清单都不应该很大 或者,如在演示中,您可以使用一系列函数填充数组,其唯一目的是重置tabIndex。您也可以完全放弃数组,只需向受影响的元素添加data-original-tab-index属性...使用document.querySelectorAll("[data-original-tab-index]")在事后检索它们。

    这是一个使用数据属性存储原始tabIndex的演示,因此您不必维护自己的数组:

    function isDescendant(ancestor, descendant) {
      do {
        if (descendant === ancestor) return true;
      } while (descendant = descendant.parentNode);
      return false;
    }
    
    var lastFocused;
    
    document.getElementById("btn-show-modal").addEventListener("click", function(e) {
      lastFocused = document.activeElement;
      var modal = document.querySelector(".modal");
      Array.prototype.forEach.call(document.all, o => {
        if (o.tabIndex > -1 && !isDescendant(modal, o)) {
          o.dataset.originalTabIndex = o.tabIndex;
          o.tabIndex = -1;
        }
      });
      // show modal
      modal.classList.add("shown");
      // focus modal autofocus
      modal.querySelector("[autofocus]").focus();
    });
    
    document.getElementById("btn-close-modal").addEventListener("click", function(e) {
      // restore tabs
      Array.prototype.forEach.call(document.querySelectorAll("[data-original-tab-index]"), o => {
        o.tabIndex = o.dataset.originalTabIndex;
        delete o.dataset.originalTabIndex;
      });
      // hide modal
      document.querySelector(".modal").classList.remove("shown");
      // restore focus
      lastFocused && lastFocused.focus();
    });
    .modal {
      display: none;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(128, 128, 128, .75);
    }
    .modal.shown {
      display: flex;
    }
    .modal-content {
      margin: auto;
      width: 500px;
      padding: 30px;
      border: 1px solid #333;
      background-color: #fdfdfd;
    }
    <label>test
      <input autofocus />
    </label>
    <button>dummy button</button>
    <hr/>
    <button id="btn-show-modal">open modal</button>
    <div class="modal">
      <div class="modal-content">
        <label>test
          <input autofocus />
        </label>
        <button id="btn-close-modal">close modal</button>
      </div>
    </div>

    参见HTMLElement.dataset

答案 1 :(得分:3)

如何抓住tab-key?在最后一个元素上然后将焦点放在第一个元素上,反之亦然shift-tab

这是我在多模态 - diaolog环境中使用,在对话框中保持焦点,在鼠标或其他键的对话框之间切换

inputs=".editing, input, textarea, button, a, select"
no_tab="[type='hidden'], :disabled"

$focusable=dlg.$form.find(inputs).not(no_tab)


$fa_first=$focusable.first()
$fa_last=$focusable.last()

$fa_last.on("keydown", (evt) =>
    if evt.keyCode==9 && ! evt.shiftKey
        $fa_first.focus()
        evt.preventDefault()
        false
)
$fa_first.on("keydown", (evt) =>
    if evt.keyCode==9 && evt.shiftKey
        $fa_last.focus()
        evt.preventDefault()
        false
)

小编辑:替换了我的on&#34; unibind()&#34; (= .off(x).on(x))函数通过jQuery&#34; on()&#34;

答案 2 :(得分:1)

查看jQuery BlockUI Plugin。它们有an example using a modal box with two buttons,它也限制了标签。

它可能会或者可能不会与您的模态窗口一起开箱即用,但值得一看,而不必实施自己的解决方案。

答案 3 :(得分:0)

即使这是一篇旧帖子,我也在寻找解决这个问题的方法,并做了以下工作来解决它。

使用JQuery我会在模态窗口打开时禁用不同形式和div的所有输入字段(模态窗体本身除外)。

$('#formId :input').prop('disabled',true);

关闭模态窗体时,可以再次启用输入元素。

在页面周围“标记”时,不会考虑禁用字段。

答案 4 :(得分:0)

如果您想将焦点限制在“父母” dom内部

parent.addEventListener('focusout', function(event) {
    event.stopPropagation();

    if (node.contains(event.relatedTarget)) {  // if focus moved to another 
                                                              parent descend
        return;
    }

    parent.focus();  // otherwise focus on parent or change to another dom
})

所有现代浏览器都支持