Building a Hidden Search Widget with JavaScript

This component creates a search field that expands when the user clicks the search button. Although visually simple, it demonstrates several important frontend concepts:

  • DOM selection
  • Event listeners
  • Component state
  • Synchronizing visual and accessibility state
  • Focus management

Selecting DOM Elements

The first step is selecting the elements JavaScript needs to interact with.

const searchBtn = document.querySelector(".search-btn");
const searchInput = document.querySelector(".search-input");


These lines do not change anything on the page.

Instead, they give JavaScript references to the existing DOM elements so they can be manipulated later.

At this point:

  • the search input is still collapsed
  • the button has not moved
  • no accessibility attributes have changed

The component simply knows which elements it will be working with later.

Registering an Event Listener

Next, a click event listener is attached to the button.

searchBtn.addEventListener("click", () => {
    ...
});


The anonymous callback function is registered, but it does not execute immediately.

Instead, JavaScript tells the browser:

“When this button is clicked in the future, run this function.”

Nothing changes visually until the user actually clicks.

Creating State

The first line inside the callback is probably the most important line in the component.

searchBtn.addEventListener("click", () => {
    ...
});


There are two things happening here.

First,

classList.toggle("search-input--active")

adds the class if it isn’t already present, or removes it if it is.

Second,

toggle() returns a boolean.

If the class was added:

true


If the class was removed:

false

That boolean is stored in:

const isOpen

This variable represents the current state of the component.

Instead of asking:

“Should I move the button?”

or

“Should aria-expanded be true?”

the rest of the component asks a single question:

Is the search currently open?

Everything else derives from that answer.

One Source of Truth

This is the biggest architectural idea in the component.

Rather than every part of the component deciding its own state independently, there is one source of truth:

const isOpen


Everything else uses that value.

isOpen
button position
aria-expanded
aria-label
focus behavior

This is the beginning of state-driven UI.

It is also one of the biggest ideas you’ll encounter in React.

Updating the Visual State

Next:

searchBtn.classList.toggle(
    "search-btn--active",
    isOpen
);