2013년 7월 11일 목요일

display:none인 객체는 스크롤이 되지 않는다

오늘 버그를 수정하다 알게 된 새로운 사실. display:none인 객체는 스크롤 되지 않는다.

구현하고자 한 기능은 간단했다. 사용자가 버튼을 클릭하면 목록을 보여주어야 한다.
목록이 길어 스크롤이 생길 수도 있는데, 이때는 사용자가 선택한 아이템이 보이도록 스크롤 된 상태로 목록을 보여주어야 한다. 

목록은 처음에는 display:none 상태로 화면에서 보이지 않는다. 사용자가 클릭하면 display:block으로 변경하여 목록을 화면에 보여준다.

기존 로직은 다음과 같았다.

  1. 사용자가 선택했던 아이템이 목록에서 몇 번째인지 조회한다.
  2. 목록당 높이를 구해 얼마나 스크롤 해야 목록의 중간에 아이템이 보일지 픽셀을 계산한다.
  3. jQuery의 scrollTop을 이용해 스크롤을 이동한다.
  4. jQuery의 show를 이용해 css 속성을 display:block으로 변경한다.
간단하다. 하지만 스크롤은 되지 않는다. 무엇이 잘못된 것일까?
결론부터 말하면 display:none이 원인이다. "CSS Display and Visibility"에 display:none에 대한 설명이 다음과 같이 나와 있다.
display:none hides an element, and it will not take up any space. The element will be hidden, and the page will be displayed as if the element is not there:
display:none은 해당 요소가 공간을 차지하지도 않고 페이지를 렌더링할 때 마치 해당 요소가 거기에 없는 것 마냥 취급한다고 한다.

반면 같은 문서에 visibility:hidden에 대해서는 다음과 같이 설명하고 있다.
visibility:hidden hides an element, but it will still take up the same space as before. The element will be hidden, but still affect the layout.
display:none과는 해당 요소를 감추는 것까지는 같지만 여전히 공간을 차지하며 레이아웃에 영향을 끼친다고 되어있다.

그렇다면 브라우저는 display:none인 요소를 어떻게 처리할까? 이와 관련된 내용은 "Rendering: repaint, reflow/relayout, restyle"에 나와 있다. 
if you're hiding a div with display: none, it won't be represented in the render tree.
요소를 display:none으로 처리하면 해당 요소는 렌더링 트리에서 제거된다.

스크롤 이동은 css 속성으로 지정하는 것이 아니다. 그냥 렌더링이다. 이처럼 스크롤하려는 요소가 렌더링 트리에서 빠져있으면 scrollTop을 호출해도 아무런 일도 벌어지지 않는다. 반면 visibility:hidden으로 처리된 요소는 렌더링 트리에 존재하므로 스크롤이 되는 것이다.

이와 비슷한 이유로 jQuery의 offset 함수는 display:none인 요소에 대해서는 동작하지 않는다. ".offset API"를 보면 다음과 같이 나와 있다.

Note: jQuery does not support getting the offset coordinates of hidden elements or accounting for borders, margins, or padding set on the body element.
While it is possible to get the coordinates of elements with visibility:hidden set, display:none is excluded from the rendering tree and thus has a position that is undefined.

렌더링 트리에서 제외되어 있기 때문에 요소가 실제 어느 위치에 있는지 알 수가 없는 것이 이유인 듯하다.

아무튼, 결론은 display:none인 요소는 렌더링 트리에 존재하지 않아 스크롤 되지 않는다.




댓글 없음: