본문으로 건너뛰기
개발 머꼬
개발 노트HTML
hohyeon.dev13

div에 onClick을 달았다가 키보드 사용자가 쓸 수 없게 된 이유

  • #Accessibility
  • #Engineering Note
  • #HTML

문제 발생

디자인상 버튼처럼 보이지 않아야 해서 div에 클릭 핸들러를 달았습니다.

<div class="card-action" onclick="openDialog()">자세히</div>

마우스로는 잘 동작했는데,

  • Tab으로 도달할 수 없었고
  • Enter/Space로 실행할 수 없었으며
  • 스크린리더가 "자세히"를 그냥 텍스트로 읽었습니다

반대로 페이지 이동을 button + location.href로 만든 곳에서는 새 탭으로 열기와 링크 복사가 되지 않았습니다.

원인 분석

요소의 의미가 곧 기본 동작입니다. MDN은 <button>마우스·키보드·손가락·음성 명령·보조 기술로 활성화되는 상호작용 요소로 정의하고, 활성화되면 폼 제출이나 대화상자 열기 같은 동작을 수행한다고 설명합니다. 링크(<a>)는 다른 페이지나 위치로 이동하는 요소입니다.

div에는 그런 의미가 없습니다. 포커스 가능성, 키보드 활성화, button 역할(role)이 전부 없으므로 우리가 직접 만들어야 합니다tabindex, role, Enter/Space 핸들러, 포커스 스타일까지. 그렇게 만들어도 브라우저가 무료로 주던 것을 흉내 내는 수준입니다.

<button>에는 흔한 함정이 하나 더 있습니다. MDN이 경고합니다 — 버튼이 서버로 폼 데이터를 제출하기 위한 것이 아니라면 type 속성을 button으로 지정해야 하며, 그렇지 않으면 폼 데이터를 제출하고 (존재하지 않는) 응답을 로드하려 시도해 현재 문서 상태를 망가뜨릴 수 있습니다.

해결 방안

  1. 의미대로 고릅니다.
<button type="button" class="card-action">자세히</button>   <!-- 동작 -->
<a class="card-action" href="/posts/1">자세히</a>            <!-- 이동 -->
  1. 모양은 CSS로 만듭니다. button { all: unset } 같은 초기화 대신 필요한 속성만 재정의하고, 포커스 표시는 반드시 남깁니다(:focus-visible).

  2. 폼 안의 버튼에는 type을 명시합니다. 기본값이 제출이라 "취소" 버튼이 폼을 보내버리는 사고가 흔합니다.

  3. 링크를 버튼처럼 쓰지 않습니다. href가 있어야 새 탭·복사·미리보기가 동작합니다. JS로 이동시키는 순간 그 기능들이 전부 사라집니다.

  4. 정말 커스텀 요소가 필요하면 대가를 인정합니다. role="button" + tabindex="0" + 키보드 핸들러를 모두 갖춰야 하고, 그래도 사용자 기대와 어긋나는 부분이 남습니다.

  5. 키보드로 한 번 순회해봅니다. Tab만으로 화면의 모든 조작에 도달할 수 있는지 확인하면 이런 문제는 대부분 그 자리에서 드러납니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.