CSS 최적화 관련하여 면접을 준비하는 과정에서 opacity는 reflow repaint 과정이 일어나는지의 물음에 대해 확신을 가지지 못해 테스트해보게 되었다.
내가 기억하기로는 opacity 속성은 repaint는 발생하지만 reflow는 발생하지 않는걸로 기억하고 있다. 그런데 다음과 같이 opacity 속성 값에 따라 reflow가 발생할수도 발생하지 않을수도 있다는 것을 볼 수 있었다.
opacity의 값이 1인 경우
.specialBox {
width: 300px;
height: 300px;
background-color: orange;
opacity: 1;
}
.specialBox--hidden {
opacity: 0;
}
렌더링 결과 Reflow가 발생하는 것을 확인 할 수 있다.
opacity의 값이 1이 아닌 경우
.specialBox {
width: 300px;
height: 300px;
background-color: orange;
opacity: 0.5;
}
.specialBox--hidden {
opacity: 0;
}
반면에 여기서는 Reflow가 발생하지 않는다.
실제로 CSS Animation이 렌더 단계에서 어떤 단계들을 trigger하는 보여주는 사이트에서도 Reflow가 발생하지 않는다고 되어있었다.
Changing opacity does not trigger any geometry changes, which is good
(크롬은 렌더링 엔진으로 Blink을 사용하기 때문에 B를 확인)
그렇다면 왜 값이 1일때랑 아닐때랑 진행되는 단계가 다른 것이 궁금해지기 시작했고 궁금증을 해결하기 위해 MDN을 다시 천천히 읽어보니 다음과 같은 내용이 적혀있었다 ㅎㅎ;;;
Using opacity with a value
other than 1
places the element in a newstacking context
.
opacity의 값으로 1이 아닌 다른 값을 사용하면 새로운 stacking context에 놓이게 된다는 사실이였다.
그렇다면 여기서 말하는 stacking context라는 것은 무엇일까?
stacking context은 가상의 z축을 따라 HTML 요소를 3차원으로 개념화한 것으로 각각의 HTML 요소는 자신들의 속성에 따라서 우선순위를 결정하여 3차원의 공간을 차지하게 된다.
가장 대표적인 사용법은 CSS의 z-index 값을 활용하는 것으로 아래와 같은 예시에서 스택 컨텍스트의 계층 구조는 다음과 같이 구성된다.
z-index외에도 stacking context를 생성하는 여러 방식이 존재하는데 그 중 하나가 opacity 값을 1이 아닌 다른 값을 사용하는 것이다.
그래서 위에서 테스트한 결과로 reflow가 발생하지 않았던 이유는 stacking context을 생성하면서 다시 렌더링하는 과정에서 계층이 구분되어 있어 layout을 고려할 필요가 없기 때문에 reflow가 발생하지 않았던 것이다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.specialBox {
width: 300px;
height: 300px;
background-color: orange;
opacity: 0.5; 또는 1;
}
.specialBox--hidden {
opacity: 0;
}
</style>
</head>
<body>
<div class="specialBox"></div>
<button class="toggle">On/Off 버튼</button>
<script>
(function () {
const toggleBtn = document.querySelector(".toggle");
toggleBtn.addEventListener("click", function () {
const specialBox = document.querySelector(".specialBox");
specialBox.classList.toggle("specialBox--hidden");
});
})();
</script>
</body>
</html>