CSS/공용 ui
[css, 공용 ui, js] max-height, max-width로 접었다 피는 폴딩 ui 반응형 만들기(% 단위를 사용하지 못할 때)
tea-tea
2024. 9. 21. 21:27
목적
폴딩 ui는 css의 transtion과 max-height, max-width 속성을 이용해서 아주 간단하게 만들 수 있다.
문제는 화면의 크기가 가변적인 상황에서 반응형 ui로 만들때인데,
ui가 펴지는 상황에서 max-height와 max-width에 퍼센티지(%)를 사용할 수 없는 경우가 있다.
이럴 때에는 펴지는 상황의 예상크기를 js로 계산하여 직접 스타일을 주어야 하는데, 그럴 때 사용하는 ui다.
원리
이 폴딩 ui는 최초에 접힌 상태로, max-width와 max-height가 0으로 설정되어 있다.
우선, 최초에 document가 로드된 시점에 max-width, max-height 제한을 풀어서 ui의 원래 크기로 되돌린다.
그리고 이 크기를 변수로 저장한 후 다시 접힌 상태로 스타일을 되돌린다. (이 과정은 아주 빨라서 제한을 푼 펴진 상태가 되기 전에 끝난다. 즉, 리플로우가 일어나지 않고 깜박임 현상도 없다.)
이제 유저가 ui를 펴는 이벤트를 발생시키면 저장했던 예상 크기의 변수로 스타일을 설정해주면 된다.
소스코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.app {
border: 1px solid black;
overflow: hidden;
transition: all 0.25s ease-in-out;
}
.close {
max-width: 0;
max-height: 0;
}
</style>
</head>
<body>
<button class="toggle">toggle</button>
<div class="app close">
<div>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Asperiores
explicabo numquam at error, incidunt, sunt doloremque enim facere
similique atque odit! Laudantium, voluptatum minus a dicta officiis
cumque facere rerum.
<br />
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Asperiores
explicabo numquam at error, incidunt, sunt doloremque enim facere
similique atque odit! Laudantium, voluptatum minus a dicta officiis
cumque facere rerum.
</div>
</div>
<script>
(function () {
const dom = {
toggle: document.querySelector(".toggle"),
app: document.querySelector(".app"),
};
class expectDomStyle {
dom = {
target: undefined,
style: {
fold: {
maxWidth: undefined,
maxHeight: undefined,
},
unfold: {
maxWidth: undefined,
maxHeight: undefined,
},
},
};
constructor(dom) {
this.dom.target = dom;
this.calcExpectStyle();
}
/// dom을 fold했을 때 예상 크기와 unfold했을 때 예상 크기를 상태변수로 저장
calcExpectStyle() {
this.dom.style.fold.maxWidth = this.dom.target.style.maxWidth || "";
this.dom.style.fold.maxHeight =
this.dom.target.style.maxHeight || "";
this.dom.target.style.maxWidth = "none";
this.dom.target.style.maxHeight = "none";
this.dom.style.unfold.maxWidth = this.dom.target.scrollWidth;
this.dom.style.unfold.maxHeight = this.dom.target.scrollHeight;
this.dom.target.style.maxWidth = "";
this.dom.target.style.maxHeight = "";
}
// dom에 unfold 시 예상크기를 style 세팅
setExpectStyle() {
this.dom.target.style.maxWidth =
this.dom.style.unfold.maxWidth + "px";
this.dom.target.style.maxHeight =
this.dom.style.unfold.maxHeight + "px";
}
// dom을 fold
// style 속성 변경
clearExpentStyle() {
this.dom.target.style.maxWidth = this.dom.style.fold.maxWidth;
this.dom.target.style.maxHeight = this.dom.style.fold.maxHeight;
}
}
const appExpectDomStyle = new expectDomStyle(dom.app);
dom.toggle.addEventListener("click", () => {
if (dom.app.classList.contains("open")) {
appExpectDomStyle.clearExpentStyle();
} else {
appExpectDomStyle.setExpectStyle();
}
dom.app.classList.toggle("open");
});
})();
</script>
</body>
</html>