AGE
AGE

Reputation: 3792

Determining when focus occurs outside an element

I am trying to see if the following is possible:

My implementation consists of a very simple HTML navigation bar as seen in the snippet below. I do native keyboard navigation between each <a> tag. The first list element is the title, containing an anchor that is visible, the second element

<ul class="test">
  <li>
    <a href="#">Title</a>
  </li>
  <li>
    <ul>
      <li>
        <a href="#">Some link</a>
      </li>
      <li>
        <a href="#">Some link</a>
      </li>
      <li>
        <a href="#">Some link</a>
      </li>
      <li>
        <a href="#">Some link</a>
      </li>
    </ul>
  </li>
</ul>

The goal of this navigation bar is simple:

  1. Native keyboard tab or shift+tab to go from anchor to anchor.
  2. Show the drop down menu when focusing on the inner anchor elements.
  3. Hide the drop down menu when not focusing on any inner anchor elements.

I have 1 and 2 down, but 3 is tricky because of the requirements listed above. I know this can be very easily be done using a global selector, but this challenge is about figuring out and understanding if it can be done otherwise.

$(document).ready(function() {
    dropdownMenu = $(".test > ul");
    dropdownMenu.hide();

    $(".test").focusin(function() {
        if (dropdownMenu.is(":hidden")) {
          dropdownMenu.show();
        }
    });
    // Some selector for some event here to handle the focus/clicks outside the $(".test") element
});

Important: I consider event.stopPropagation();, as explained in CSS Tricks - The Dangers of Stopping Event Propagation to be a dangerous technique for the scope of this question, however if using said technique results in the most efficient approach then I will welcome it.

Upvotes: 8

Views: 4020

Answers (2)

nrabinowitz
nrabinowitz

Reputation: 55678

One option here, without global selectors, is to delay the close action briefly:

  var isVisible = false;

  $(".test").focusin(function() {
    if (dropdownMenu.is(":hidden")) {
      dropdownMenu.show();
    }
    isFocused = true;
  });

  $(".test").focusout(function() {
    isFocused = false;
    setTimeout(function() {
      if (!isFocused && dropdownMenu.is(":visible")) {
        dropdownMenu.hide();
      }
    }, 100);
  });

This is a little fiddly, but protects you from errant closes while tabbing. See https://jsfiddle.net/d5fa5o8q/4/

Upvotes: 2

Bill Criswell
Bill Criswell

Reputation: 32941

I'm not sure I'm following the question 100% but I think I got you.

You can use event.target with closest using the focusin event.

$(document).on('focusin', function (event) {
  var $target = $(event.target);
  if (!$target.closest('.bar').length) {
    console.log('You focused outside of .bar!');
  }
});

Here's a fiddle: https://jsfiddle.net/crswll/qk14r7c7/2/

Upvotes: 4

Related Questions