Paran0a
Paran0a

Reputation: 3457

Hide unordered list when losing parent focus

I have an input field. When user focuses on that input a unordered list appears beneath that input.

When user clicks on one of the list children that data is inserted into input and unordered list is hidden.

CURRENT PROBLEM

If a user clicks on input , but doesn't choose any of the options and then click somewhere else in the document the unordered list will remain shown.

I don't if there's some kind of event I'm missing that I could use to solve this.

$('body').on('focus blur click', '.autocomplete input , .autocomplete ul,  .autocomplete li' , function() {

if (event.type === 'focus' && event.target.nodeName == 'INPUT') { 
  $(this).siblings('ul').show();
 } else if ((event.type === 'blur' || event.type === 'click' ) && event.target.nodeName == 'LI') {
  $(this).parent('ul').hide();
 }

});


$('body').on('click' ,'li' ,function() {
 
 $('#P1_FIRST_NAME').val($(this).attr('data-value'));

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<div class="input__container autocomplete">
    <div class="input__label__container">
        <label for="P1_FIRST_NAME" tabindex="999">Autocomplete</label>
    </div>
    <input type="text" id="P1_FIRST_NAME" class="text_field" autocomplete="off" value="">
    <ul for="P1_FIRST_NAME" style="display: none;">
        <li tabindex="1" data-value="MAD">Madder</li>
        <li tabindex="1" data-value="SAD">Sandy</li>
    </ul>
</div>

Upvotes: 0

Views: 270

Answers (3)

Tomalak
Tomalak

Reputation: 338228

Notes

  • You can use an object with .on() to pass in multiple event handlers. Don't use if/else to switch between different behaviors for different events.
  • don't hard-code the ID of the element you want to set into your JS code. Use a data- attribute (see targetSelector below).
  • Work with .closest() to navigate to the correct elements.
  • Use CSS positioning for the <ul> so that it does not push down content below.

$('body')
    .on({
        'focus': function() {
            var $ul = $(this).closest('.autocomplete').find('ul');
            $ul.width( $(this).width() ).show();
        },
        'blur': function() {
            var $ul = $(this).closest('.autocomplete').find('ul');
            setTimeout(function () { $ul.fadeOut('fast'); }, 250);
        }
    }, '.autocomplete input')
    .on('click', '.autocomplete li', function() {
        var targetSelector = $(this).closest('ul').data('for');
        $(targetSelector).val( $(this).data('value') );
        $(this).closest('ul').fadeOut('fast');
    });
.autocomplete {
  position: relative;
}
.autocomplete ul {
  display: none;
  position: absolute;
  margin: 0;
  padding: 0;
  list-style: none;
  background-color: white;
  box-shadow: 4px 4px 5px -2px rgba(0,0,0,0.59);
  border: 1px solid #efefef;
}
.autocomplete ul li {
  padding: 3px;
  cursor: pointer;
}
.autocomplete ul li:hover {
  background-color: #FCF3CF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="input__container autocomplete">
    <div class="input__label__container">
        <label for="P1_FIRST_NAME" tabindex="999">Autocomplete</label>
    </div>
    <input type="text" id="P1_FIRST_NAME" class="text_field" autocomplete="off" value="">
    <ul data-for="#P1_FIRST_NAME">
        <li data-value="MAD">Madder</li>
        <li data-value="SAD">Sandy</li>
    </ul>
</div>
<p>content below content below content below content below content below</p>

Upvotes: 1

Nathaniel Flick
Nathaniel Flick

Reputation: 2963

you'd need to use "this" selectors if there's more than one on a page, but here's one way to click the field to show the ul list and click away from the ul list to hide it again:

$( ".text_field" ).click(function() {
  $('ul').show();
});

$(document).mouseup(function(e) {
    var container = $("ul");
    if (!container.is(e.target) && container.has(e.target).length === 0) {
        $('ul').hide();
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<div class="input__container autocomplete">
    <div class="input__label__container">
        <label for="P1_FIRST_NAME" tabindex="999">Autocomplete</label>
    </div>
    <input type="text" id="P1_FIRST_NAME" class="text_field" autocomplete="off" value="">
    <ul for="P1_FIRST_NAME" style="display: none;">
        <li tabindex="1" data-value="MAD">Madder</li>
        <li tabindex="1" data-value="SAD">Sandy</li>
    </ul>
</div>

Upvotes: 0

gurvinder372
gurvinder372

Reputation: 68393

check this fiddle

you need to bind a click event at html and stop event propagation at input and ul level

$('body').on('focus blur click', '.autocomplete input , .autocomplete ul,  .autocomplete li' , function(e) {

if (event.type === 'focus' && event.target.nodeName == 'INPUT') { 
  $(this).siblings('ul').show();
 } else if ((event.type === 'blur' || event.type === 'click' ) && event.target.nodeName == 'LI') {
  $(this).parent('ul').hide();
 }
 e.stopPropagation();

});


$('body').on('click' ,'li' ,function(e) {

 $('#P1_FIRST_NAME').val($(this).attr('data-value'));
 e.stopPropagation();

});

$("html").click( function(e) {

 $('ul').hide();

});

Upvotes: 2

Related Questions