el.hidden = true를 분명히 실행했고, 개발자 도구로 봐도 hidden 속성이 붙어 있습니다.
그런데 화면에는 그대로 보입니다. 범인은 CSS 파일에 있던 display: grid 한 줄이었습니다.
<style>
.cardgrid { display: grid; grid-template-columns: repeat(3, 1fr); }
</style>
<div class="cardgrid" hidden>
<div>카드</div>
</div>
hidden이 붙어 있는데도 이 요소는 화면에 보입니다.
속성을 잘못 건 것도, 자바스크립트가 안 돈 것도 아닙니다.
hidden 속성이 요소를 숨기는 건 HTML 명세가 마법을 부려서가 아닙니다.
브라우저 기본 스타일시트(user-agent stylesheet)에 다음 규칙이 들어 있기 때문입니다.
/* 브라우저가 기본으로 들고 있는 규칙 */
[hidden] { display: none; }
여기서 오해가 생깁니다. [hidden]은 속성 선택자라 클래스 선택자와 명시도가 같고,
뒤에 온 쪽이 이길 것 같습니다. 하지만 이 비교는 애초에 성립하지 않습니다.
CSS는 명시도를 따지기 전에 선언의 출처부터 정렬합니다.
| 순서 | 출처 | 예 |
|---|---|---|
| 1 (가장 약함) | 브라우저 기본 | [hidden] { display: none } |
| 2 | 사용자 설정 | 접근성 설정 등 |
| 3 | 작성자 스타일 | .cardgrid { display: grid } |
| 4 (가장 강함) | 작성자 !important | display: none !important |
내 CSS 파일에 쓴 display: grid는 3번, 브라우저의 display: none은 1번입니다.
명시도를 비교할 기회조차 없이 작성자 스타일이 이깁니다.
클래스에 display를 한 번이라도 지정하면 그 요소의 hidden은 그때부터 무력해집니다.
display: flex, display: grid, display: block 전부 해당합니다.
레이아웃을 잡으려고 무심코 쓴 한 줄이 나중에 hidden을 무력화합니다.
그리고 이 조합은 처음 만들 때는 멀쩡하고, 나중에 숨김 기능을 붙일 때 터집니다.
display를 지정하는 클래스에는 [hidden] 짝을 같이 만들어 둡니다.
속성 선택자를 클래스와 함께 쓰면 명시도가 더 높아지므로 확실하게 이깁니다.
.cardgrid { display: grid; }
.cardgrid[hidden] { display: none; } /* 짝을 만든다 */
프로젝트 전체에 한 번에 깔고 싶다면 전역 규칙을 하나 두는 방법도 있습니다.
[hidden] { display: none !important; }
다만 !important는 나중에 정당하게 display를 바꿔야 할 때 다시 걸림돌이 됩니다.
범위를 아는 프로젝트가 아니라면 클래스마다 짝을 만드는 쪽이 예측 가능합니다.
이 버그는 자동 검증을 통과한 채로 배포됐습니다. 검증 코드가 이렇게 생겼기 때문입니다.
// 통과했지만 아무것도 보장하지 못한 검사
expect(el.hidden).toBe(true);
el.hidden은 속성이 걸렸는지만 알려줍니다. 화면에서 사라졌는지는 모릅니다.
숨김을 검증하려면 실제로 계산된 스타일을 봐야 합니다.
// 화면에서 사라졌는지를 검사한다
expect(getComputedStyle(el).display).toBe("none");
상태를 검사할 때는 "내가 건 값"이 아니라 "브라우저가 최종적으로 계산한 값"을 봐야 합니다. 이 구분을 놓치면 검증은 계속 초록불인데 사용자만 문제를 봅니다.
hidden의 숨김은 브라우저 기본 스타일시트의 [hidden]{display:none}이 담당합니다.display가 무조건 이깁니다.display를 지정하는 클래스에는 .cls[hidden]{display:none} 짝을 만듭니다.el.hidden이 아니라 getComputedStyle().display로 합니다.이 글은 EonsFlow가 자동화 도구를 만들며 겪은 내용을 정리한 것입니다. "통과했는데 실제로는 안 되던" 다른 사례로 lstrip("www.")은 접두사를 지우지 않습니다도 함께 보시면 좋습니다.