W3C Web Accessibility Initiative
Web Accessibility Tutorials Guidance on how to create websites that meet WCAG
Jump to the navigation

Example for an Application Menu

Example:
Code snippet: HTML
<div role="menubar">
  <ul role="menu" aria-label="functions" id="appmenu">
      <li role="menuitem" aria-haspopup="true">
        File
        <ul role="menu">
              <li role="menuitem">New</li>
              <li role="menuitem">Open</li>
              <li role="menuitem">Print</li>
          </ul>
      </li>
      <li role="menuitem" aria-haspopup="true">
        Edit
        <ul role="menu">
              <li role="menuitem">Undo</li>
              <li role="menuitem">Redo</li>
              <li role="menuitem">Cut</li>
              <li role="menuitem">Copy</li>
              <li role="menuitem">Paste</li>
          </ul>
      </li>
      <li role="menuitem" aria-haspopup="true">
          Format
          <ul role="menu">
              <li role="menuitem">Font</li>
              <li role="menuitem">Text</li>
          </ul>
      </li>
      <li role="menuitem" aria-haspopup="true">
        View
        <ul role="menu">
          <li role="menuitem">100%</li>
          <li role="menuitem">Zoom In</li>
          <li role="menuitem">Zoom Out</li>
        </ul>
      </li>
      <li role="menuitem">Help</li>
  </ul>
</div>
Code snippet: CSS
#appmenu {
  width:80%;
  float: left;
  margin: 0;
  padding: 0;
  color: #fff;
  background-color: #369;
  padding: .25em;
}

#appmenu li {
  white-space: nowrap;
  display:block;
  padding: .25em .75em;
  border: 1px solid #fff;
}

#appmenu > li {
  float: left;
  background-color: #036;
  text-align: center;
  position:relative;
  cursor: pointer;
}

#appmenu :hover,
#appmenu :focus {
  background-color: #fff;
  color: #036;
  border: 1px solid #036;
  text-decoration: underline;
}

#appmenu :hover li,
#appmenu :focus li {
  color: #fff;
  background-color: #036;
}

#appmenu > li > ul {
  display: none;
  position:absolute;
  left:0;
  right:0;
  top:100%;
  padding:0;
  margin:0;
  background-color: #036;
  width: 200%;
  text-align: left;
}

#appmenu > li[aria-expanded="true"] > ul {
  display:block;
}
Code snippet: JavaScript
var appsMenuItems = document.querySelectorAll('#appmenu > li');
var subMenuItems = document.querySelectorAll('#appmenu > li li');
var keys = {
  tab:    9,
  enter:  13,
  esc:    27,
  space:  32,
  left:   37,
  up:     38,
  right:  39,
  down:   40
};
var currentIndex, subIndex;

var gotoIndex = function(idx) {
  if (idx == appsMenuItems.length) {
    idx = 0;
  } else if (idx < 0) {
    idx = appsMenuItems.length - 1;
  }
  appsMenuItems[idx].focus();
  currentIndex = idx;
};

var gotoSubIndex = function (menu, idx) {
  var items = menu.querySelectorAll('li');
  if (idx == items.length) {
    idx = 0;
  } else if (idx < 0) {
    idx = items.length - 1;
  }
  items[idx].focus();
  subIndex = idx;
}

Array.prototype.forEach.call(appsMenuItems, function(el, i){
    if (0 == i) {
      el.setAttribute('tabindex', '0');
      el.addEventListener("focus", function() {
        currentIndex = 0;
      });
    } else {
      el.setAttribute('tabindex', '-1');
    }
    el.addEventListener("focus", function() {
      subIndex = 0;
      Array.prototype.forEach.call(appsMenuItems, function(el, i){
        el.setAttribute('aria-expanded', "false");
      });
    });
    el.addEventListener("click",  function(event){
      if (this.getAttribute('aria-expanded') == 'false' || this.getAttribute('aria-expanded') ==  null) {
        this.setAttribute('aria-expanded', "true");
      } else {
        this.setAttribute('aria-expanded', "false");
      }
      event.preventDefault();
      return false;
    });
    el.addEventListener("keydown", function(event) {
      switch (event.keyCode) {
        case keys.right:
          gotoIndex(currentIndex + 1);
          break;
        case keys.left:
          gotoIndex(currentIndex - 1);
          break;
        case keys.tab:
          if (event.shiftKey) {
            gotoIndex(currentIndex - 1);
          } else {
            gotoIndex(currentIndex + 1);
          }
          break;
        case keys.enter:
        case keys.down:
          this.click();
          subindex = 0;
          gotoSubIndex(this.querySelector('ul'), 0);
          break;
        case keys.up:
          this.click();
          var submenu = this.querySelector('ul');
          subindex = submenu.querySelectorAll('li').length - 1;
          gotoSubIndex(submenu, subindex);
          break;
        case keys.esc:
          document.querySelector('a[href="#related"]').focus();
      }
      event.preventDefault();
    });
});

Array.prototype.forEach.call(subMenuItems, function(el, i){
  el.setAttribute('tabindex', '-1');
  el.addEventListener("keydown", function(event) {
      switch (event.keyCode) {
        case keys.tab:
          if (event.shiftKey) {
            gotoIndex(currentIndex - 1);
          } else {
            gotoIndex(currentIndex + 1);
          }
          break;
        case keys.right:
          gotoIndex(currentIndex + 1);
          break;
        case keys.left:
          gotoIndex(currentIndex - 1);
          break;
        case keys.esc:
          gotoIndex(currentIndex);
          break;
        case keys.down:
          gotoSubIndex(this.parentNode, subIndex + 1);
          break;
        case keys.up:
          gotoSubIndex(this.parentNode, subIndex - 1);
          break;
        case keys.enter:
        case keys.space:
          alert(this.innerText);
          break;
      }
      event.preventDefault();
      event.stopPropagation();
      return false;
    });
  el.addEventListener("click", function(event) {
      alert(this.innerHTML);
      event.preventDefault();
      event.stopPropagation();
      return false;
    });
});