<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);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/[email protected]/dist/stimulus.umd.js
  2. https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js