1

I got a input box, when the focus event fired on it, a list of prompt text will show up below the input box, and when blur event is fired, list will be hidden.

I add click event listener on the list of prompt, but when I try to click the prompt, blur event on the input box fired first so the click event on the prompt never get fired.

Is there a way to make click event on the prompt text fire first? After that I will fire the blur event handler to hide the prompt text.

var textEl = document.getElementById("text"),
  listEl = document.getElementById("list");

textEl.onfocus = function() {
  listEl.style.display = "initial";
}

textEl.onblur = function() {
  listEl.style.display = "none";
}

listEl.onclick = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}
#container {
  position: relative;
  width: 300px;
  margin: auto;
}

input {
  width: 100%;
}

#list {
  display: none;
  position: absolute;
  width: 100%;
  background: #fff;
}

.item {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
}
<div id="container">
  <input type="text" id="text">

  <div id="list">
    <div class="item"> item 1</div>
    <div class="item"> item 2</div>
    <div class="item"> item 3</div>
  </div>

</div>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Julien
  • 1,230
  • 16
  • 20
  • 1
    https://stackoverflow.com/questions/7621711/how-to-prevent-blur-running-when-clicking-a-link-in-jquery this might help you, just needs to be translated to vanilla js – Sterling Archer Sep 22 '17 at 17:21

3 Answers3

3

You could use mouseenter and mouseleave on listEl to decide whether or not the textEl should response to the blur event. So, based on that, you could just attach/detach the blur handler.

var textEl = document.getElementById("text"),
  listEl = document.getElementById("list");

textEl.onfocus = function() {
  listEl.style.display = "initial";
}

var blurHandler = function() {
  listEl.style.display = "none";
};

textEl.onblur = blurHandler;

listEl.onclick = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}

listEl.onmouseenter = function(event) {
  textEl.onblur = null;
}

listEl.onmouseleave = function(event) {
  textEl.onblur = blurHandler;
  
  // Also trigger the blur handler, if the element is not in focus.
  if(document.activeElement.id !== "text"){
    blurHandler();
  }
}
#container {
  position: relative;
  width: 300px;
  margin: auto;
}

input {
  width: 100%;
}

#list {
  display: none;
  position: absolute;
  width: 100%;
  background: #fff;
}

.item {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
}

.item:hover {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
  color: red;
}
<div id="container">
  <input type="text" id="text">

  <div id="list">
    <div class="item"> item 1</div>
    <div class="item"> item 2</div>
    <div class="item"> item 3</div>
  </div>

</div>

Or, you could use some CSS for it. For instance, you could set the display: block on list, when the input is being hovered on, or is in focus; or the list itself is in focus or is being hovered on.

#text:focus ~ #list, #list:focus,
#text:hover ~ #list, #list:hover{
  display: block;
}

Since it gets rid of those JS event handlers, I'd say it is preferable among the two.

var textEl = document.getElementById("text"),
  listEl = document.getElementById("list");


listEl.onclick = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}
#container {
  position: relative;
  width: 300px;
  margin: auto;
}

#list {
  display: none;
  position: absolute;
  width: 100%;
  background: #fff;
}

#text:focus ~ #list, #list:focus,
#text:hover ~ #list, #list:hover{
  display: block;
}

input {
  width: 100%;
}

.item {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
}

.item:hover {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
  color: red;
}
<div id="container">
  <input type="text" id="text">

  <div id="list">
    <div class="item"> item 1</div>
    <div class="item"> item 2</div>
    <div class="item"> item 3</div>
  </div>

</div>
Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
  • That's a good solution.But how can I do this on mobile device?mouseenter and mouseleave may not work. – Julien Sep 23 '17 at 03:20
  • 1
    Both snippets in my answer work on my phone. I would admit I am a bit surprised that mouseenter works. If I had to implement this, I'd go with the CSS based approach as that would be consistent across various devices. – Nisarg Shah Sep 23 '17 at 03:23
1

onmousedown will prevent the list to hide, instead of click.

var textEl = document.getElementById("text"),
  listEl = document.getElementById("list");

textEl.onfocus = function() {
  listEl.style.display = "initial";
}

textEl.onblur = function() {
  listEl.style.display = "none";
}

listEl.onmousedown = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}
#container {
  position: relative;
  width: 300px;
  margin: auto;
}

input {
  width: 100%;
}

#list {
  display: none;
  position: absolute;
  width: 100%;
  background: #fff;
}

.item {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
}
<div id="container">
  <input type="text" id="text">

  <div id="list">
    <div class="item"> item 1</div>
    <div class="item"> item 2</div>
    <div class="item"> item 3</div>
  </div>

</div>
bhansa
  • 7,282
  • 3
  • 30
  • 55
  • The list doesn't get hidden when you focus out of the input. Pretty easy trap to fall for. Basically, you are checking if the event handler is attached on the `listEl` or not. :) – Nisarg Shah Sep 22 '17 at 17:31
  • It works, and I use touchstart event on mobile device instead. Thanks for your help. – Julien Sep 23 '17 at 03:29
1

You have to change the onclick to onmousedown which will fire before the blur event.

listEl.onmousedown = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}

Try this snippet

var textEl = document.getElementById("text"),
  listEl = document.getElementById("list");

textEl.onfocus = function() {
  listEl.style.display = "initial";
}

textEl.onblur = function() {
  listEl.style.display = "none";
}

listEl.onmousedown = function(event) {
  var item = event.target;
  alert(item.textContent);
  textEl.value = item.textContent;
}
#container {
  position: relative;
  width: 300px;
  margin: auto;
}

input {
  width: 100%;
}

#list {
  display: none;
  position: absolute;
  width: 100%;
  background: #fff;
}

.item {
  padding: 12px 0;
  border-bottom: 1px solid #ccacac;
}
<div id="container">
  <input type="text" id="text">

  <div id="list">
    <div class="item"> item 1</div>
    <div class="item"> item 2</div>
    <div class="item"> item 3</div>
  </div>

</div>
Dinh Tran
  • 535
  • 4
  • 13