DEV Community

Alex
Alex

Posted on

3 1

Mastering :focus-within

Dropdown-on-hover pattern is used in interfaces for a very long time. And most of that time, these dropdowns are not fully acccesible. For example, if you navigate via keyboard there is no chance that you will get into this dropdown and select something hidden in it.

But everything has changed when :focus-within landed in browsers. And according to caniuse.com this code is enough for about 81% of users to make them a little happier:

<div class="dropdown" tabindex="0">
  <p class="dropdown__title">This is dropdown</p>
  <div class="dropdown__wrapper">
    <a href="#">Some hidden link</a>
  </div>
</div>
.dropdown {
  position: relative;
}
.dropdown__wrapper {
  width: 0;
  height: 0;
  overflow: hidden;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1;
}
.dropdown:hover .dropdown__wrapper,
.dropdown:focus-within .dropdown__wrapper {
  width: 100%;
  height: auto;
  overflow: visible;
}

Note that you have to add tabindex="0" to the dropdown container, so it become focusable.

So, we’re done. But what about that near 19% of browsers that doesn’t support :focus-within? Javascript all the things!

// so here is module pattern begins
;(function () {
  // get all of dropdowns on page and define active class
  const dropdowns = Array.from(document.querySelectorAll('.dropdown'))
  const dropdownActiveClass = 'dropdown--active'

  // add event listeners to focusin and focusout to our dropdowns
  dropdowns.forEach(dropdown => {
    dropdown.addEventListener('focusin', focusinListener)
    dropdown.addEventListener('focusout', focusoutListener)
  })

  // if focus is inside dropdown, add active class
  function focusinListener (event) {
    event.target.closest('.dropdown').classList.add(dropdownActiveClass)
  }

  // if focused element is not dropdown, remove active class from all dropdowns
  function focusoutListener (event) {
    if (!document.activeElement.classList.contains('dropdown')) {
      dropdowns.forEach(dropdown => {
        dropdown.classList.remove(dropdownActiveClass)
      })
    }
  }
}())

And we have to update CSS:

.dropdown:hover .dropdown__wrapper,
.dropdown:focus-within .dropdown__wrapper,
.dropdown--active .dropdown__wrapper {
  width: 100%;
  height: auto;
  overflow: visible;
}

And then just enjoy this accessible dropdown! Full demo to play (use your Tab button):

Imagine monitoring actually built for developers

Billboard image

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay