如何使用键盘箭头在下拉菜单中导航

时间:2021-02-19 12:53:17

标签: javascript html jquery css

我有一个简单的下拉菜单,我希望用户可以通过下拉菜单中的键盘箭头(向上/向下)导航。

代码如下:

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>

这在我使用 tab 时工作正常,但我想使用箭头(向上/向下),有人可以帮助我吗?

5 个答案:

答案 0 :(得分:0)

您可以编写一个函数来侦听击键(当下拉列表打开时),确定键并相应地更改焦点。

答案 1 :(得分:0)

米哈尔玛!

因此,通过在互联网上四处搜寻(关键字“HTML5 键盘导航下拉菜单”),我发现了这一点:https://dev.to/emmabostian/creating-a-custom-accessible-drop-down-3gmo

您可以在下拉列表周围使用元素。

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

遗憾的是,这会导致一些看起来很垃圾的样式,这正是本文其余部分的目的。

答案 2 :(得分:0)

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>

不要忘记在选择下拉选项并关闭后立即删除 eventListener

答案 3 :(得分:0)

我会将侦听器添加到下拉列表及其内容中,而不是整个页面。

您可以通过使用下拉列表的 focus() 属性存储所选索引来对上一个/下一个子项调用 dataset

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>

答案 4 :(得分:0)

只需在下拉菜单上添加 keydown 事件,播放焦点并取消滚动

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>