드롭다운 메뉴가 금방 닫힐 때, 틈 메우기와 transition-delay로 잡는 법
상단 드롭다운 메뉴에 마우스를 올려 하위 메뉴로 내려가는 순간 메뉴가 닫혀 버려 클릭을 못 한다는 이야기, 리뉴얼 상담에서 생각보다 자주 듣습니다.
원인은 대부분 상위 메뉴와 하위 메뉴 사이의 작은 틈, 그리고 닫힐 때 여유가 전혀 없는 CSS입니다. 스크립트 없이 CSS만으로 정리하는 방법을 순서대로 적었습니다.
💡 왜 내려가는 도중에 닫힐까요?
:hover는 포인터가 그 요소 위에 있는 동안에만 유지됩니다. 하위 메뉴를 margin-top으로 몇 px 띄워 두면 마우스가 그 빈 공간을 지나는 순간 어느 요소에도 올라가 있지 않게 되고, 메뉴는 바로 닫힙니다. 사람 손은 메뉴를 향해 비스듬히 움직이기 때문에 옆 메뉴를 살짝 스치기만 해도 같은 일이 생깁니다.
1. 상위 메뉴와 하위 메뉴 사이의 틈부터 메우기
간격은 margin이 아니라 하위 메뉴 안쪽의 padding-top으로 만듭니다. 눈에 보이는 간격은 같지만 그 공간도 하위 메뉴의 일부라서 hover가 끊기지 않습니다. 배경색 때문에 padding을 쓸 수 없다면 ::before로 투명한 다리를 하나 놓아 주면 됩니다.
.gnb > li { position: relative; }
.gnb .sub {
position: absolute;
top: 100%;
left: 0;
/* margin-top 대신 padding-top으로 간격 */
padding-top: 12px;
}
/* 디자인상 padding을 못 쓴다면 투명 다리 */
.gnb .sub::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: -12px;
height: 12px;
}
2. 닫힐 때만 transition-delay로 여유 주기
틈을 메워도 대각선으로 움직이다 잠깐 벗어나는 경우는 남습니다. 그래서 열릴 때는 바로, 닫힐 때는 0.2~0.3초 뒤에 사라지도록 지연을 겁니다. display: none은 전환 효과가 걸리지 않으니 opacity와 visibility 조합을 씁니다.
.gnb .sub {
opacity: 0;
visibility: hidden;
/* 닫힐 때: 0.25초 기다렸다가 사라짐 */
transition: opacity .2s ease .25s,
visibility 0s linear .45s;
}
.gnb > li:hover > .sub,
.gnb > li:focus-within > .sub {
opacity: 1;
visibility: visible;
z-index: 10;
/* 열릴 때: 지연 없이 바로 */
transition-delay: 0s;
}
지연을 0.5초 넘게 주면 옆 메뉴로 옮겼을 때 이전 하위 메뉴가 겹쳐 보입니다. 위 코드처럼 현재 열린 메뉴에 z-index를 올려 두면 새 메뉴가 항상 위에 뜨고, 지연은 0.3초 안팎이면 충분합니다.
3. 키보드와 터치 기기도 같이 챙기기
위 선택자에 :focus-within을 함께 넣은 이유는 마우스 없이 Tab 키로 이동하는 방문자도 하위 메뉴를 열 수 있게 하기 위해서입니다. 휴대폰과 태블릿에는 hover가 없어서 첫 탭이 메뉴 열기와 링크 이동 중 어느 쪽으로 동작할지 브라우저마다 다르게 느껴질 수 있습니다. 모바일에서는 드롭다운 대신 탭으로 펼치는 전체 메뉴를 따로 두는 편이 결과적으로 문의가 적었습니다.
| 증상 | 먼저 볼 곳 |
|---|---|
| 내려가자마자 닫힘 | 하위 메뉴의 margin-top, top 값 |
| 비스듬히 가면 닫힘 | 닫힘 지연(transition-delay) |
| 메뉴끼리 겹쳐 보임 | 지연 시간, z-index |
| Tab 키로 안 열림 | :focus-within 누락 |
"메뉴는 원하는 곳까지 무사히 데려다줄 때 제 역할을 합니다."
나눔웹은 메뉴를 만들 때 마우스 동선과 키보드 이동까지 직접 확인한 뒤에 오픈합니다.