이전에 올렸던 드롭다운 비게이션 키보드로 접근하기포스트의 코드의 동작에 몇 가지 문제가 있어 수정보완 작업을 하였습니다. 관련해서 새로이 포스팅합니다.


웹접근성 연구소 사례연구

웹접근성에 관련한 이슈들은 아직까지 충분히 논의된 사항이 많지 않을 뿐더러, 필자같이 시야가 좁은 환경에서 일하는 경우 관련한 정보를 얻기가 힘들다. 특히 ‘드롭다운 메뉴의 동작을 어떻게 제한/활용할 것인가’같은 사소한 각각의 사안을 어떻게 처리하는 것이 시각장애인 사용자에게 있어 편리하고, 또한 일반 사용자의 웹 사용에 불편함이 없을지에 대해 스스로 선택할 수 밖에 없다.

다행히 한국 웹접근성 연구소에서 몇 가지 경우의 우수 사례들을 예제로 제공하고 있는데,한국웹접근성 연구소 웹사이트의 드롭다운 메뉴 예제를 살펴보았다.

드롭다운 메뉴의 키보드 내비게이션은 다음과 같다. Tab 키를 이용하여 ‘관련사이트’ 링크 메뉴로 초점이 이동하면, Enter 키를 눌러 하위 메뉴를 화면에 보여주고, 첫 번째 하위 메뉴(‘정부청사관리소’)로 초점이 이동한다. 이어서 Tab 키를 누를 때마다 다음 하위 메뉴로 이동한다. 마지막 하위 메뉴(‘소청심사위원회’)로 초점이 이동한 후에는 드롭다운 메뉴를 빠져나가면서 드롭다운 메뉴가 비활성화 된다.

Shift + Tab 키에 의한 이동시에는 현재의 메뉴가 주 메뉴(‘관련사이트’)이면 드롭다운 메뉴를 빠져나가면서 드롭다운 메뉴가 비활성화 되어야 하며, 하위 메뉴에 초점이 있을 경우에는 Shift + Tab 키를 누를 때마다 역순으로 이동하여 주 메뉴로 초점이 이동한다.

화면 낭독 프로그램을 사용할 경우에는 Tab 키와 Shift + Tab 키의 조작에 따라 초점이 주어지는 메뉴를 읽어주어야 한다. 예를 들어 ‘관련사이트’ 링크 메뉴에 초점이 주어지면 대체 텍스트(‘관련사이트, 엔터키를 누르시오’)를 읽어주어야 한다.
키보드를 이용하여 드롭다운 메뉴를 접근할 시에, 가장 문제가 되는 부분이 Tab키로 ‘드롭 다운’이 되는 메뉴들을 자연스럽게 열고 닫도록 제어하는 것이다. 오직 Tab키로만 하위 메뉴로 접근할 시에, 어떤 경우에 하위 메뉴를 열고, 어떤 경우에 하위 메뉴를 닫도록 할 지를 계산하고 그에 맞추어 제작하는 것이 많이 까다로운 일이었다.

때문에 이전 포스팅의 경우, ‘키보드로 메뉴에 접근’하는 특이한 경우에 해당 네비게이션에 접근 시 모든 네비게이션에 존재하는 모든 드롭다운 메뉴를 열고, 네비게이션을 떠날 경우 닫히도록하여 비교적 코드를 단순화하여 작성하였다. 그러나 focus 이벤트로 작성된 해당 코드는, IE에서 click 이벤트를 함께 발생시켰고, 이 때문에 메뉴를 클릭시에 키보드접근시와 똑같이 모든 하위메뉴를 열어버리는 동작이상을 겪었다.

그러나 위 예제에 대한 설명을 살펴보면, tab키가 아닌 enter 키를 이용해 메뉴를 열고 닫는 것을 확인할 수 있고, 다른 방식의 이벤트 트리거를 추가적으로 제공함으로 Javascript 코드를 비교적 수월하게 짤 수 있게 되었다.


HTML

기본적으로 BEM 작명규칙에 따라 작성하였다. nav 클래스에는 기본적인 네비게이션 모듈의 동작과 스타일을 입히고, 동등위 클래스인 nav--main 에는 각 모듈의 특수한 동작과 스타일을 연결한다. 이 경우에는 dropdown_keyboard_access() 함수를 필요한 네비게이션에만 작동시킬 수 있을 것이다.

<ul class="nav nav--main">
  <li class="nav__li">
    <a href="menu_01.php">Menu_01</a>
    <ul class="nav__ul">
      <li><a href="menu_01.php">Menu_01</a></li> 
      <li><a href="menu_02.php">Menu_02</a></li>
      <li><a href="menu_03.php">Menu_03</a></li>
      <li><a href="menu_04.php">Menu_04</a></li>
    </ul>
  </li>
</ul>

Javascript

jQuery(document).ready(function(){
  // .nav--main 에만 함수를 실행.
  dropdown_keyboard_access('.nav--main');
});

function dropdown_keyboard_access(nav) {
  $(nav + ' > .nav__li > a').keydown(function(e) {
    if(e.keyCode == 13){
      // 엔터를 눌렀을 때에 하위 메뉴를 열고, 첫번째 메뉴에 포커스를 이동한다.
      // IE에서 클릭이벤트를 해제하기 위해서 e.preventDefault(); 추가
      e.preventDefault();
      $(this).siblings('.nav__ul').show().find('a:first').focus();
    }
  });
  $(nav + ' > .nav__li').each(function(){
    // 드롭다운된 메뉴의 최하단에서 쉬프트키를 누르지 않은 채 탭키를 누르면 
    // 해당 하위 메뉴를 닫고, 
    // 상위 메뉴의 바로 다음 메뉴에 포커스를 위치시킨다.
    $(this).find('a:last').keydown(function(e){
      if( ! e.shiftKey ){
        $(this).parents('.nav__li').find('a:first').focus();
        $(this).parents('.nav__ul').hide()
      }
    });
  });
  $(nav + ' > .nav__li').each(function(){
    // 드롭다운된 메뉴의 최상단에서 쉬프트키와 탭키를 누르면 
    // 해당 하위 메뉴를 닫고, 
    // 상위 메뉴의 바로 이젠 메뉴에 포커스를 위치시킨다.
    $(this).find('a:eq(1)').keydown(function(e){
      if( e.shiftKey ){
        $(this).parents('.nav__li').before().find('a:first').focus();
        $(this).parents('.nav__ul').hide();
      }
    });
  });
}

CSS

.nav {
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav a {
  white-space: nowrap;
}
.nav__li {
  list-style: none;
  display: inline-block;
  * display: block;
  * float: left;
  zoom: 1;
  position: relative;
}
.nav__ul {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  * left: -40px;
}
.nav__li a {
  display: block;
}
.nav__li:hover > .nav__ul,
.nav__li:focus > .nav__ul,
.dropdown-visible .nav__ul {
  display: block;
}
.nav__ul li {
  width: auto;
  overflow: hidden;
  list-style: none;
}