사이드 탭을 여는 경우 다른 탭이 열릴 때 완전히 닫히기 - java script

조회수 796회

사이드 탭을 만드는 중인데요. 기본적인 화면에는 얇고 긴 사각형 안에 아이콘만 들어있고, 해당하는 아이콘을 누르는 경우 그 탭이 확장되어 열리는 방식입니다. 각각의 탭을 누르고 닫는 것에는 성공했지만, 여러 탭을 동시에 누르는 경우, 예를 들어 가->나->다 의 순서로 열었다면 역순인 다->나->가 로 닫았을 때 문제는 없지만, '나'를 먼저 닫거나 '가'를 닫게 되면 오류가 발생해 탭이 열리지 않습니다. 제 생각에는 다른 탭을 눌렀을 때 기존의 탭이 완전히 닫히지 않아 발생하는 문제인 것 같지만 해결할 방도를 찾지 못하고 이렇게 질문합니다. 어떻게 if 문, 혹은 다른 함수를 구성해야 원하는 방법대로 가능할까요 ? 아래에는 js 코드 입니다. 수정) html 과 css 코드도 첨부합니다 !! 감사합니다 !

let sidebar = document.querySelector(".sidebar");
let project = document.querySelector(".project");
let friends = document.querySelector(".friends");
let todo = document.querySelector(".todo");
let calendar = document.querySelector(".calendar");
let closeBtn = document.querySelector("#btn");
let projectBtn = document.querySelector(".bx-grid-alt");
let friendsBtn = document.querySelector(".bx-user");
let todoBtn = document.querySelector(".bx-chat");
let calendarBtn = document.querySelector(".bx-folder");


closeBtn.addEventListener("click", ()=>{
    sidebar.classList.toggle("open");
    menuBtnChange();//calling the function(optional)
});

projectBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    project.classList.toggle("open");
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

friendsBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    friends.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

todoBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    todo.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

calendarBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    calendar.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }

    menuBtnChange(); //calling the function(optional)
});

// following are the code to change sidebar button(optional)
function menuBtnChange() {
   if(sidebar.classList.contains("open")){
     closeBtn.classList.replace("bx-menu", "bx-menu-alt-right");//replacing the iocns class
   }else {
     closeBtn.classList.replace("bx-menu-alt-right","bx-menu");//replacing the iocns class
   }
}
<!DOCTYPE html>
<!-- Created by CodingLab |www.youtube.com/CodingLabYT-->
<html lang="ko" dir="ltr">

<head>
  <meta charset="UTF-8">
  <!--<title> Responsive Sidebar Menu  | CodingLab </title>-->
  <link rel="stylesheet" href="stylesss.css">
  <!-- Boxicons CDN Link -->
  <script src="https://kit.fontawesome.com/29ee0bc57e.js" crossorigin="anonymous"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <link href='https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css' rel='stylesheet'>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ToasTooL</title>
</head>

<body>
  <div class="sidebar">
    <div class="logo-details">
      <i class='bx bx-menu' id="btn"></i>
    </div>
    <ul class="nav-list">
      <li>
        <nav class="project"></nav>
        <a href="#">
          <i class='bx bx-grid-alt'></i>
        </a>
        <span class="tooltip">Project</span>
      </li>
      <li>
        <nav class="friends"></nav>
        <a href="#">
          <i class='bx bx-user'></i>
        </a>
        <span class="tooltip">Friends</span>
      </li>
      <li>
        <nav class="todo">
          <div id="tabwrap">
            <button class="menu1 on">Project</button>
            <button class="menu2">Personal</button>
            <div class="board1" -ms-overflow-style: none;>
              <header>
                <input type="text" id="myInput1" placeholder="Tings to be done in the project..." autocomplete="off" />
                <span class="addBtn1" id="add_button1">+</span>
              </header>
              <ul class="ful" id="myUL1"></ul>
              <script script src="script.js"></script>
            </div>
            <div class="board2">
              <header>
                <input type="text" id="myInput2" placeholder="Tings to be done in personal..." autocomplete="off" />
                <span class="addBtn2" id="add_button2">+</span>
              </header>
              <ul id="myUL2"></ul>
              <script script src="scriptt.js"></script>
            </div>
          </div>
        </nav>
        <a href="#">
          <i class='bx bx-chat'></i>
        </a>
        <span class="tooltip">Todo</span>
      </li>
      <li>
        <nav class="calendar"></nav>
        <a href="#">
          <i class='bx bx-folder'></i>
        </a>
        <span class="tooltip">Calendar</span>
      </li>
      <li class="profile">
        <div class="profile-details">
          <img src="profile.jpg" alt="profileImg">
          <div class="name_job">
            <div class="name">Prem Shahi</div>
            <div class="job">Web designer</div>
          </div>
        </div>
        <i class='bx bx-log-out' id="log_out"></i>
      </li>
    </ul>
  </div>
  <section class="home-section">
    <div class="text">Dashboard</div>
  </section>
  <iframe src="www.wikipedia.org"></iframe>
  <script script src="scripttt.js"></script>
