whatisthis?

practice - css / js ) j쿼리 없이 아코디언메뉴 만들기 본문

PRACTICE/SELF

practice - css / js ) j쿼리 없이 아코디언메뉴 만들기

thisisyjin 2022. 3. 10. 12:13

Accordion Menu

(without jQuery)

 

- 보통 아코디언 메뉴는 j쿼리를 이용하는데,

j쿼리에는 slideToogle 등의 편리한 애니메이션 관련 메소드가 있기 때문!

 

BUT!

나는 제이쿼리는 되도록 쓰지 않고 싶은 1人 으로 .. (물론 지금 많은 페이지들이 제이쿼리를 사용하므로 배우는것은 필수라 생각)

바닐라 자바스크립트만을 사용해서 한번 구현해봐야겠다고 생각했다.

 

- css 를 기반으로 클릭이벤트마다 class 추가 / 삭제 

>> 이또한 j쿼리는 toogleClass 메소드로 if-else문 없이 깔끔하게 가능하지만 ..

 

 

 

- 펼치고 접는 +, - 아이콘을 flex만으로 구현 + 애니메이션(트렌지션) 🔻

 

index.html
0.00MB

 

 

📃 실습하면서 알게된 점 기록

📌 css :hover시 선택자
예> div:hover일때 h1을 달라지게함
<div> <h1> 이런식으로 부모자식관계면
div:hover h1 { /* style */ }
띄어쓰기 하기!

만약, 가상요소의 경우엔? .sel:hover.sel::after 붙여서 씀!

📌 js target / currentTarget 차이
currentTarget : 이벤트 리스너가 달린 요소
target : 실제 이벤트가 발생하는 요소
단, currentTarget은 IE 지원 안됨


📌 아직 이해안가는 것
+, - 버튼 .
.icon {
float: right;
width: 30px;
height: 30px;
/* 🔻 왜 이렇게 .. */
display: flex;
margin-top: -3px;
align-items: center;
justify-content: center;
}

📌 margin으로 요소 이동
여태 난 살짝 포지셔닝 안맞으면 position: relative로 top: -3px 뭐 이런식으로 옮겼었는데, (또는 transition)
margin: -3px 하면 위로 3px 올라간다. 신기하다.

 

 

 

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Accordion Menu</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <section class="wrapper">
      <div class="container">
        <ul class="accordion">
          <li class="item">
            <h2 class="accordion-title">menu 1 <span class="icon"></span></h2>
            <div class="text">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia
              officiis magnam doloribus optio laborum esse!
            </div>
          </li>
          <li class="item">
            <h2 class="accordion-title">menu 2 <span class="icon"></span></h2>
            <div class="text">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia
              officiis magnam doloribus optio laborum esse!
            </div>
          </li>
          <li class="item">
            <h2 class="accordion-title">menu 3 <span class="icon"></span></h2>
            <div class="text">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia
              officiis magnam doloribus optio laborum esse!
            </div>
          </li>
          <li class="item">
            <h2 class="accordion-title">menu 4 <span class="icon"></span></h2>
            <div class="text">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia
              officiis magnam doloribus optio laborum esse!
            </div>
          </li>
          <li class="item">
            <h2 class="accordion-title">menu 5 <span class="icon"></span></h2>
            <div class="text">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia
              officiis magnam doloribus optio laborum esse!
            </div>
          </li>
        </ul>
      </div>
    </section>
    <script src="app.js"></script>
  </body>
</html>

 

style.css

@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;700&display=swap");

* {
  box-sizing: border-box;
  margin: 0;
}

body {
  font-family: "Montserrat", sans-serif;
  background-color: #d8e2dc;
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  width: 100%;
  max-width: 500px;
  min-width: 300px;
  position: relative;
}

.container::before {
  content: "";
  position: absolute;
  width: 90%;
  height: 100%;
  margin: 0;
  top: 0;
  right: 0;
  background-color: transparent;
  box-shadow: 0px 20px 40px #52616b;
  opacity: 0.2;
  z-index: -1;
  transition: all 0.4s ease-in-out;
}

.container:hover.container::before {
  box-shadow: 0px 40px 90px #52616b;
}

ul {
  list-style: none;
  padding: 0;
  background-color: #fff;
  border-radius: 5px;
}

