목록 화면 아래에 고정 버튼을 뒀는데 어떤 기기에서는 그것이 가려졌다. 목록 높이를 calc(100vh - 56px - 64px) 로 화면 높이에서 머리와 발의 크기를 빼서 계산해 주고 있었다.
Table of contents
Open Table of contents
원인 — 높이를 계산해서 준 배치
계산에 쓴 56px 은 머리가 한 줄일 때의 크기였다. 제목이 두 줄이 되는 기기에서는 실제 크기가 더 컸다.
빼는 값이 실제보다 작으니 목록이 그만큼 길어져 발을 덮었다. 계산이 틀린 것이 아니라 계산의 입력이 조건에 따라 변하는 값이었다.
조치 — 계산하지 않는 구조
두 가지 방법이 있었는데 머리 크기를 JavaScript 로 재서 계산에 넣는 것과 display: flex 세로 배치로 머리와 발은 자기 내용만큼 차지하고 목록이 flex: 1 로 남은 공간을 전부 갖게 하는 것이다. 뒤쪽을 골랐다. 이유는 재서 넣어도 글자 크기나 회전으로 다시 어긋나서다. 대신 옛 브라우저를 위해 두던 고정 높이 폴백을 버리는 것을 감수했다. 어느 쪽도 높이를 숫자로 지정하지 않는다.
계산을 없애니 계산이 어긋날 자리도 함께 없어졌고 머리가 몇 줄이 되든 배치가 유지됐다. 조건이 바뀔 때 따라오는 것은 계산이 아니라 구조여야 했다.
주의 — 화면 높이와 안전 영역
화면 높이 단위도 문제였는데 모바일에서 주소창이 보일 때와 숨을 때 100vh 값이 달랐다. CSS Values 명세가 정하는 동적 뷰포트 단위 100dvh 를 쓰되 지원하지 않는 브라우저를 위해 100vh 를 먼저 함께 선언했다.
일부 기기의 아래쪽 가림은 env(safe-area-inset-bottom) 을 여백으로 써서 해결했다. 기기마다 값이 다르므로 고정값을 주면 안 되고 값이 없는 기기를 위해 최솟값을 함께 잡았다.
검증 — 키보드가 올라올 때
입력 칸을 누르면 키보드가 올라오면서 실제로 보이는 높이가 줄어 고정 버튼이 가렸다. visualViewport 의 resize 를 받아 그 height 를 화면 높이로 쓰게 했다.
지원하지 않는 환경에서는 원래 값을 쓰도록 기본값을 뒀다. 여러 조건에서 확인해야 드러나는 문제라 작은 화면과 가로 모드와 키보드와 큰 글자 크기까지 바꿔 가며 봤다.
배경 — 위치를 숫자로 기억하면
목록을 다시 그리면 스크롤이 맨 위로 가서 보던 자리를 잃었다. 스크롤 위치를 숫자로 기억했다 되돌렸더니 항목 높이가 다르면 다른 자리가 됐다.
그래서 scrollTop 픽셀이 아니라 보고 있던 항목의 data-id 를 기억하고 다시 그린 뒤 scrollIntoView() 로 그 항목으로 이동하게 했다. 위치를 숫자로 기억하면 조건이 바뀔 때 어긋나므로 무엇을 보고 있었는지를 기억하는 편이 나았다.
정리
- 크기를 계산해서 주면 계산의 입력이 변할 때 어긋난다
- 위아래를 고정하고 가운데가 남은 공간을 갖게 한다
- 계산을 없애면 어긋날 자리도 없어진다
- 화면 높이가 동적으로 바뀌는 환경이 있다
- 안전 영역은 기기가 알려 주는 값을 쓰고 고정값을 주지 않는다
- 키보드가 올라오면 실제로 보이는 높이를 쓴다
- 여러 조건에서 봐야 드러나는 문제가 있다
- 위치는 숫자가 아니라 무엇을 보고 있었는지로 기억한다