[JavaScript30] Day5: Flex Panels Image Gallery

sunshani·2023년 12월 4일
0
post-thumbnail

무엇을 만들 것인가.

각각의 사진 패널을 클릭하면 해당 요소가 커지면서 위아래로 메세지가 까꿍~ 하고 나타났다가 다시 클릭하면 작아지면서 쏙 들어가는 웹페이지 구현. 위 GIF 참고.

코드를 살펴보자.

HTML

  <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>

CSS

전부 가져오기엔 너무 길어서 살펴볼 부분만 추려서 가져왔다.

.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;
}

JavaScript

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를 사용하면 그런 작업들을 특정 태그에서만 할 수 있도록 설정할 수도 있다. 여기에 보기 쉽게 설명되어 있어서 나중에 헷갈리면 또 참고해서 봐야겠다.

transitiontransform

transition 속성

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 속성과 함께 사용하는 경우가 많다고 한다.

어떻게 써야 하는지는 여기에 잘 나와있으니 나중에 참고해보자.

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과 마찬가지로 단독으로는 효과를 제공하지 않아 쓸모가 없고 다른 것들과 적절히 조합했을 때 그 효과가 나타난다.

어떻게 써야 하는지는 여기에 잘 나와있으니 나중에 참고해보자.

forEach() 메소드 사용하여 이벤트 등록하기

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으로 파라미터를 넣어줄 때 참고할만한 내용이 있어 추가로.. 적는다. (여기 참고))

classList 사용시에는 클래스명만 쓴다

function toggleOpen() {
    this.classList.toggle("open");
  }

영상을 따라 코드를 다 작성하고나서 풀리지 않던 문제가 있었다. 패널을 클릭을 할 때 toggelOpen 함수가 실행이 돼서 개발자도구 상으로는 open이라는 클래스명이 생겼다 없어졌다 잘 토글이 되고 아무런 오류도 없는데 실제로 화면 상에는 구현이 되지 않는 문제였다.

예시 코드랑 몇 번이고 비교를 해보아도 다른 부분이 없는 줄 알았는데... 알고보니

function toggleOpen() {
    this.classList.toggle(".open");
  }

이렇게 클래스명 앞에 .을 붙였던 것. 점을 붙이니 바로 해결이 되었다.

classList 프로퍼티 메소드를 사용할 때는 클래스명만 쓰자.

참고로! Flexbox Froggy 🐸

오늘은 flex가 주된 내용이었으니 추가하지 않을 수 없는 부분. CSS 배우기 시작할 즈음에 이전 팀원분께서 공유해주신 Flexbox Froggy라는 교육용 게임인데 flexbox와 친해질 수 있는 그런 귀엽고 유익한 게임이다. 난 다 깸 ✌️

무엇을 느꼈나.

  • 생각보다 CSS를 사용해서 구현할 수 있는 것들이 많은 것 같다. transition과 transform은 자유자재로 사용하고 싶다. 나중엔 keyframe으로 애니메이션 주는 것도 해봐야지.
  • 공부를 하다보면 '그런가보다'하고 넘어가는 부분이 왕왕 있는데 오늘처럼 한번씩은 제대로 짚고 넘어가자. '그런가보다' 리스트를 어디 만들어 둬야겠다 ㅋㅋ

참고

profile
日新又日新 ☀️ 😎

0개의 댓글