How to set focusout on multiple element but prevent when shifting focus between them?

Viewed 192

I want to make a searchable input that when it gets focusin shows a dropdown containing list of possible values that fills using server side language. I tried to use focusin on any element that is not one of elements that are involved using code below:

$("body").not($(".dropdownSearch").find("input")).not($(".dropdownSearch").find("a")).on("click focusin", function () {
    $(".dropdown-menu").hide();
});

But when I run the code and click on input it actually get focusin on input but after that get focusin on body and focusout occur on input.

And this is html elements:

<div class="dropdownSearch dropdown">

    <span><input type="text" id="input1" name="input1" class="dropdown-toggle" /></span>
    <span><input type="text" id="input2" name="input2" class="dropdown-toggle" /></span>
    <span><input type="text" id="input3" name="input3" class="dropdown-toggle" /></span>

    <div class="dropdown-menu">
        <a class="dropdown-item">name</a>
        <a class="dropdown-item">name</a>
        <a class="dropdown-item">name</a>
    </div>

</div>

In this code multiple inputs can open same dropdown but change the values using js reads data from JSON files.

Is there any better way? Thanks for any help.

1 Answers

So let's unpack your selector a little, because I'm not sure it's doing what you intend it to do

You can simplify this $(".dropdownSearch").find("input") which looks for any input elements as the descendants of .dropdownSearch to a single CSS descendant selector like this $(".dropdownSearch input").

So you're selector is identical to this:

$("body").not(".dropdownSearch input").not(".dropdownSearch a")

What this is looking for is any body element, but then exclude that match if it's an input or a, which effectively resolve to just selecting the body:

$("body")

Which will listen for all events that happen and bubble all the way up to body, regardless of their origin.


What I think you want is a delegated handler to listen for events that bubble up to body, but then only proceed if they match the provided selector or you can check the value inside the handler like this:

$("body").on("click focus", function (e) {
  var notDropdownEl = $(e.target).is(":not(.dropdownSearch input):not(.dropdownSearch a)")

  if (notDropdownEl) {
    $(".dropdown-menu").hide();
  }
});

I'm not quite positive what your looking for, but here's a stack snippet that will hide the dropdown when you click outside of the dropdowsearch area

$("body").on("click focus", function (e) {
  if ($(e.target).is(":not(.dropdownSearch input):not(.dropdownSearch a)")) {
    $(".dropdown-menu").hide();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<div class="dropdownSearch dropdown">
    Will Not Hide if Clicked here
    <span><input type="text" id="input1" name="input1" class="dropdown-toggle" /></span>
    <span><input type="text" id="input2" name="input2" class="dropdown-toggle" /></span>
    <span><input type="text" id="input3" name="input3" class="dropdown-toggle" /></span>

    <div class="dropdown-menu">
        <a class="dropdown-item">name</a>
        <a class="dropdown-item">name</a>
        <a class="dropdown-item">name</a>
    </div>

</div>

<p>Will Hide if Clicked Here</p>
<input type="text" name="outside" />

Related