1

ul will display when input field gets some input. Now I want to hide ul when both input and ul loses focus. Same as the stackoverflow search bar works. I tried some options but didn't work. I am sure I'm not applying appropriate method for this .I want to know the functionality for this so, kindly give your solution in javascript not jquery.

document.getElementById("1").addEventListener("blur", function(){
    document.getElementById("2").addEventListener("blur", function(){
        document.getElementById("2").style.display = 'none'
    })
})
<input type="text" id="one">
<ul id="two" style="display: none;">
   <li>ajbf</li>
</ul>

6
  • What about a global variable set to true when both events occur Commented Sep 24, 2020 at 14:48
  • ID's cannot start with a number. Commented Sep 24, 2020 at 14:49
  • 2
    ID's CAN start with a number: id The restriction was removed in HTML5 Commented Sep 24, 2020 at 14:57
  • How do you blur a ul element when you have an input element active? Commented Sep 24, 2020 at 14:58
  • @ATD huh... I didn't know the restriction was removed. Thanks for the info. Commented Sep 24, 2020 at 14:59

3 Answers 3

1

You can achieve this effect in two steps:

  1. Add the attribute tabindex to your <ul> - this will make the element focusable
  2. Create an object called pageState (or similar) which can monitor the state of various elements, including whether the <input> has a value which isn't '' and whether either the <input> or the <ul> have focus.

Working Example:

const myInput = document.getElementById('my-input');
const myList = document.getElementById('my-list');

// THIS object WILL KEEP TRACK OF THE STATE OF THE PAGE
const pageState = {
  myInputEmpty: true,
  myInputBlur: true,
  myListBlur: true
}

// THIS FUNCTION WILL CHANGE THE PAGE ACCORDING TO THE pageState OBJECT
const checkPageState = () => {

  if (pageState.myInputEmpty === true) {
    myList.classList.remove('show');
  }

  if (pageState.myInputEmpty === false) {
    myList.classList.add('show');
  }
  
  if ((pageState.myInputBlur === true) && (pageState.myListBlur === true)) {
    myList.classList.remove('show');
  }
}

// THIS FUNCTION MONITORS THE value OF THE <input>
const checkInputValue = () => {

  if (myInput.value === '') {
    pageState.myInputEmpty = true;
  }
  
  else {
    pageState.myInputEmpty = false;
  }
  
  checkPageState();
}

// THIS FUNCTION MONITORS WHERE ON THE PAGE THE FOCUS IS
const checkFocus = () => {

  if (document.activeElement.id !== 'my-input') {
    pageState.myInputBlur = true;
  }
  
  else {
    pageState.myInputBlur = false;
  }

  if (document.activeElement.id !== 'my-list') {
    pageState.myListBlur = true;
  }
  
  else {
    pageState.myListBlur = false;
  }
  
  checkPageState();
}

myInput.addEventListener('keyup', checkInputValue, false);
document.body.addEventListener('click', checkFocus, false);
#my-input,
#my-list {
  margin: 0;
  padding: 0;
}

#my-list {
  display: none;
  width: 120px;
  height: 120px;
  padding: 6px;
  border: 1px solid rgb(255, 0, 0);
  list-style-type: none;
}

#my-list.show {
  display: block;
}
<input type="text" id="my-input">
<ul id="my-list" tabindex="0">
<li>apples</li>
<li>bananas</li>
<li>cherries</li>
<li>dragonfruit</li>
</ul>


Further Reading:

Sign up to request clarification or add additional context in comments.

2 Comments

I've spent years trying to understand Model, View, Controller and never really grasped it. If the solution above is a crude example of it, I think I might have finally got my head around it...
I don't know why replacing document.body with window worked. Thanks.
0

Given that you may or may not have other items on the page with similar functionality requirements, it might be best to give the input and ul items IDs and target them directly:

const x = document.getElementById('mytext')
const ul = document.getElementById('mylist')
x.addEventListener('focus', function() {
  ul.style.display = '';
})

x.addEventListener('blur', function() {
  ul.style.display = 'none'
})
<input type="text" id="mytext">
<ul style="display: none;" id="mylist">
  <li>ajbf</li>
</ul>

1 Comment

Also note that you can not set the focus to an LI item unless you set it's tabindex attribute
0

Instead of having 2 events just add an click event to all the list items.

const input = document.querySelector('input');
const ul = document.querySelector('ul');
const lis = [...ul.querySelectorAll('li')];

input.addEventListener('input', function() {
  ul.style.display = 'initial';
});

lis.forEach(li => {
  li.addEventListener('click', function() {
    ul.style.display = 'none';
    console.log(this.innerHTML);
  });
});
<input type="text">
<ul style="display: none;">
  <li>abc</li>
  <li>efg</li>
  <li>hij</li>
</ul>

3 Comments

No it won't worked, because in your case as soon as I click on ul items after inputting , ul will hide. As I said I want to hide ul only when both input and ul loses focus.
You didn't mention anything about clicking on the li item.
Clicking on the LI item doesn't give it focus - you have to set its tabindex attribute first. Once that is done, blur should work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.