position: sticky로 고정된 요소가 있는 레이아웃에서, 스크롤 후 특정 영역을 클릭했는데 아무 반응이 없는 경험을 한 적 있을 것이다. 클릭한 것 같은데 동작하지 않거나, 엉뚱한 곳의 이벤트가 발생하는 경우다.
원인은 이벤트 처리가 아니라 렌더링 순서에 있을 가능성이 높다.
브라우저는 DOM에 등장하는 순서대로 요소를 그린다. 나중에 오는 요소가 먼저 오는 요소 위에 쌓인다. z-index를 별도로 지정하지 않아도 그렇다.
<div class="container">
<div class="content">클릭하고 싶은 영역</div>
<div class="sticky-bar">고정 요소</div> <!-- 나중에 오므로 위에 쌓임 -->
</div>position: sticky가 적용된 요소는 스크롤에 따라 위치가 바뀌면서, 의도치 않게 다른 요소와 시각적으로 겹치는 구간이 생길 수 있다. 이 구간에서는 위에 쌓인 sticky 요소가 클릭을 가로챈다. 사용자 눈에는 아무것도 없는 것처럼 보이는 자리인데도.
stopPropagation클릭 이벤트가 부모 요소로 전파되지 않도록 막는다. 부모-자식 관계에서의 충돌에는 효과적이지만, 형제 요소가 위에서 덮고 있는 상황은 해결하지 못한다. 이벤트 자체가 아예 아래 요소에 도달하지 않기 때문이다.
z-index 올리기css
.content {
position: relative;
z-index: 1;
}겹치는 구간에서 클릭을 살릴 수는 있다. 하지만 z-index는 주변 요소에 연쇄적으로 영향을 줄 수 있어서, 다른 레이어 구조가 무너지거나 예상치 못한 부작용이 생기기 쉽다.
flex 레이아웃을 사용하고 있다면 order 속성을 활용할 수 있다. 화면에 보이는 순서는 그대로 두고, DOM 안에서의 순서만 바꾸는 것이다.
sticky 요소를 DOM에서 먼저 두면, 브라우저 렌더링 기준으로 콘텐츠가 나중에 그려져 위에 쌓힌다. order로 시각적 위치만 원래대로 되돌리면 된다.
<div class="container">
<div class="sticky-bar"></div>
<div class="content"></div>
</div>.container {
display: flex;
}
.sticky-bar {
order: 2;
flex-shrink: 0;
}
.content {
order: 1;
min-width: 0; /* flex 환경에서 콘텐츠 영역 축소 방지 */
}function Layout() {
return (
<div style={{ display: 'flex' }}>
{/* DOM에서 sticky 요소를 먼저 두되, order로 시각적 위치 조정 */}
<StickyBar style={{ order: 2, flexShrink: 0 }} />
{/* 콘텐츠는 DOM에서 나중에 오므로 위에 쌓임 */}
<Content style={{ order: 1, minWidth: 0 }} />
</div>
);
}const Container = styled.div`
display: flex;
`;
const StickyBar = styled.div`
order: 2;
flex-shrink: 0;
`;
const Content = styled.div`
order: 1;
min-width: 0;
`;
function Layout() {
return (
<Container>
<StickyBar />
<Content />
</Container>
);
}function Layout() {
return (
<div className="flex">
<div className="order-2 shrink-0">{/* StickyBar */}</div>
<div className="order-1 min-w-0">{/* Content */}</div>
</div>
);
}sticky 요소가 숨겨지거나 접히는 상태가 있다면, 투명해진 레이어가 여전히 클릭을 막을 수 있다. 이때는 pointer-events: none으로 명시적으로 비활성화한다.
.sticky-bar.hidden {
pointer-events: none;
}방법 | 언제 유효한가 | 한계 |
|---|---|---|
| 부모-자식 이벤트 충돌 | 형제 요소가 위에 덮인 경우엔 무효 |
콘텐츠에 | 빠른 임시 해결 | 다른 레이어에 연쇄 영향 가능 |
DOM 순서 변경 + | flex 레이아웃 환경 | flex가 아닌 레이아웃엔 적용 불가 |
sticky 요소와 콘텐츠가 겹쳐서 클릭이 막힌다면,
z-index보다 DOM 순서를 바꾸고order로 시각 순서를 유지하는 것이 더 안전한 해결책이다.