アクセス可能なドロップダウンナビゲーションを作成する


ホバーナビゲーションはJavaScriptなしで行うのはかなり簡単です.HTMLとCSSはかなり簡単です.
HTML
<nav>
  <ul class="menu">
    <li class="menu__item">
      <a href="/" class="menu__link">About</a>
      <ul class="submenu">
        <li class="submenu__item">
          <a class="submenu__link" href="/our-mission">Our Mission</a>
        </li>
        <li class="submenu__item">
          <a class="submenu__link" href="/our-team">Our Team</a>
        </li>
      </ul>
    </li>
  </ul>
</nav>
CSS
.submenu {
  position: absolute;
  left: 0;
  padding: 0;
  list-style: none;
  height: 1px; 
  width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  clip: rect(1px, 1px, 1px, 1px);
}

.menu__item:hover .submenu {
  padding: 0.5rem 0;
  width: 9rem;
  height: auto;
  background: #eedbff;
  clip: auto;
}
注:私はvisually-hidden の代わりにスタイリングdisplay: none . これはアクセシビリティにとって重要です、そして、あなたは上記のリンクでより多くを読むことができます.
私は一般的なスタイルのいくつかを取り出したが、このCSSはホバー効果に貢献するものです.ただし、以下のGIFで見ることができますので、タブキーを使用すると同じように動作しません.

コーディングにジャンプする前に、私はこの問題に私のアプローチを共有したかったです.最初に、私はホバーだけでなく、フォーカスにもNAVを開くの問題を解決したい.第二に、それぞれのサブメニューがフォーカスしていることを確認したい.第三に、私はリンクを介してタブを1回、その特定のサブメニューを閉じるときに私はそれを閉じることを確認したい.さあ始めましょう!

ホバー効果を焦点に複製する


なぜなら私たちは:hover 上の擬似クラスli 要素、我々はまた、li 要素.しかし、あなたが私のブログポストを読むならば、あなたはtabindexesの概念を認めます.li 要素はtabindexを持ちませんが、リンクは行います.私が個人的に好きなのは、JavaScriptのトップレベルのリンクを対象とし、フォーカスイベントで親にクラスを追加することです.もう少しで歩きましょう.
const topLevelLinks = document.querySelectorAll('.menu__link');
console.log(topLevelLinks);

私はconsole.log 変数は、トップメニュー項目のノードリストを取得します.閉じるこの動画はお気に入りから削除されていますforEach ループして、それぞれのログparentElement 's.
topLevelLinks.forEach(link => {
  console.log(link.parentElement);
});

今私がしたいことはfocus リンクへのイベントリスナー、そしてコンソール.ログthis 正しい文脈を持っていることを確認するためにthis .
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    console.log(this);
  });
});

私は古い学校機能(ES 6 +矢印機能の代わりに)を使用していますthis がターゲットです.これについてのブログ記事がたくさんあります.とにかく、今、私たちはそれを目標にしていますparentElement このうち、li .
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    console.log(this.parentElement);
  });
});

この親要素はターゲットに必要なものです.私がするつもりは、私たちがコンソールにログインしたLIにクラスを加えることです.それから、私がすることは、我々が持っているスタイルを複製するためにCSSクラスを使うことです:hover .
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });
});

.menu__item:hover .submenu,
.menu__item.focus .submenu {
  padding: 0.5rem 0;
  width: 9rem;
  height: auto;
  background: #eedbff;
  clip: auto;
}

ご覧のように、メニューを閉じた後、私たちの行動項目のうちの1つは、私がレイアウトしたまま閉じていません.それをする前に、2番目について学びましょうblur イベントとそれが意味するもの.

ぼやけた出来事


ごとにblur event 要素がフォーカスを失うと解凍されます.最後のサブメニュー項目がフォーカスを失うまで、サブメニューを開いておきたい.それで、我々がする必要があることは、焦点クラスをぼかしに取り除きます.
私がしたい最初のことは、我々が持っているそのforeachループの中にありますnextElementSibling .
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });

  console.log(link.nextElementSibling);
});

次に、条件を作成します.サブメニューがある場合は、次のコードを実行したいだけです.これは私がしたことです.
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });

  if (link.nextElementSibling) {
    const subMenu = link.nextElementSibling;
    console.log(subMenu);
    console.log(subMenu.querySelectorAll('a'));
  }
});

私が両方を記録する理由subMenuquerySelectorAll は、視覚的な学習のためです.サブメニュー要素を正しくターゲットにしているのを見て、その中のリンクのためのNodeListを持っているのを見るのは良いことです.それで、私がここでしたいことは、それの最後の関連を目標とすることですquerySelectorAll . それをより読みやすくするために、変数に入れましょう.
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });

  if (link.nextElementSibling) {
    const subMenu = link.nextElementSibling;
    const subMenuLinks = subMenu.querySelectorAll('a');
    const lastLinkIndex = subMenuLinks.length - 1;
    console.log(lastLinkIndex);
    const lastLink = subMenuLinks[lastLinkIndex];
    console.log(lastLink);
  }
});

これらの最後のリンクの各々で、我々はそれからクラスを取り除くぼかしイベントを加えたいですli . まず、チェックアウトしましょうlink.parentElement 我々が我々が予想するものを得ていることを確実とするために.
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });

  if (link.nextElementSibling) {
    const subMenu = link.nextElementSibling;
    const subMenuLinks = subMenu.querySelectorAll('a');
    const lastLinkIndex = subMenuLinks.length - 1;
    const lastLink = subMenuLinks[lastLinkIndex];

    lastLink.addEventListener('blur', function() {
      console.log(link.parentElement);
    });
  }
});

私たちが期待することがある今、私はフォーカス・イベント・リスナーの上で行う反対をするつもりです.
topLevelLinks.forEach(link => {
  link.addEventListener('focus', function() {
    this.parentElement.classList.add('focus');
  });

  if (link.nextElementSibling) {
    const subMenu = link.nextElementSibling;
    const subMenuLinks = subMenu.querySelectorAll('a');
    const lastLinkIndex = subMenuLinks.length - 1;
    const lastLink = subMenuLinks[lastLinkIndex];

    lastLink.addEventListener('blur', function() {
      link.parentElement.classList.remove('focus');
    });
  }
});

最後にすることは、その条件文の中でフォーカスイベントリスナーを配置することです.実際には、サブメニューを持たない項目にフォーカスクラスを追加する必要はありません.
topLevelLinks.forEach(link => {
  if (link.nextElementSibling) {
    link.addEventListener('focus', function() {
      this.parentElement.classList.add('focus');
    });

    const subMenu = link.nextElementSibling;
    const subMenuLinks = subMenu.querySelectorAll('a');
    const lastLinkIndex = subMenuLinks.length - 1;
    const lastLink = subMenuLinks[lastLinkIndex];

    lastLink.addEventListener('blur', function() {
      link.parentElement.classList.remove('focus');
    });
  }
});

追加課題


このブログのポストは非常に長いので、多分私は来週フォローアップポストを行います.私がここで解決していない1つのことは、私のフォローアップ・ポストにしたがっています.を使用する場合tab and shift 同時にキーを押すと、メニューに戻ると動作しません.あなたが追加の挑戦をしたい場合は、自分でそれを試してみてください!
だから今のところだ!それが私のものと異なるならば、あなたがどのようにこれに解決策を考え出すかについて見たいです.あなたが何を考えて知ってみよう!