How to use keyboard arrows to navigate in the dropdown

I have a simple dropdown and I want to have the possibility that the user can navigate via keyboard arrows (up/down) in the dropdown.

Here is the code:

function myFunction() {
  document.getElementById("myDropdown").classList.toggle("show");
}

window.onclick = function(event) {
  if (!event.target.matches('.dropbtn')) {
    var dropdowns = document.getElementsByClassName("dropdown-content");
    var i;
    for (i = 0; i < dropdowns.length; i++) {
      var openDropdown = dropdowns[i];
      if (openDropdown.classList.contains('show')) {
        openDropdown.classList.remove('show');
      }
    }
  }
}
.dropbtn {
  background-color: #3498DB;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

.dropbtn:hover, .dropbtn:focus {
  background-color: #2980B9;
}

.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-content {
  display: none;
  position: absolute;
  background-color: #f1f1f1;
  min-width: 160px;
  overflow: auto;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  z-index: 1;
}

.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

.dropdown a:hover {background-color: #ddd;}

.show {display: block;}
<div class="dropdown">
  <button onclick="myFunction()" class="dropbtn">Dropdown</button>
  <div id="myDropdown" class="dropdown-content">
    <a href="#home">Home</a>
    <a href="#about">About</a>
    <a href="#contact">Contact</a>
  </div>
</div>

This works fine when I am using tab, but I want to use arrows (up/down), can anybody try to help me with this?

70 thoughts on “How to use keyboard arrows to navigate in the dropdown”

  1. You could write a function that listens for keystrokes (when the dropdown is open), determine the key and change the focus accordingly.

    Reply
  2. Mihalma!

    So, by scrounging around on the internet (keywords "HTML5 keyboard navigable dropdown"), I found this: https://dev.to/emmabostian/creating-a-custom-accessible-drop-down-3gmo

    You can use a element around your dropdown.

    <select>
    <option value="">Value 1</option>
    <option value="">Value 2</option>
    <option value="">Value 3</option>
    <option value="">Value 4</option>
    </select>
    

    Sadly, this makes for some pretty trashy looking styling, which is what the rest of the article is for.

    Reply
  3. function myFunction() {
      let i = 0; // iterate over children elements inside dropdown
      const dropdown = document.getElementById("myDropdown");
      const childs = dropdown.children; // get all dropdown elements
      dropdown.classList.toggle("show");
      // attach keyboard events
      window.addEventListener("keydown", event => {
        switch(event.code) {
          case "ArrowDown":
            for (let c of childs) 
              c.classList.remove('dropbtn-selected')
            childs[Math.abs(i) % childs.length].classList.add('dropbtn-selected');
             i++;
            break;
          case "ArrowUp":
            for (let c of childs) 
              c.classList.remove('dropbtn-selected')
            childs[Math.abs(i) % childs.length].classList.add('dropbtn-selected');
             i--;
            break;
        }
      if (event.isComposing || event.keyCode === 229) {
        return;
      }
      });
    }
    .dropbtn {
      background-color: #3498DB;
      color: white;
      padding: 16px;
      font-size: 16px;
      border: none;
      cursor: pointer;
    }
    .dropbtn-selected {
      background-color: #2980B9;
    }
    .dropbtn:hover, .dropbtn:focus {
      background-color: #2980B9;
    }
    
    .dropdown {
      position: relative;
      display: inline-block;
    }
    
    .dropdown-content {
      display: none;
      position: absolute;
      background-color: #f1f1f1;
      min-width: 160px;
      overflow: auto;
      box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
      z-index: 1;
    }
    
    .dropdown-content a {
      color: black;
      padding: 12px 16px;
      text-decoration: none;
      display: block;
    }
    
    .dropdown a:hover {background-color: #ddd;}
    
    .show {display: block;}
    <div class="dropdown">
      <button onclick="myFunction()" class="dropbtn">Dropdown</button>
      <div id="myDropdown" class="dropdown-content">
        <a href="#home">Home</a>
        <a href="#about">About</a>
        <a href="#contact">Contact</a>
      </div>
    </div>

    Don’t forget to remove eventListener as soon as you select a dropdown option and close

    Reply
  4. just add keydown event on dropdown ,play with the focus and cancel the scrolling

    var pos = 0;
    var maxpos = 0;
    function myFunction() {
       pos=0;
       document.getElementById("myDropdown").classList.toggle("show");
       maxpos = $("#myDropdown a").length - 1;
        var x = window.scrollX, y = window.scrollY;
        $("#myDropdown a").eq(pos).trigger("focus");
        window.scrollTo(x, y);
    }
    
    $("#myDropdown, .dropbtn").on("keydown", function(e){
      if(e.which == 40){//down 
        pos = pos == maxpos ? 0 : pos + 1;
        $("#myDropdown a").eq(pos).trigger("focus");
      }
      if(e.which == 38){//up 
        pos = pos == 0 ? maxpos : pos - 1;
        $("#myDropdown a").eq(pos).trigger("focus");
      }
      return false;//cancel scrolling
    });
    
    
    window.onclick = function(event) {
      if (!event.target.matches('.dropbtn')) {
         var dropdowns = document.getElementsByClassName("dropdown-content");
        var i;
        for (i = 0; i < dropdowns.length; i++) {
          var openDropdown = dropdowns[i];
          if (openDropdown.classList.contains('show')) {
            openDropdown.classList.remove('show');
          }
        }
      }
    }
    .dropbtn {
      background-color: #3498DB;
      color: white;
      padding: 16px;
      font-size: 16px;
      border: none;
      cursor: pointer;
    }
    
    .dropbtn:hover, .dropbtn:focus {
      background-color: #2980B9;
    }
    
    .dropdown {
      position: relative;
      display: inline-block;
    }
    
    .dropdown-content {
      display: none;
      position: absolute;
      background-color: #f1f1f1;
      min-width: 160px;
      overflow: auto;
      box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
      z-index: 1;
    }
    
    .dropdown-content a {
      color: black;
      padding: 12px 16px;
      text-decoration: none;
      display: block;
    }
    
    .dropdown a:hover, .dropdown a:focus{
    background-color: #ddd;
    }
    
    .show {display: block;}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="dropdown">
      <button onclick="myFunction()" class="dropbtn">Dropdown</button>
      <div id="myDropdown" class="dropdown-content">
        <a href="#home">Home</a>
        <a href="#about">About</a>
        <a href="#contact">Contact</a>
      </div>
    </div>
    Reply
  5. I would add the listeners to the dropdown and its content, rather than the entire page.

    You can call focus() on the previous/next child by storing a selected index using the drop-down’s dataset property.

    const
      ARROW_UP = 38,
      ARROW_DOWN = 40;
    
    const mod = (n, m) => ((n % m) + m) % m;
    
    const navigateList = e => {
      const
        dropdown = e.target.closest('.dropdown'),
        selectedIndex = parseInt(dropdown.dataset.selectedIndex, 10),
        children = dropdown.querySelectorAll('.dropdown-content a');
    
      switch (e.which) {
        case ARROW_UP:
          focusDropdownChild(dropdown, mod(selectedIndex - 1, children.length));
          break;
        case ARROW_DOWN:
          focusDropdownChild(dropdown, mod(selectedIndex + 1, children.length));
          break;
      }
    };
    
    const toggleShow = (e) => {
      const
        button = e.target,
        dropdown = button.closest('.dropdown'),
        content = dropdown.querySelector('.dropdown-content');
      content.classList.toggle('show');
      focusDropdownChild(dropdown, 0);
    };
    
    const navigate = e => {
      const
        item = e.target,
        content = item.closest('.dropdown-content');
      console.log(`Navigating to... "${item.textContent}"`);
      content.classList.toggle('show');
      e.preventDefault();
      e.stopImmediatePropagation();
    };
    
    const focusDropdownChild = (dropdown, index) => {
      const children = dropdown.querySelectorAll('.dropdown-content a');
      children.forEach(child => child.classList.remove('dropdown-item-focus'));
      dropdown.dataset.selectedIndex = index;
      children[index].focus();
      children[index].classList.add('dropdown-item-focus');
    };
    
    const connectListeners = (dropdown) => {
      const
        button = dropdown.querySelector('.dropdown-button'),
        content = dropdown.querySelector('.dropdown-content'),
        children = content.querySelectorAll('a');
      button.addEventListener('click', toggleShow);
      content.addEventListener('keydown', navigateList);
      children.forEach(child => child.addEventListener('click', navigate));
    };
    
    const disconnectListeners = (dropdown) => {
      const
        button = dropdown.querySelector('.dropdown-button'),
        content = dropdown.querySelector('.dropdown-content'),
        children = content.querySelectorAll('a');
      button.removeEventListener('click', toggleShow);
      content.removeEventListener('keydown', navigateList);
      children.forEach(child => child.removeEventListener('click', navigate));
    };
    
    document.querySelectorAll('.dropdown').forEach(connectListeners);
    .dropdown-button {
      background-color: #3498DB;
      color: white;
      padding: 16px;
      font-size: 16px;
      border: none;
      cursor: pointer;
    }
    
    .dropdown-button:hover,
    .dropdown-button:focus {
      background-color: #2980B9;
    }
    
    .dropdown {
      position: relative;
      display: inline-block;
    }
    
    .dropdown-content {
      display: none;
      position: absolute;
      background-color: #f1f1f1;
      min-width: 160px;
      overflow: auto;
      box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
      z-index: 1;
    }
    
    .dropdown-content a {
      color: black;
      padding: 12px 16px;
      text-decoration: none;
      display: block;
    }
    
    .dropdown a:hover {
      background-color: #ddd;
    }
    
    .show {
      display: block
    }
    
    .dropdown-item-focus {
      background: yellow;
    }
    <div class="dropdown">
      <button class="dropdown-button">Dropdown</button>
      <div class="dropdown-content">
        <a href="#home">Home</a>
        <a href="#about">About</a>
        <a href="#contact">Contact</a>
      </div>
    </div>
    Reply
  6. positive and negative multiplication rules plant community biology definition , positive reinforcement zhongwen. community action agency michigan lyrica dosage lyrica no prescripion , community action partnership in st joseph missouri followers guardian tales positive words meme , positive ana during pregnancy community representative synonym? positive pregnancy test jpg culture kings us, community first. credit union positive quotes rbg.
    community acquired pneumonia prevention , positive correlation or negative correlation biology leadership community community bank siren wi.

    Reply
  7. office suite for mac download drawing program website to buy Autocad software software recession 2020 india. 2020 software engineer program jp morgan 2020 kitchen design software for sale engeeeneringu#$sssaunnplus , whatsapp software 2020 ka. what is the best office software for mac, office compatible software for mac virtual dj software 2020 download office software update. leica geo office software free download software mac 2020, best free tax software 2020 reddit.

    Reply

Leave a Comment