z-index가 동작하지 않는 경우
popover 등의 컴포넌트를 개발하다 보면 특정 html 엘리먼트를 다른 엘리먼트 위에 표시하고 싶을 때가 있다.
그럴 때 간편하게 사용할 수 있는 것이 css의 z-index 속성이다.
z-index
https://developer.mozilla.org/ko/docs/Web/CSS/z-index
z-index를 지정함으로써 두 가지 항목을 지정할 수 있다.
- 현재 쌓임 맥락에서 자신의 위치
- 자신에 대한 쌓임 맥락 생성 여부
값 지정
z-index 속성은 auto
키워드 또는 정수 값을 부여함으로써 지정할 수 있다.
auto
로 지정할 경우, 해당 박스가 새로운 쌓임 맥락을 생성하지 않는다.- 즉 현재 쌓임 맥락에서의 위치는 부모 엘리먼트와 동일하다 (위에 표시되지 않는다)
- 값을 정수로 지정할 경우, 현재 쌓임 맥락에서의 위치로 해당 값을 사용한다.
- 또한 자신 만의 쌓임 맥락을 생성하고, 해당 맥락에서 자신의 위치를 0으로 설정한다.
- 이는 현재 엘리먼트의 자식 엘리먼트들의 z-index를 자신 외의 바깥 요소와 비교하여 영향을 미치는 일을 없애기 위해서이다.
보다 복잡한 쌓임 맥락에 대한 이해를 위해서는 아래 공식 문서를 참고해보면 좋다. https://developer.mozilla.org/ko/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index
z-index가 동작하지 않는 몇 가지 이유들
https://coder-coder.com/z-index-isnt-working/
앞서 간단하게 언급만 하고 지나갔던 쌓임 맥락이 생각보다 복잡하게 동작하기 때문에, z-index를 999999와 같은 숫자로 설정한다고 항상 엘리먼트가 최상단에 위치하는 것은 아니다.
1. 동일한 쌓임 맥락에 있는 요소들은 appearance 순서에 영향을 받는다.
z-index 속성을 지정하지 않을 경우에는 웹 페이지의 natural한 쌓임 순서를 따른다. 즉 엘리먼트들의 z-index가 지정되지 않은 경우 엘리먼트들의 appearance 순서에 따라 쌓임 순서를 가지게 된다.
마크업 상 나중에 등장하게 되는 엘리먼트들은 이전에 등장했던 엘리먼트보다 순서 상 위에 위치하게 된다.
2. 엘리먼트들의 position 설정 여부
css position 속성이 정해져 있는지 여부에 따라 쌓임 순서가 정해지기도 한다.
static
position 값 보다는 relative
, absolute
와 같은 값으로 설정하는 것이 엘리먼트들의 위치를 고정시키기에는 더 좋다.
position이 정해진 엘리먼트들은 항상 정해져 있지 않은 엘리먼트보다 더 위에 보이게 된다.
때문에 하나의 엘리먼트를 position: relative
로 설정해 놓고, 나머지 엘리먼트들은 unpositioned 상태로 두게 한다면 relative
한 엘리먼트가 다른 엘리먼트보다 위에 보이게 된다.
3. opacity나 transform 적용 여부
흔히 하는 실수 중 하나가, transform
이나 opacity
는 단일 엘리먼트에 대한 속성이기 때문에 쌓임 순서에 영향을 미치지 않을 것이라는 것이다.
하지만 transform
이나 opacity
를 설정하는 것은 엘리먼트들을 새로운 쌓임 맥락에 집어넣겠다는 의미와도 같다.
즉 특정 엘리먼트에 transform
속성을 지정한다는 것은 z-index
나 position
을 지정하지 않았음에도 불구하고 z-index
를 0으로 지정한다는 의미와 동일하다.
해결 방법
그렇다면 popover나 툴팁과 같은 엘리먼트들은 항상 최상단에 위치해야 할텐데, 어떻게 하면 좋을까?
쌓임 맥락간의 간섭을 사전에 방지하고 올바른 z-index
설정을 먹이고 싶다면 최대한 동일한 컨텍스트 안에서 z-index 속성을 설정하는 것을 권고한다.
만약 한 엘리먼트가 position: relative
속성이 설정되어 있고, 다른 엘리먼트는 unpositioned 상태에서 z-index 설정을 해야 한다면 나머지 엘리먼트들도 position: relative
속성을 추가해줘야 한다는 의미이다.
4. 낮은 쌓임 맥락에 있는 엘리먼트는 부모의 z-index 레벨을 따른다
웹 페이지 본문에서 모달 창을 띄우는데, footer나 header가 더 위로 위치하게 되어서 배경이 딤드되지 않는 경험을 겪어본 사람들이 적지는 않을 것이라 예상해본다.
자식 엘리먼트들은 부모의 엘리먼트들의 쌓임 맥락에 제한 받기 때문이다.
예를 들어..
<section class="content">
<div class="modal"></div>
</section>
<div class="side-tab"></div>
위와 같은 마크업이 있다고 가정해보자.
- 컨텐츠와 사이드 탭 엘리먼트들은 친척 관계이다 (depth가 동일)
- 즉 마크업 상에서 동일한 레벨에 위치해 있고, 이는 z-index level과는 다르다.
- 모달이 컨텐츠 엘리먼트 안에 위치해 있기 때문에, 모달에 적용된 z-index 속성은 부모 엘리먼트 컨텍스트 안에서만 의미 있고 content 엘리먼트에는 영향을 끼치지 않는다.
- 즉 content 다른 자식 엘리먼트들이 있었다면 모달은 그 엘리먼트 위에 보이게 된다.
- 하지만 부모 엘리먼트 바깥에 위치한 side-tab에는 영향을 전혀 미치지 못하므로, 딤드되는 듯한 효과를 줄 수 없다 (z-index) 속성이 적용되지 않는다
해결 방법
모달과 같이 컨텐츠 컨텍스트 안에서 논리적으로 가장 위에 위치해 있어야 하는 엘리먼트들은 마크업의 최상위 레벨로 이동시키면 된다.
즉 z-index 레벨은 최상위 컨텍스트의 엘리먼트들끼리 비교 연산을 거치게 되고, 다른 엘리먼트들에 비해 z-index가 높다면 해당 엘리먼트는 무조건 최상위에 위치하게 된다. (올바르게 설정했다면)
또 다른 해결 방법으로는, 컨텐츠에서 position 속성을 제거해서 모달의 z-index 속성을 제한하지 않도록 하는 방법도 있다.
- 부모 엘리먼트의 position 속성이 지정되어 있지 않다면 자식 엘리먼트의 z-index 속성은 한 단계 상위의 엘리먼트에 적용된다.
- 하지만 컨텐츠 엘리먼트의 position을 다시 지정해야 할 일이 생긴다면 모달의 순서가 다시 돌아갈 것이기 때문이다.
요약
만약 z-index 속성이 예상처럼 잘 먹히지 않는다면,
- 엘리먼트들의 position 속성을 확인하고, z-index 속성이 올바른 순서로 지정되었는지 확인해야 한다.
- 부모 엘리먼트들이 자식 엘리먼트들의 z-index 속성을 제한하는 일이 없도록 해야 한다.
즉 popover 라이브러리 등을 사용해서 popover를 개발했는데, 만약 최상단에 위치해야 할 gnb나 lnb보다 위에 위치하게 되어서 이를 가린다면
- popover의 쌓임 맥락이 다른 엘리먼트들과 비교하였을 때 적절한 hierarchy에 위치해 있는지 확인한다. (자식 엘리먼트의 쌓임 맥락은 부모 엘리먼트의 쌓임 맥락 내에서 적용되기 때문이다)
- 동일한 쌓임 맥락의 엘리먼트들의 position 속성을 조정한다.