2010-02-03 3 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), чтобы понять это. В идеале я бы закрыл меню, когда основное внимание уделяется элементу на странице OTHER, чем любые дочерние элементы меню.

Логически, я ищу это:

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

Однако, вы не можете сделать это, так как LI фактически не имеет фокуса, а якорь тег внутри него.

Любые предложения?

UPDATE:

, возможно, лучше/простой способ задать вопрос:

Via JQuery, есть способ «часы», чтобы увидеть, если фокус перемещается за пределы всех детей определенного объекта ?

+0

Есть опечатка? '$ ('ul.primaryMenuItem a: first'). focus ([call showMenu function])' -> '$ ('li.primaryMenuItem a: first'). focus ...' – superjos

ответ

2

Используйте новые функции jquery 1.4: focusin и focusout вместо blur и focus. Вот как focusout отличается:

focusout событие отправляется на элемент, когда он, или любой элемент внутри его, теряет фокус. Это отличается от от события размытия тем, что оно поддерживает обнаружение потери фокуса из родительских элементов (другими словами, поддерживает пузырьки событий).

+0

@ Keltex Я посмотрел на это , Однако это не то, что мне нужно. Я хочу знать, потерял ли родительский контейнер фокус. focusout будет срабатывать, если ANY из дочерних элементов потеряет фокус. Это означает, что табуляция из подменю в подменю вызовет это событие. Мне нужно что-то вроде строки «имеет событие, связанное с человеком, занесенным вне контейнера». –

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

Проблема заключается в том, что есть много способов выйти из меню, прежде чем табуляция из последнего элемента. Например, вы можете перемещаться назад, а это значит, что вы можете выходить из последнего элемента, но все равно находиться в том же меню. Кроме того, можно использовать команду клавиатуры или щелчок мышью, чтобы размыть, находясь на полпути через меню. –

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

Интересно! Однако я чувствую себя странно, присоединяя обработчик событий к телу и всем дочерним элементам. Есть ли какие-либо проблемы с производительностью? В конечном счете, ваше решение «в каждом фокусе, посмотрите, находится ли оно в меню. Если нет, закройте его. Это, безусловно, имеет смысл. –

+0

Ну, это вызовет событие focusin в любое время, когда событие фокусировки происходит внутри тела, но фокус обычно не меняется очень быстро, и будет ограниченное количество элементов, которые могут быть объектом фокусного события (ссылки/элементы формы), поэтому лично я не думаю, что называть это сравнение на каждом фокусном мероприятии слишком сильно повлияет на производительность. Вы можете попытаться оптимизировать сравнение (я не уверен, что быстрее $ (e.target) .is ('ul.popUpMenu li a') или пример), и вы должны кэшировать запрос элемента. Если производительность является реальной проблемой, вам нужно запустить некоторые тесты, чтобы проверить влияние. – emmychan

+2

focusin не будет запускаться в некоторых браузерах для тега тела (или многих других тегов). Установка tabindex в -1, похоже, исправляет это и делает это решение подходящим - $ («body»). Attr («tabindex», -1); – John

0

Это помогло мне ... http://plugins.jquery.com/project/focus

Это будет обнаружить, если вы все еще в пределах родителя автоматически. В основном это изменяет фокусировку jQuery, чтобы работать таким образом, но я чувствую, как это должно работать.

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

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

Я не понимаю, почему вкладка прессования, чтобы переместить текстовое поле между дочерними элементами должны вызывать focusout на родитель, потому что вы все еще в пределах этого родителя. Что-то должно происходить, что вытаскивает вас из него на мгновение, и я подозреваю, что это ошибка ... кто-нибудь со мной по этому поводу? Ну, в любом случае плагин выше исправляет его. Просто включите его перед своим кодом, чтобы «исправить» это. Хотел бы кого-нибудь объяснить, почему это не ошибка, если это не так.

Спасибо, Дом

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

0

У меня была аналогичная проблема ... Я создал jsfiddle, чтобы определить, когда родитель FIELDSET теряет фокус и затем вызвать функция. Это, безусловно, можно было бы оптимизировать, но это начало.

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'); 
    } 
}); 
Смежные вопросы