jeudi 16 juin 2016

Custom datalist - options disappear before selection

I've prepared the following code for a live search. On input focus, a list of available options appear. On input blur, options disappear. There lies my problem. When you click on an item form the list, input loses the focus and the list disappears before the click event takes place.

var artists = [{"artist":"3 Doors Down"},{"artist":"5 Seconds of Summer"},{"artist":"Adele"},{"artist":"Alicia Keys"},{"artist":"Amanda Abizaid"}];

function liveSearch(element) {
  return new liveSearch.prototype.init(element);
}
liveSearch.prototype = {
  init : function(element) {
    if (!element) {
      this.element = document.createElement("ul");
      this.element.classList.add("liveSearch");
    } else {
      this.element = element;
    }
  },
  update : function(queryElement) {
    this.clear();
    var lookUpArray = queryElement.name + "s";
    var results = this.search(artists, queryElement.value, queryElement.name);
    for (var i = 0; i < results.length; i++) {
      var li = document.createElement("li");
      var value = results[i];
      if (queryElement.value != "") {
        var re = new RegExp(queryElement.value, "gi");
        value = value.replace(re, "<span class="highlight">" + "$&" + "</span>");
      }
      li.innerHTML = value;
      this.element.appendChild(li);
    }
    return results.length;
  },
  search : function(lookUpArray, string) {
    var results = [];
    for (var i = 0; i < lookUpArray.length; i++) {
      if (lookUpArray[i].artist.toLowerCase().search(string.toLowerCase()) != -1) {
        results.push(lookUpArray[i].artist);
      }
    }
    return results;
  },
  clear : function() {
    this.element.innerHTML = "";
  },
  hidden : function() {
    this.element.style.display = "none";
  },
  visible : function() {
    this.element.style.display = "";
  },
  remove : function() {
    this.element.parentElement.removeChild(this.element);
  },
};
liveSearch.prototype.init.prototype = liveSearch.prototype;

$("#lyrics-form").on("focus", "input.liveSearch-input", function() {
  this.parentElement.appendChild(liveSearch().element);
  if (liveSearch(this.nextElementSibling).update(this)) {
    liveSearch(this.nextElementSibling).visible();
  } else {
    liveSearch(this.nextElementSibling).hidden();
  }
});

$("#lyrics-form").on("blur", "input.liveSearch-input", function() {
  liveSearch(this.nextElementSibling).remove();
});

$("#lyrics-form").on("keyup", "input.liveSearch-input", function() {
  if (this.value != "") {
    if (liveSearch(this.nextElementSibling).update(this)) {
      liveSearch(this.nextElementSibling).visible();
    } else {
      liveSearch(this.nextElementSibling).hidden();
    }
  } else {
    liveSearch(this.nextElementSibling).update(this);
  }
});

$("#lyrics-form").on("click", "li", function() {
  this.parentElement.previousElementSibling.value = this.innerText;
  liveSearch(this.parentElement).hidden();
});
input, ul {
  width: 180px;
  box-sizing: border-box;
}
.liveSearch {
  margin: 0;
  border: 1px solid silver;
  box-shadow: 0 2px 5px rgba(0,0,0,.5);
  position: absolute;
  background: white;
  padding: 5px;
  max-height: 195px;
  width: 180px;
  overflow-y: scroll;
  z-index: 1000;
}
.liveSearch li {
  padding: 2px 5px;
  cursor: default;
}
.liveSearch li:hover {
  background: rgba(0,0,0,.05);
  color: black;
}
.liveSearch .highlight {
  font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="addlyrics.php" id="lyrics-form" method="post" autocomplete="off" enctype="multipart/form-data">
	<input type="text" name="artist" id="artist" class="liveSearch-input" placeholder="Artist" required />
</form>

I've used a timeout on blur to get around the problem, but it's not good enough. It's not consistent, and higher delays makes the design ugly. How can I fix this?

$("#lyrics-form").on("blur", "input.liveSearch-input", function() {
    /* liveSearch(this.nextElementSibling).remove(); */
    setTimeout(function() {
        liveSearch(this.nextElementSibling).remove();
    }.bind(this), 100);
});

Aucun commentaire:

Enregistrer un commentaire