</body>

</html>
let sidebar = document.querySelector(".sidebar");
let project = document.querySelector(".project");
let friends = document.querySelector(".friends");
let todo = document.querySelector(".todo");
let calendar = document.querySelector(".calendar");
let closeBtn = document.querySelector("#btn");
let projectBtn = document.querySelector(".bx-grid-alt");
let friendsBtn = document.querySelector(".bx-user");
let todoBtn = document.querySelector(".bx-chat");
let calendarBtn = document.querySelector(".bx-folder");


closeBtn.addEventListener("click", ()=>{
    sidebar.classList.toggle("open");
    menuBtnChange();//calling the function(optional)
});

projectBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    project.classList.toggle("open");
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

friendsBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    friends.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

todoBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    todo.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(calendar.classList.contains("open")){
        calendar.classList.toggle("close");
    }
    menuBtnChange(); //calling the function(optional)
});

calendarBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
    calendar.classList.toggle("open");
    if(project.classList.contains("open")){
        project.classList.toggle("close");
    }
    if(friends.classList.contains("open")){
        friends.classList.toggle("close");
    }
    if(todo.classList.contains("open")){
        todo.classList.toggle("close");
    }

    menuBtnChange(); //calling the function(optional)
});

// following are the code to change sidebar button(optional)
function menuBtnChange() {
   if(sidebar.classList.contains("open")){
     closeBtn.classList.replace("bx-menu", "bx-menu-alt-right");//replacing the iocns class
   }else {
     closeBtn.classList.replace("bx-menu-alt-right","bx-menu");//replacing the iocns class
   }
}

  • (•́ ✖ •̀)
    알 수 없는 사용자

