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

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

Какие-либо предложения?

ОБНОВИТЬ:

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

Через jQuery, есть ли способ "посмотреть", чтобы увидеть, переместился ли фокус за пределы всех дочерних элементов определенного объекта?

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

Вы могли бы (должны), вероятно, сделать его более оптимизированным, но это работает.

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

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

Как насчет, если вы делаете следующее:

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

Я придумал этот рецепт (ванильный 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;
}

Попробуй это

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

Это помогло мне... 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');
});

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

Спасибо дом

У меня была похожая проблема... Я создал 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');
    }
});
Другие вопросы по тегам