Record4me

시작하면 끝을 봐야지

TIL

[TIL] 23_0107 순수 자바스크립트로 만든 검색 기능 구현을 마치며, Element.closest()

잇츄미 2024. 1. 7. 20:21

순수 자바스크립트로 만든 검색 기능 구현을 마치며,

필수요구사항부터 차례대로 구현하고 선택요구 사항까지 구현 했지만 유지보수하기가 어려운 코드같아서 고민을 좀 해봐야할 것 같아. 

 

 

난제봉착

css를 구성하면서 card-content클래스를 가진 div 아래로 여러 하위 div요소를 추가하게 됐어. 새로 추가된 요소들이 있으니 값을 가져오는 경로도 달라져서 이전 alert.js 문을 수정해야했어. 그 과정에서 값을 가져올 때 난황을 겪었어.

 

1. createElement로 만든 div만 클릭했을 때만 alert가 나오고 카드 내부를 클릭하면 alert가 안 떴어.

- createElement로 만든 상위 div를 제외하고 내용물을 한 번에 감싸는 div가 없어서 분리가 된 상태였거든. (아래 코드참고)

<div class="target">id : ${mainCards.id}</div>
<div class="imgSort">
	<img
      src="https://image.tmdb.org/t/p/w500${mainCards.poster_path}"
      alt="${mainCards.title}"
	/>
</div>                    
<h3>${mainCards.title}</h3>
<p class="over-view">${mainCards.overview}</p>
<p class="rating">Rating : <span class="rating-number">${mainCards.vote_average}</span></p>

 

2. createElement로 생성한 div를 클릭했을 때, 이전처럼 해당 div의 정보를 getElementBy~, querySelector 메서드로 가져오려고 했지만 querySelector 메서드는 해당 요소가 없는 값이라며 null값이 나오고 get 가져올 수 없었어.   HTMLcollection이나 window내의 document값을 찾아봤을 때 분명히 존재한 걸 확인했었는데도.

 

getElementsByClassName으로 정보를 가져오려고 했을 때.

 

 

 

 

 

 

참고 : css를 주면서 클래스 card-wrap을 가진 div요소가 추가된 동적요소 실행문.

movies.map((mainCards) => {
    const $mainCardsDiv = document.createElement("div");
    $mainCardsDiv.setAttribute("class", "card-content");
    $mainCardsDiv.setAttribute("movieId", mainCards.id);
    //div로 따로 놀던 div들을 묶어서 하나의 카드를 클릭했을 때 어딜 클릭해도 나오게끔
    $mainCardsDiv.innerHTML = `
                  <div id="${mainCards.id}" class="card-wrap">
                    <div class="target">id : ${mainCards.id}</div>
                    <div class="imgSort">
                       <img
                         src="https://image.tmdb.org/t/p/w500${mainCards.poster_path}"
                         alt="${mainCards.title}"
                       />
                    </div>                    
                    <h3>${mainCards.title}</h3>
                    <p class="over-view">${mainCards.overview}</p>
                    <p class="rating">Rating : <span class="rating-number">${mainCards.vote_average}</span></p>
                  </div>
                  `;
    return $cards.insertAdjacentElement("beforeend", $mainCardsDiv);
  });

 

 

 

해결

Element .closest()

 

1. card-wrap이라는 클래스를 가진 div를 innerHTML의 템플릿 리터럴 안에서 분리된 요소들을 감싸는 하나의 div를 만들어주고 영화의 id값을 div요소의 id값으로 넣어줬어. 그리고 상위 태그인 card-content라는 클래스를 가진 div에도 movieid라는 속성을 생성해줐어. 영화의 id값을 넣어줘서 어딜 클릭해도 id값을 가져올 수 있도록. 

 

2. 그렇게 closest() 메서드로 카드를 감싸는 최상위 값에 click이벤트를 걸어줘서 그 안에 카드를 target으로 지정해서 클릭해줬을 때 alert를 뜨게 했는데 브라우저에서 카드인 div와 카드를 감싸는 최상위 값 사이의 빈공간을 누르면 null값이 떠서 처리해줬어

 

// 카드 최상위 div 범위(하위요소로 card-content란 클래스를 가진 div가 딸려있음을 console.log로 확인 가능했음)
const $cards = document.querySelector("#cards-container"); 

// 최상위 div요소와 card-content란 클래스를 가진 div등이 있는 하위요소들까지 포함한 범위 내의 Element를 클릭하고,
$cards.addEventListener("click", (e) => {
// null값이 안 뜨면 
// 이건 카드 내용물 이 외의 부분을 클릭했을 때 null값이 나오게 되서, 그 값에 대한 alert를 안 뜨게 하기 위해 추가한 조건이야)
    if (e.target.closest(".card-content") !== null) {
    //상위요소 .card-content란 클래스를 가진 div의 내부 div를 찾아 그 id를 가져와.
      const targetCardId = e.target
        .closest(".card-content")
        .querySelector("div").id;
// e.target으로 지정된 카드의 영화 id를 alert로 띄워줘
      if (targetCardId) {
        window.alert(`영화 id : ${targetCardId}`);
      }
    }
  });

 

 

3. 여기서 지정한 div정보를 가져오려고 getElementsByClass, querySelector도 안 되서 지정한 Element에서 해당 선택자를 가진 요소의 정보을 상위요소들 중에서 찾아서 가져오는 closest라는 메서드를 써서 가져오고(e.target.closest())

 

4. 가져온 상위요소 안의 div 안에 있는 id를 찾아왔지.    

 // 지정한 movie카드의 id를 가져오기 
 const targetCardId = e.target
        .closest(".card-content")
        .querySelector("div").id;

 

 

- Event 인터페이스의 target 속성과 currentTarget은 이벤트가 발생한 대상 객체를 가리켜서 한 꾸러미 내를 탐색해서 값을 찾아오는 getElementsByClass()와 querySelector()메서드를 사용해도 값이 안 나왔어. 그래서 지정한 Element를 기준으로 찾아가는 메서드를 써야했지.

 

*querySelector : 제공한 선택자 또는 선택자 '뭉치'와 일치하는 문서 내 첫 번째 Element를 반환해. querySelector의 주체가 되는 Element는 하나의 뭉치여야 하는 거지.

 

*참고사이트
- https://developer.mozilla.org/ko/docs/Web/API/Document/querySelector (querySelector 메서드에 대하여)

- https://developer.mozilla.org/ko/docs/Web/API/Event/target (Event 인터페이스의 target속성에 대하여)

- https://developer.mozilla.org/ko/docs/Web/API/Element/closest (Element.closest 메서드에 대하여)

 

 

그렇게 alert문을 다시 보수하면서 alert.js는 굳이 쓰지 않게 됐어. 

 

느낀점

 

 

- html의 영역을 잘 나눠서 css를 좀 더 생각하면서 가독성도 염두해두고 구성해야겠어. 정확한 로직의 코드를 작성하지 못 해서 반성하게 돼. 

- 동적 요소의 구성을 복잡하게 짜서 유지보수하기가 어려운 js를 짠 것 같아. 추가하고 싶은 기능을 넣기 전에 코드부터 유지보수가 쉽도록 간결하고 값을 쉽게 추가하고 받을 수 있는 코드로 수정해야겠어.

- 이 후엔 각 기능을 파일로 분리해서 import, export를 해보려고 해.