2010-02-03 70 views
6

我一直在搞清楚让下拉菜单键盘可访问的逻辑。jQuery搞清楚,如果父母已经失去了'重点'

的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')) 

但是,你不能这样做,因为李实际上并没有重点,而是在它的锚标记。

有什么建议吗?

UPDATE:

也许是一个更好/更简单的方法来提出这样的问题:

通过jQuery,是有办法“手表”,看看重点已经移到特定对象的所有儿童的外?

+0

是否有错字? '$('ul.primaryMenuItem a:first')。focus([​​call showMenu function])' - >'$('li.primaryMenuItem a:first')。焦点...' – superjos 2012-08-27 16:22:38

回答

2

使用新的jquery 1.4功能:focusinfocusout而不是blurfocus。下面是如何focusout不同:

该事件的内容的事件被发送到 元素时,或者里面的它 任何元素,失去焦点。这与模糊事件 是不同的,因为它 支持从父元素检测焦点丢失 (换句话说,它支持事件冒泡,即 )。

+0

@Keltex我看着那个。但是,那不是我需要的。我想知道父容器是否失去了焦点。如果任何一个子元素失去焦点,聚焦会触发。这意味着从子菜单切换到子菜单会触发该事件。我需要沿着“让人在容器外面标签”类型事件的方式行事。 – 2010-02-03 22:44:11

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'); ... 
+0

问题在于在退出最后一项之前离开菜单的方法很多。例如,你可能会倒退,这意味着你可以从最后一个项目中删除,但仍然在同一个菜单中。另外,可以在菜单中途使用键盘命令或鼠标点击模糊。 – 2010-02-04 00:06:33

6

您可以使用事件冒泡检查什么具有焦点在focusin事件中。我用以下代码获得了成功:


$("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'); 
    } 
    }); 

您可能(应该)可能会使其更加优化,但它可以工作。

+0

有趣!然而,我感到奇怪的是,将一个事件处理程序附加到身体及其所有子元素。是否有任何性能问题这样做?最终,你的解决方案是“在每个焦点上,看看它是否在菜单中。如果没有,关闭它'。这当然是有道理的。 – 2010-02-04 00:11:35

+0

好吧,只要焦点事件发生在主体内,它就会触发focusin事件,但焦点通常不会很快改变,并且会有少量的元素可以成为焦点事件的目标(链接/表单元素),所以我个人认为在每个焦点事件上调用这个比较都不会影响性能。您可以尝试优化比较(我不确定哪个更快$(e.target).is('ul.popUpMenu li a')或示例),并且您应该缓存元素查询。如果性能是一个真正的问题,您需要运行一些基准来检查影响。 – emmychan 2010-02-04 05:26:36

+2

focusin在某些浏览器中不会触发body标签(或许多其他标签)。将tabindex设置为-1似乎可以解决这个问题,并使得这个解决方案非常合适 - $(“body”)。attr(“tabindex”,-1); – John 2011-06-03 19:57:29

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键移动的子元素之间的文本字段应该触发事件的内容父,因为你仍是父母之内。有些事情一定会让你离开它一会儿,我怀疑这是一个错误...任何与我有关的人?无论如何,上面的插件修复了它。只需在代码之前加入它来“解决”这个问题。如果不是的话,会爱一个人解释为什么这不是一个错误。

感谢,大教堂

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 
}); 
+0

+1这让我朝着正确的方向前进。我不明白为什么会需要第二个选择器('#link_A_id> *'),我没有使用它。我还必须在超时中包装if语句,因为在下一个元素获得焦点之前'body'元素*会抢占焦点。 – toxalot 2013-04-25 11:38:05

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'); 
    } 
});