How to Add Dropdowns on Input Fields with Bootstrap

Bootstrap is so 2017, Tailwind sucks, CSS is the only way to style – I don’t like frontend because everyone is so angry all the time. Nevertheless, a proper UI is critical to just about any application, so I must make do with the tools that are available. I’ve traditionally used React because the ecosystem of available components makes it trivial to add features in most cases but recently I’ve been drawn to returning to static HTML and vanilla JS.

Since I suck at design, I still go back to Bootstrap at the moment but one thing has always bothered me – the included dropdown components only work out of the box with buttons and links. The design choice is that a user will click on a button or link and then be presented with more links which is great, but I need an autocomplete. Specifically an autocomplete where I can put my own HTML elements in the dropdown without too much work.

Slightly embarrassing note – rachelscoolshit.com is built (but not for long) with a React frontend solely to add a MUI autocomplete component for the movie search tool. All the auth / state / API annoyingness is in place just so I could call a JS function and have it populate a preconfigured input field to have autocomplete.

Barebones Autocomplete

Because the vocal part of the internet has shunned Bootstrap the AI tools weren’t too helpful here, often pointing to old versions of bootstrap or JQuery monstrosities that I absolutely did not want to implement. I finally took a closer look at the docs and realized I had been overthinking the whole thing, but missing a few crucial details. My HTML is dead-simple, almost perfectly lifted from the bootstrap docs:

<div class="dropdown">
      <input type="text" class="form-control" id="terms" placeholder="Type here..." data-bs-toggle="dropdown">
      <ul class="dropdown-menu" aria-labelledby="terms">
        <li><a class="dropdown-item" href="#">Item 1</a></li>
        <li><a class="dropdown-item" href="#">Item 2</a></li>
        <li><a class="dropdown-item" href="#">Item 3</a></li>
      </ul>
    </div>

I’m not planning on doing anything truly dynamic here:

  • There is a parent div with the class ‘dropdown’
  • The input element still has the data-bs-attribute set
  • Basic dropdown with a couple items, class is dropdown-menu and it’s pointing to the input

The JS portion is also surprisingly straightforward. I added my standard debounce code because I never have a network-connected autocomplete without it, so feel free to remove it as needed:

const terms = document.querySelector("#terms");
let timeout;

const stopNormal = (event) => {
  event.preventDefault();
  event.stopPropagation();
  event.target.nextElementSibling.classList.remove("show");
};

const showDropdown = (event) => {
  clearTimeout(timeout);

  timeout = setTimeout(() => {
    const dropdownMenu = event.target.nextElementSibling;
    if (dropdownMenu) {
      dropdownMenu.classList.add("show");
    }
  }, 500);
};

terms.addEventListener("input", showDropdown);
terms.addEventListener("click", stopNormal);

Again, nothing out of the ordinary:

  • Assign the input field to something
  • Define a function to actually show the dropdown (with debounce)
  • Define a function to stop any bootstrap-intended features (added to click events)
  • Hook these functions up to the input

I honestly don’t know why it took me so long to try something like this out. I’ll blame the low barrier to entry for React but jeesh has it added some complexity to projects I have not needed just to avoid this.

Learnings / Considerations

You might notice if you remove the data-bs-toggle attribute from the input element that things keep working and you don’t need the stopNormal function. I tried this, but ended up keeping the tag in there only to keep the autoclose functionality in place. With the way I’ve written it above the user can click outside the input field or the results to close the dropdown. I’ve done the whole create-a-background-div with an event listener to close the ‘modal’ or dropdown and didn’t feel like I gained anything from that instead of this implementation.

Otherwise this was pretty clean – I’m sure there are better ways but I’m excited to see where I can go with a JS-controlled dropdown that I can hookup to any type of element while using Bootstrap and not touching CSS. Let me know how you’ve done it better!

Leave a comment