<div data-controller="datalist" class="datalist-container">
<input data-target="datalist.input" data-action="focus->datalist#showOptions
input->datalist#filterOptions
keydown->datalist#keyboardSelect" type="text" name="player_form[player]" id="player_form_player" value="">
<ul data-target="datalist.list" class="custom-datalist">
<li class="show" data-value="1" data-action="click->datalist#selectOption">
Andre Rublev</li>
<li class="show" data-value="2" data-action="click->datalist#selectOption">Andre Agassi</li>
<li class="show" data-value="3" data-action="click->datalist#selectOption">Pete Sampras</li>
<li class="show" data-value="4" data-action="click->datalist#selectOption">Roger Federer</li>
<li class="show" data-value="5" data-action="click->datalist#selectOption">Rafael Nadal</li>
<li class="show" data-value="6" data-action="click->datalist#selectOption">Novak Djokovic</li>
<li class="show" data-value="7" data-action="click->datalist#selectOption">Stefan Edberg</li>
<li class="show" data-value="8" data-action="click->datalist#selectOption">Stefanos Tsitsipas</li>
</ul>
</div>
body {
font-family: system-ui;
}
.show {
display: block !important;
}
.datalist-container {
position: relative;
width: 25%;
margin: 0 auto;
padding: 0.375rem 0.75rem;
input {
padding: 0.375rem 0.75rem;
border-radius: 5px;
border-color: grey;
width: 100%;
}
}
.custom-datalist {
padding-left: 0px;
margin-top: 0px;
display: none;
position: absolute;
width: 100%;
background-color: white;
border: 1px solid grey;
border-radius: 0 0 5px 5px;
border-top: none;
z-index: 10;
max-height: 10rem;
overflow-y: auto;
li {
display: none;
background-color: white;
padding: 0.375rem 0.75rem;
color: grey;
margin-bottom: 1px;
font-size: calc(7px + 0.7vw);
cursor: pointer;
&:hover {
background-color: grey;
color: black;
}
&.focus {
background-color: grey;
color: black;
}
}
}
View Compiled
import { useClickOutside } from "https://cdn.skypack.dev/stimulus-use";
const application = Stimulus.Application.start();
class DatalistController extends Stimulus.Controller {
static get targets() {
return ["input", "list"];
}
connect() {
useClickOutside(this);
this.focus = -1;
}
filterOptions() {
this.listTarget.classList.add("show");
const text = this.inputTarget.value.toUpperCase();
let options = this.listTarget.children;
for (var i = 0; i < options.length; i++) {
if (options[i].innerHTML.toUpperCase().indexOf(text) != -1) {
options[i].classList.add("show");
} else {
options[i].classList.remove("show");
}
}
}
showOptions() {
this.listTarget.classList.add("show");
}
clickOutside(event) {
this.listTarget.classList.remove("show");
this.focus = -1;
}
selectOption(event) {
this.inputTarget.value = event.currentTarget.innerHTML;
this.listTarget.classList.remove("show");
}
keyboardSelect(event) {
const options = Array.from(this.listTarget.children).filter((option) =>
option.classList.contains("show")
);
if (!options.length) return;
if (event.keyCode == 13) {
event.preventDefault();
if (this.focus > -1) {
options[this.focus].click();
}
} else if (event.keyCode == 40) {
this.focus++;
this.putFocus(options);
} else if (event.keyCode == 38) {
this.focus--;
this.putFocus(options);
}
}
putFocus(options) {
this.removeFocus(options);
if (this.focus >= options.length) {
this.focus = 0;
} else if (this.focus < 0) {
this.focus = options.length - 1;
}
options[this.focus].classList.add("focus");
options[this.focus].scrollIntoViewIfNeeded(false);
}
removeFocus(options) {
for (var i = 0; i < options.length; i++) {
options[i].classList.remove("focus");
}
}
}
application.register("datalist", DatalistController);
This Pen doesn't use any external CSS resources.