.accordion-title {
  position: relative;
  padding: 20px;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: #52616b;
  cursor: pointer;
  transition: padding-left 0.2s ease-in-out;
}

.accordion-title:hover {
  padding-left: 25px;
}

.accordion-title::before,
.accordion-title::after {
  content: "";
  position: absolute;
  height: 2px;
  border-radius: 50px;
  bottom: 0;
  left: 0;
  transition: all 0.6s ease-in-out;
}
.accordion-title::before {
  width: 100%;
  background-color: #c9d6df;
}

.accordion-title::after {
  background: linear-gradient(90deg, #52616b, #c9d6df);
  width: 0;
}

.accordion-title:hover.accordion-title::after {
  width: 100%;
}

.accordionTitleActive::after {
  content: "";
  position: absolute;
  height: 2px;
  border-radius: 50px;
  transition: all 0.6s ease-in-out;
  bottom: 0;
  left: 0;
  /* display: flex;
  justify-content: center;
  align-items: center; */
}

.accordionTitleActive:after {
  background-image: linear-gradient(90deg, #52616b, #c9d6df);
  width: 100%;
}

.icon {
  float: right;
  width: 30px;
  height: 30px;
  display: flex;
  /* margin-top: -3px; */
  align-items: center;
  justify-content: center;
}

.icon::before,
.icon::after {
  content: "";
  position: absolute;
  /* border-radius: 50px; */
  background-color: #c9d6df;
  transition: all 0.3s ease;
}

.icon::before {
  width: 2px;
  height: 20px;
}

.icon::after {
  width: 20px;
  height: 2px;
}

.accordion-title:hover .icon::before,
.accordion-title:hover .icon::after {
  background-color: #52616b;
}

.icon.anime.icon:before {
  transform: rotate(90deg);
}

.accordion .item .text {
  opacity: 0;
  height: 0;
  padding: 0 20px;
  position: relative;
  line-height: 24px;
  font-size: 16px;
  font-weight: 200;
  letter-spacing: 0.01em;
  transition: all 0.4s ease-out;
  overflow: hidden;
  background-color: #f0f5f9;
}

.accordion .item .text.show {
  opacity: 1;
  height: auto;
  padding: 25px 20px;
  position: relative;
  z-index: 0;
  border-radius: 0px 0px 3px 3px;
}

 

 

app.js

// Ref. @rafaelavlucas (https://codepen.io/rafaelavlucas/pen/zyVXYV)

// variables

const accordionBtn = document.querySelectorAll(".accordion-title");
const texts = document.querySelectorAll(".text");
const icons = document.querySelectorAll(".icon");

console.log(accordionBtn, texts, icons);
// 🔺 accordionBtn, texts, icons는 nodelist 이다. (5개 요소 담은 리스트. forEach로 다뤄야함.)

// event listener

accordionBtn.forEach(el => {
  el.addEventListener("click", toogleAccordian);
});

// function

function toogleAccordian(el) {
  // 🔺 콜백에서의 el은 위에 forEach의 el과 다르다.
  // forEach에서 el은 HTML element를 의미하는데, 여기서는 event객체이다.

  const target = el.currentTarget;
  const targetIcon = el.currentTarget.children[0];
  const targetText = el.currentTarget.nextElementSibling;
  console.log(targetText);
  // 🔺 target, targetIcon, targetText는 currentTarget
  // 즉, 클릭 이벤트가 일어난 accordionBtn과 그 sibling, children

  if (targetText.classList.contains("show")) {
    targetText.classList.remove("show");
    targetIcon.classList.remove("anime");
    target.classList.remove("accordionTitleActive");
  } else {
    accordionBtn.forEach(el => {
      el.classList.remove("accordionTitleActive");
      texts.forEach(el => {
        el.classList.remove("show");
      });
      icons.forEach(el => {
        el.classList.remove("anime");
      });
    });
    targetText.classList.add("show");

    target.classList.add("accordionTitleActive");
    targetIcon.classList.add("anime");
  }
}

 

 

result

 

 

 

GitHub - thisisyjin/css-practice: various CSS practice (self-prac-ex)

various CSS practice (self-prac-ex). Contribute to thisisyjin/css-practice development by creating an account on GitHub.

github.com

  • reference : @rafaelavlucas