最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use keyboard arrows to navigate in the dropdown - Stack Overflow

programmeradmin1浏览0评论

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>

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?

Share Improve this question asked Feb 19, 2021 at 12:53 MihalmaMihalma 1013 silver badges12 bronze badges 0
Add a ment  | 

5 Answers 5

Reset to default 2

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./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>

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>

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

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

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.

发布评论

评论列表(0)

  1. 暂无评论