div에 onClick을 달았다가 키보드 사용자가 쓸 수 없게 된 이유
문제 발생
디자인상 버튼처럼 보이지 않아야 해서 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으로 지정해야 하며, 그렇지 않으면 폼 데이터를 제출하고 (존재하지 않는) 응답을 로드하려 시도해 현재 문서 상태를 망가뜨릴 수 있습니다.
해결 방안
- 의미대로 고릅니다.
<button type="button" class="card-action">자세히</button> <!-- 동작 -->
<a class="card-action" href="/posts/1">자세히</a> <!-- 이동 -->-
모양은 CSS로 만듭니다.
button { all: unset }같은 초기화 대신 필요한 속성만 재정의하고, 포커스 표시는 반드시 남깁니다(:focus-visible). -
폼 안의 버튼에는
type을 명시합니다. 기본값이 제출이라 "취소" 버튼이 폼을 보내버리는 사고가 흔합니다. -
링크를 버튼처럼 쓰지 않습니다.
href가 있어야 새 탭·복사·미리보기가 동작합니다. JS로 이동시키는 순간 그 기능들이 전부 사라집니다. -
정말 커스텀 요소가 필요하면 대가를 인정합니다.
role="button"+tabindex="0"+ 키보드 핸들러를 모두 갖춰야 하고, 그래도 사용자 기대와 어긋나는 부분이 남습니다. -
키보드로 한 번 순회해봅니다. Tab만으로 화면의 모든 조작에 도달할 수 있는지 확인하면 이런 문제는 대부분 그 자리에서 드러납니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.