6

我一直在弄清楚使下拉菜单键盘可访问的逻辑。

HTML 的结构如下(为了清晰起见,使用了额外的类名):

<ul>
    <li class="primaryMenuItem">
        <a href="">Link 1</a>
        <ul class="popUpMenu">
            <li><a href="">Sub Link 1</a></li>
            <li><a href="">Sub Link 2</a></li>
        </ul>
    </li>
    <li class="primaryMenuItem">
        <a href="">Link 2</a>
        <ul class="popUpMenu">
            <li><a href="">Sub Link 1</a></li>
            <li><a href="">Sub Link 2</a></li>
        </ul>
    </li>    
</ul>

链接 1 和链接 2 悬停时将显示子菜单列表(下拉菜单)。我用一些 jQuery 和 jQuery hoverIntent 插件可以很好地工作。

问题是目前这只适用于鼠标。

下一个挑战是让它通过键盘工作。

我可以轻松地将焦点事件添加到顶级链接,然后触发二级菜单:

$('ul.primaryMenuItem a:first').focus([call showMenu function]) 

这很好用。

要关闭菜单,一个选项是,当打开另一个菜单时,检查是否已经打开了另一个菜单,如果有,关闭它。

这也很好用。

但是,失败的地方是,如果您打开了最后一个菜单并从中跳出。由于您尚未进入另一个菜单,因此该菜单保持打开状态。

挑战在于弄清楚如何/何时关闭菜单以及需要的逻辑(jQuery)来解决这个问题。理想情况下,当焦点位于页面上的元素上时,我会关闭菜单,而不是菜单的任何子元素。

从逻辑上讲,我正在寻找这个:

$('li.primaryMenuItem').blur([close $(this).find('ul.popUpMenu'))

但是,您不能这样做,因为 LI 实际上没有焦点,而是其中的锚标记。

有什么建议么?

更新:

也许是一个更好/更简单的提问方式:

通过 jQuery,有没有办法“观察”来查看焦点是否已经移到特定对象的所有子对象之外?

4

7 回答 7

6

您可以使用事件冒泡来检查焦点事件的焦点。我使用以下代码取得了成功:


$("li:has(ul.popUpMenu)").focusin(function(e) {
    $(this).children().fadeIn('slow');
  });
  $('body').focusin(function(e) {
    if (!$(e.target).parent().is('ul.popUpMenu li')) {
      $('ul.popUpMenu').fadeOut('slow');
    }
  });

您可以(应该)可能使其更加优化,但它确实有效。

于 2010-02-04T00:06:44.307 回答
2

使用新的 jquery 1.4 函数:focusinandfocusout代替blurand focus。以下是focusout不同之处:

当某个元素或其中的任何元素失去焦点时,会将 focusout 事件发送到该元素。这与 blur 事件不同,它支持检测父元素失去焦点(换句话说,它支持事件冒泡)。

于 2010-02-03T22:35:53.377 回答
2

如果您执行以下操作会怎样:

$('#link_A_id, #link_A_id > *').focusout(function () {
    if ($(document.activeElement).closest('#link_A_id').length == 0)
        //focus is out of link A and it's children
});
于 2012-01-12T03:02:21.977 回答
0

这帮助了我... http://plugins.jquery.com/project/focus

它会自动检测您是否仍在父级中。它基本上将 jQuery focusout 改为以这种方式工作,我觉得它应该是这样工作的。

<div class="parent">
   <input type="text" />
   <input type="text" />
</div>

$('#parent').focusout(function () {
    console.log('focusout of parent');
});

我不明白为什么按 Tab 在子元素之间移动文本字段会触发对父元素的聚焦,因为您仍在该父元素中。一定是发生了什么事情让你暂时离开了它,我怀疑这是一个错误……有人和我在一起吗?无论如何,上面的插件修复了它。只需将其包含在您的代码之前即可“修复”此问题。如果不是,希望有人解释为什么这不是错误。

谢谢, 多姆

于 2011-09-12T09:40:17.227 回答
0

尝试这个

$('li.primaryMenuItem:last li a:last').blur([do whatever you need to do])

从逻辑上讲,如果您的用户跳出标签,他一定是在关注最后一个锚点。

您甚至可以像这样设置自己的事件处理程序:

$('li.primaryMenuItem:last').bind('myblur', function() ...);

并在最后一个锚点模糊事件中调用它:

...blur(function() {
    $(this).parents('li.primaryMenuItem').trigger('myblur'); ...
于 2010-02-03T23:00:11.197 回答
0

我有一个类似的问题......我创建了一个 jsfiddle 来确定父字段集何时失去焦点然后调用一个函数。它当然可以优化,但这是一个开始。

http://jsfiddle.net/EKhLc/10/

function saveFields() {
  $.each($('fieldset.save'),function(index, value) {
    // WHERE THE POST WOULD GO
    alert('saving fieldset with id '+ value.id);
    $(value).removeClass('save');
  });

}
$('.control-group').focusin(function(){
  var thefield = $(this).parent('fieldset');
  if (!thefield.hasClass('active')) {
    if($('fieldset.active').length > 0){

      $('fieldset.active').removeClass('active').addClass('save');
      saveFields();
      }
    thefield.addClass('active');
    } else {
        console.log('already active');
    }
});
于 2013-04-10T22:48:38.763 回答
0

我想出了这个配方(vanilla JS),它完全符合 OP 的要求(提供一种聆听“焦点在容器元素外移动”的方法),并且足够通用,可以适用于任何用例。

在 OP 的示例中,它将像这样使用:

for (const primaryMenu of Array.from($('.primaryMenuItem'))) {
  onFocusOutsideOf(primaryMenu, () => closeMenu(primaryMenu));
}

这是代码onFocusOutsideOf

/**
 * Invokes `callback` when focus moves outside the given element's DOM
 * subtree.
 *
 * Returns a function that can optionally be called to remove the
 * event listener.
 */
function onFocusOutsideOf(container, callback) {
  const eventListener = () => {
    // setTimeout is used to allow `document.activeElement` to
    // be updated to the newly focused element. This is needed
    // since the 'focusout' event within the container happens
    // before the 'focus' event on the new element.
    setTimeout(() => {
      if (!isDescendantOf(container, document.activeElement)) {
        callback();
      }
    })
  };
  container.addEventListener('focusout', eventListener);

  return function unsubscribe() {
    container.removeEventListener('focusout', eventListener);
  }
}

/**
 * Utility function which returns whether a given DOM element
 * has another DOM element as a descendant.
 */
function isDescendantOf(ancestor: Element, potentialDescendant: Element) {
  let parent = potentialDescendant.parentNode;
  while (parent) {
    if (parent === ancestor) return true;
    parent = parent.parentElement;
  }
  return false;
}
于 2020-06-08T19:32:43.387 回答