각각의 사진 패널을 클릭하면 해당 요소가 커지면서 위아래로 메세지가 까꿍~ 하고 나타났다가 다시 클릭하면 작아지면서 쏙 들어가는 웹페이지 구현. 위 GIF 참고.
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
전부 가져오기엔 너무 길어서 살펴볼 부분만 추려서 가져왔다.
.panels {
display: flex;
}
.panel {
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s;
flex: 1;
justify-content: center;
display: flex;
flex-direction: column;
}
.panel > * {
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.panel > *:first-child {
transform: translateY(-100%);
}
.panel.open-active > *:first-child {
transform: translateY(0);
}
.panel > *:last-child {
transform: translateY(100%);
}
.panel.open-active > *:last-child {
transform: translateY(0);
}
.panel.open {
flex: 5;
font-size: 40px;
}
const panels = document.querySelectorAll(".panel");
function toggleOpen() {
this.classList.toggle("open");
}
function toggleActive(e) {
console.log(e.propertyName);
if (e.propertyName.includes("flex")) {
this.classList.toggle("open-active");
}
}
panels.forEach((panel) =>
panel.addEventListener("click", toggleOpen)
);
panels.forEach((panel) =>
panel.addEventListener("transitionend", toggleActive)
);
:first-child
와 :last-child
.panel > *:first-child {
transform: translateY(-100%);
}
.panel > *:last-child {
transform: translateY(100%);
}
이렇게 함으로써 .panel
이라는 클래스를 가지는 전체(*
) 요소 중 첫 번째 자식 요소와 마지막 자식 요소에만 주고 싶은 속성을 주었다.
:first-child
와 :last-child
는 각각 부모 요소의 첫 번째 자식 요소와 마지막 요소를 가리키는 pseudo class 선택자이다. psuedo class 선택자는 앞에 콜론(:
)을 사용한다. 특정 요소를 선택하는 pseudo class 선택자들은 이 두 가지 말고도 더 있는데 :nth-child()
이 대표적이다.
찾아보니 잘만 활용하면 구체적으로 몇 번째 요소부터 선택 몇 개마다 혹은 몇 개씩 선택하고 싶은지까지도 정할 수 있다. 심지어 :nth-of-type
를 사용하면 그런 작업들을 특정 태그에서만 할 수 있도록 설정할 수도 있다. 여기에 보기 쉽게 설명되어 있어서 나중에 헷갈리면 또 참고해서 봐야겠다.
transition
과 transform
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s;
뾰왕~!하면서(?) 열리고 닫히는 그 효과를 담당하는 부분이다. (맨 위 이미지 참고) Day2 때 시계 만들면서도 등장했던 transition
속성.
말 그대로 트랜지션 효과를 주는 속성이다. 영상 편집할 때 컷과 컷 사이에 아무런 트랜지션 효과를 넣지 않으면 툭툭 끊기듯이 CSS도 마찬가지다. 예를 들어서 :hover
시에는 박스가 커져야 하는데 갑자기 툭. 커지면 좀 이상하니까 자연스럽게 트랜지션을 주는 거다.
참고로 transition
속성은 그 자체로는 쓸모가 없고 pseudo class 선택자나 자바스크립트로 부수적인 액션이 있어야 쓸모 있어진다. 이것도 영상 편집할 때 트랜지션 생각하면 됨. 컷과 컷이 있어야 얘네가 중간에서 잘 이어주는 거.
이어서 살펴볼 transform
속성과 함께 사용하는 경우가 많다고 한다.
어떻게 써야 하는지는 여기에 잘 나와있으니 나중에 참고해보자.
.panel > *:first-child {
transform: translateY(-100%);
}
.panel.open-active > *:first-child {
transform: translateY(0);
}
.panel > *:last-child {
transform: translateY(100%);
}
.panel.open-active > *:last-child {
transform: translateY(0);
}
크롬 개발자 도구에서 transform
속성을 껐다 켰다 하면 위와 같이 동작한다. 위로 갔다 아래로 갔다~ 실제로 이런 부분을 담당하는 것이 tranform
이다.
좀 더 정확하고 간단하게 얘기하자면, 요소에 이동(translate), 회전(rotate), 확대 및 축소(scale), 비틀기(skew) 효과를 넣어주는 속성이다. 참고로 이것도 transition
과 마찬가지로 단독으로는 효과를 제공하지 않아 쓸모가 없고 다른 것들과 적절히 조합했을 때 그 효과가 나타난다.
어떻게 써야 하는지는 여기에 잘 나와있으니 나중에 참고해보자.
const panels = document.querySelectorAll(".panel");
panels.forEach((panel) =>
panel.addEventListener("click", toggleOpen)
);
panels.forEach((panel) =>
panel.addEventListener("transitionend", toggleActive)
);
.panel
클래스는 모든 요소가 공통적으로 가지고 있는 클래스다. 이 요소들을 panels로 저장을 한 다음 forEach
로 하나씩 하나씩 이벤트를 등록하고 있다.
panels.forEach((panel) =>
panel.addEventListener("click", toggleOpen)
);
이벤트 등록 시 함수명에는 괄호를 쓰지 않는다. 지금까지 그냥 그런가보다 하고 넘어간 부분이었지만 함수명에 괄호가 있고 없고의 차이를 이참에 확실히 짚고 넘어가자면,
toggleOpen()
: toggelOpen 함수 호출하여 실행toggleOpen
: 함수를 가리키는 변수둘의 차이는 이것이다. 실제로 호출하느냐, 아니면 가리키기만 하느냐.
이벤트 핸들러 등록을 하는 것은 기본적으로 직접 함수를 호출하는게 아니라 브라우저에게 함수 호출을 하도록 만드는 것이기 때문에 'toggleOpen
함수를 찾아서 실행해줘라'하고 이름을 전달해주는 것이다. 그러니 괄호를 사용하지 않는다. 오케이~?
(지금 하는 과제는 바닐라JS로 구현하는 과제기는 하지만 React에서 onClick으로 파라미터를 넣어줄 때 참고할만한 내용이 있어 추가로.. 적는다. (여기 참고))
function toggleOpen() {
this.classList.toggle("open");
}
영상을 따라 코드를 다 작성하고나서 풀리지 않던 문제가 있었다. 패널을 클릭을 할 때 toggelOpen
함수가 실행이 돼서 개발자도구 상으로는 open이라는 클래스명이 생겼다 없어졌다 잘 토글이 되고 아무런 오류도 없는데 실제로 화면 상에는 구현이 되지 않는 문제였다.
예시 코드랑 몇 번이고 비교를 해보아도 다른 부분이 없는 줄 알았는데... 알고보니
function toggleOpen() {
this.classList.toggle(".open");
}
이렇게 클래스명 앞에 .
을 붙였던 것. 점을 붙이니 바로 해결이 되었다.
classList 프로퍼티 메소드를 사용할 때는 클래스명만 쓰자.
오늘은 flex가 주된 내용이었으니 추가하지 않을 수 없는 부분. CSS 배우기 시작할 즈음에 이전 팀원분께서 공유해주신 Flexbox Froggy라는 교육용 게임인데 flexbox와 친해질 수 있는 그런 귀엽고 유익한 게임이다. 난 다 깸 ✌️