2 답변

  • HTML, CSS 요청드렸는데 늦게 답변드린점 죄송해요 (회사에서 할거없을때 답변다는 편이라... ㅎㅎ;;)

    일단 문제 발생원인은 메뉴클릭시 DOM Element 들의 class 변경상태들을 보면 나오는데요 순서대로 클릭하지 않을시에 open close 가 같이 class 에 들어가면서 되는거라서.. 확실히 open / close 하나의 클래스만 들어가게끔 해주시면 되요

    projectBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
        if(project.classList.contains("close")){
            project.classList.toggle('close');
        }
        project.classList.toggle("open");
        if(friends.classList.contains("open")){
            friends.classList.toggle('open');
            friends.classList.toggle("close");
        }
        if(todo.classList.contains("open")){
            todo.classList.toggle('open');
            todo.classList.toggle("close");
        }
        if(calendar.classList.contains("open")){
            calendar.classList.toggle('open');
            calendar.classList.toggle("close");
        }
        menuBtnChange(); //calling the function(optional)
    });
    
    friendsBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
        if(friends.classList.contains("close")){
            friends.classList.toggle('close');
        }
        friends.classList.toggle("open");
        if(project.classList.contains("open")){
            project.classList.toggle('open');
            project.classList.toggle("close");
        }
        if(todo.classList.contains("open")){
            todo.classList.toggle('open');
            todo.classList.toggle("close");
        }
        if(calendar.classList.contains("open")){
            calendar.classList.toggle('open');
            calendar.classList.toggle("close");
        }
        menuBtnChange(); //calling the function(optional)
    });
    
    todoBtn.addEventListener("click", ()=>{ // Sidebar open when you click on the search iocn
        if(todo.classList.contains("close")){
            todo.classList.toggle('close');
        }
        todo.classList.toggle("open");
        if(project.classList.contains("open")){
            project.classList.toggle('open');
            project.classList.toggle("close");
        }
        if(friends.classList.contains("open")){
            friends.classList.toggle('open');
            friends.classList.toggle("close");
        }
        if(calendar.classList.contains("open")){
            calendar.classList.toggle('open');
            calendar.classList.toggle("close");
        }
        menuBtnChange(); //calling the function(optional)
    });
    

    위 코드는 비효율적이고 버그 수정이나 기능 수정시 시간이 오래걸려 실제 적용방식은 아래와 같이 해주시면 될거 같습니다.

    /* 상동 */
    var q = new Array(project, friends, todo, calendar);
    var e = new Array(projectBtn, friendsBtn, todoBtn, calendarBtn);
    
    for (i in e) {
      e[i].addEventListener("click", function() { menuBtnChange(this) });
    }
    
    function menuBtnChange(w) {
      for(i in e){
        if (w === e[i]) {
          if (q[i].classList.contains('close')) q[i].classList.remove('close');
          q[i].classList.add('open');
        } else {
          if (q[i].classList.contains('open')){
            q[i].classList.remove('open');
            q[i].classList.add('close');
          }
        }
      }
    
       if(sidebar.classList.contains("open")){
         closeBtn.classList.replace("bx-menu", "bx-menu-alt-right");//replacing the iocns class
       }else {
         closeBtn.classList.replace("bx-menu-alt-right","bx-menu");//replacing the iocns class
       }
    }
    
    • 헉 !! 아코디언 형식을 찾아보려니 가로형은 잘 없고, 그마저도 무조건 하나의 섹션은 띄워져 있어야 해서 고전 중이었는데, 덕분에 진행도를 확 높일 수 있을 것 같습니다 !!!! 하단의 코드를 사용하려니 나와 있는 섹션이 다시 들어가질 않아서 제가 그 부분만 수정하면 정말로 제가 생각했던 그대로의 UI가 완성되네요 ! 늦게라도 잊지 않으시고 친절하고 자세한 답변 달아주신 점 정말 감사합니다. 정말 복 받으실 거예요 😊 알 수 없는 사용자 2021.10.21 19:27
    • 아뇨. 도움이 되었다니 감사합니다 :D 김호원 2021.10.22 09:32
  • 데모를 보니 생각보다 본격적인 작업이어서 제 수준에서는 뭐라고 함부로 말 얹기는 어렵지만... 저는 UI 구현하다가 막히면 부트스트랩 등 각종 프레임워크의 구현체와 jquery 플러그인들을 살펴보는 편인데요, 탭은 보통 다음과 같이 구현들을 하더군요.

    1. .active 클래스가 붙은 것은 표시되고, 안 붙은 것은 안 표시되도록 CSS를 작성합니다.
    2. 언제 무엇이 .active 클래스를 갖는지 통제하는 JS 스크립트를 작성합니다.

    그리고 이 통제를 어떻게 작성하느냐에 따라서 그건 탭이 되기도 하고 아코디언이 되기도 하고 천차만별이 되지요. 그리고 제 생각에 질문자님이 구현하려고 하시는 것은 아코디언에 가깝습니다. 펼쳐질 수 있는 것이 최소 0개 최대 1개이기 때문이죠.

    부트스트랩의 경우, 제 경험으로 배운 것이 맞다면, 아코디언은 다음과 같이 통제를 합니다.

    1. 일단은 전부 .active를 없앤다.
    2. 탭을 하나 클릭했을 때, 그 탭에 딸린 패널("pane"이라는 어려운 영단어로 부름")의 .active를 토글한다.
    3. 다른 패널은 묻지도 따지지도 않고 무조건 .active를 없앤다.

    확실히, 이렇게 하면, active한 패널은 최대 1개이고, 그 패널의 탭을 다시 누르면 모든 패널이 잘 닫히는 등, 크게 비논리적인 행동 없이 기대한 대로 잘 작동할 것 같은데 어떠신가요?

    코드 한 줄 없이 다 말로 때웠습니다만, 여기서부터는 질문자님이 스스로 자기 코드를 돌아보고 어떻게 리팩토링할 것인지를 고민하는 단계에 들어가셔야 합니다. 결국, 질문자님이 정말 원하는 최종 결과가 뭔지는 질문자님이 가장 잘 알고 계십니다. 한번 잘 생각해 보시고 차분히 가 보세요. 도움이 됐기를 바라며 이만 줄이겠습니다.


    사족 1. 질문 자체에만 답을 드리자면... 이상 현상이 발생하고 있는 이유는 .open.close가 동시에 사용되고 있기 때문일 것으로 보입니다. 복붙 코드와 CSS의 나열로 인해 우선순위가 꼬여 있고 그 꼬인 우선순위를 추적하기도 매우 어려울 것입니다. CSS는 나중에 나오는 정의가 더 중요하므로, .friend.close 이후에 .calendar.open이 적혀 있는 한, 캘린더의 열림 여부가 친구 목록창의 닫힘 여부보다 더 중요합니다. 아닌가? 아! 헷갈리네요.

    사족 2. 일상 생활에서는 open의 반대가 close입니다만, 공학의 세계에서 open의 반대는 not open입니다. 왜 그러냐면, open and close라든가 not open and not close라든가 하는 경우가 이론상으로는 가능하기 때문입니다. (지금 메뉴들이 바로 그럴 거에요.) 이 부분 상당히 중요한 개념인 관계로 굳이 아는체를 해 봅니다. 다른 건 몰라도 이건 잊지 말고 이후 유념해 주세요.

    • 정말 정성스러운 답변 감사합니다 ! 태그 해주신 사이트를 읽어보니 정말로 탭이 아니라 아코디언에 더 가까울 지도 모르겠네요 !! 일단은 더 공부해보고 active 상태를 만드는 것을 우선으로 수정해 나가보도록 하겠습니다 ! 급한 프로젝트여서 복붙을 많이 했는데 이런 문제가 생길 줄이야 ,,,, 덧붙여 주신 이야기도 꼼꼼히 참고하도록 하겠습니다 . 정말 감사해요 !!! 😄 알 수 없는 사용자 2021.10.18 23:23

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)