한 걸음씩
[JS] FAQ(자주 묻는 질문) 페이지 만들기 ✅ 본문
[요구사항]
박스를 열었다 닫았다 할 수 있을 것
동시에 열지 못할 것
<div class="box">
<div class="answer">
<span>Do You Accept All Major Credit Cards?</span>
<span class="material-symbols-outlined add">add_box</span>
</div>
<div class="question hidden">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est dolore illo dolores quia nemo doloribus quaerat, magni numquam repellat reprehenderit.</p>
</div>
</div>
span태그는 아이콘을 나타내는 태그인데 두 개의 아이콘을 HTML파일에 두고 hidden 클래스로 toggle할 계획이었으나 toggle이 되지 않아서 기존의 클래스와 텍스트를 제거하고 새로운 클래스와 텍스트를 넣어 아이콘을 바꾸는 방법을 선택했다.
const boxes = document.querySelectorAll('.box'); // box가 여러개니까 All
// 열려 있는 박스를 추적하는 변수
let openBox = null; // 최초상태 : 박스가 닫혀있음
// '+' 아이콘을 클릭했을 때 실행할 함수
function onOpenBtn(event) {
// querySelector의 경우 문서 전체를 탐색하기 때문에 현재 이벤트가 발생한 요소를 선택하기 어렵다
// closest는 현재 요소와 관련된 요소를 정확하게 찾아낸다.
const box = event.currentTarget.closest('.box');
const question = box.querySelector('.question');
const answer = box.querySelector('.answer');
const addBox = box.querySelector('.add');
if (openBox && openBox !== box) { // 이전에 열려 있던 박스가 있고, 현재 열려 있는 박스가 다른 박스일 경우
// openBox : 이전에 열려있던 박스를 가리키는 변수 : null이 아닐 경우 실행
// box : 현재 박스를 기리키는 변수
// openBox !== box : 이전 박스와 현재 박스가 다른 경우 closeBox실행
// -> 한 번에 하나의 박스만 열릴 수 있도록!
closeBox(openBox); // 이전 박스를 닫음
}
openBox = box; // 현재 박스를 열린 상태로 설정
question.classList.remove('hidden'); // question이 화면에 보일 수 있게 hidden 제거
answer.style.borderBottom = '1px solid rgb(183, 183, 183)'; // 밑줄 효과
addBox.classList.remove('material-symbols-outlined'); // '+' 아이콘 제거
addBox.textContent = ''; // 아이콘 텍스트 제거
addBox.classList.add('material-symbols-outlined'); // '-'아이콘 추가
addBox.textContent = 'remove'; // 아이콘 텍스트 추가
addBox.addEventListener('click', onCloseAnswer); // '-'아이콘 클릭시 닫는 이벤트 실행
}
// '-' 아이콘을 클릭했을때 실행할 함수
function onCloseAnswer(event) {
const box = event.currentTarget.closest('.box');
closeBox(box); // 현재 박스를 닫음
}
// 중복으로 열린 박스를 닫기 위한 closeBox 함수
function closeBox(box) {
const question = box.querySelector('.question');
const answer = box.querySelector('.answer');
const addBox = box.querySelector('.add');
question.classList.add('hidden'); // question이 화면에 안보이도록 hidden 추가
answer.style.borderBottom = 'none'; // 밑줄 제거
addBox.classList.add('material-symbols-outlined'); // '+'아이콘 추가
addBox.textContent = 'add_box'; // 아이콘 텍스트 추가
openBox = null; // 열린 상태를 해제 : 초기화
// onCloseAnswer를 실행하는 이벤트를 제거하지 않으면 박스를 닫는 이벤트가 유지가 되기때문에
// onCloseAnswer와 onOpenBtn가 충돌이 생겨 정상작동을 하지 않는다
addBox.removeEventListener('click', onCloseAnswer);
}
// '+'아이콘을 클릭했을 때 실행할 함수
boxes.forEach((box) => {
const addBox = box.querySelector('.add');
// addBox는 '+' 박스를 여는 아이콘을 가리킴
addBox.addEventListener('click', onOpenBtn);
});
현재의 요소를 가리킬 때 사용하는 currentTarget,
closest는 현재 요소에서 가장 가까운 조상을 찾고, querySelector는 전체 문서에서 해당하는 요소를 찾는다는 차이점,
removeEventListener로 이벤트를 제거하는 방법,
박스가 동시에 열리지 못하도록 조건문과 초기에 null을 할당한 변수를 이용한 컨트롤 등
시작하기 전에는 금방 끝나겠지 하고 도전했다가 결코 쉽지 않았던 챌린지였다.
그만큼 새로운 것도 많이 배웠다.
'JS' 카테고리의 다른 글
자바스크립트 중급 총정리 (0) | 2023.07.28 |
---|---|
[JS] Modal 만들기 (0) | 2023.07.10 |
[JS] 반응형 Navbar, Sidebar 만들기 (0) | 2023.07.07 |
[JS] Carousel 만들기 (0) | 2023.07.06 |
[JS] Number Counter 만들기 (0) | 2023.07.06 |