
자바스크립트 수업이 시작되고 DOM을 조작하는 메소드와 속성을 공부하면서 class 속성을 기준으로 여러 요소를 선택할 때 querySelectAll과 getElementsByClassName을 사용할 수 있다는 것을 알았다.
그런데 둘 다 class를 기준으로 선택하는거면 무슨 차이가 있길래 비슷하게 동작하는 메소드를 두개나 만들었을까? 하고 궁금해서 찾아봤더니 정적 / 동적으로 동작하는 차이가 있었다.
그 당시에는 그냥 그렇구나하고 넘어갔는데 며칠 후 캠프 디스코드에 해당사항과 관련된 질문글이 올라왔다. 그런데 충격적이게도 나는 처음에 그 질문글을 봤을 때 내가 아는 내용인지도 몰랐다!!!!!!@@@@@!@!@!@ 대충격( l|l⚆ᗝ⚆)
질문대로 왜 그렇게 동작하는걸까? 하고 실습해다 보니 어느샌가 내가 전에 공부했던 메소드의 동작방식 때문이었구나 하고 알게되었다. 오늘은 그 내용에 대해서 정리해보겠다!
두개의 dom메소드는 인자로 받은 값에 일치하는 조건을 가진 dom객체를 불러온다는 공통점이 있다.
mdn과 내가 참고한 블로그를 보면 querySelectAll은 정적인 nodelist를 반환하고, getElementsByClassName은 동적인 HTMLcollection을 반환한다고 설명한다.
아니 그럼 그건 또 뭔데?! 둘 다 여러 개의 요소 노드 객체를 반환할 때 사용되는데,
HTMLcollection : mdn - 현대적 DOM의 이전, 구성요소로 HTML 요소만 지닐 수 있었던 시절에 정의. HTML 문서 내에서 선택한 요소만을 문서 내 정렬된 순서대로 모아둔 집합이다. 이것은 유사 배열 객체(배열과 유사한 형태를 취하고 있어서 lenght가 있고 반복문을 돌 수 있지만, 배열이 아니기 때문에 대부분의 배열객체 메소드(map, filter 등)를 사용 할 수 없다.)이고, 주의해서 살펴볼점은 DOM의 변경 사항을 실시간(또는 live)으로 반영한다는 점이다.
nodelist : 웹페이지의 HTML 문서 내에서 선택한 요소 객체뿐만 아니라 텍스트, 주석, 속성 등의 모든 노드를 문서 내 정렬된 순서대로 모아둔 집합으로, 마찬가지로 유사배열이라 lenght가 있고 반복문을 돌 수 있고 특이하게도 forEach() 메소드를 사용 할 수 있다. nodelist도 마찬가지로 변경사항을 실시간으로 반영하는데, 주의할 점은 document.querySelectorAll() 은 정적 NodeList를 반환한다는 것이다! 즉 메소드에 따라 live인지 none-live인지가 결정된다.

0~8까지의 <li>를 만들고, 모두 선택하여 list라는 변수에 저장한 후 반복문을 돌면서 <li>가 짝수인덱스일 경우 list에서 삭제하라 라는 코드이다.
querySelectAll로 <li>를 선택한 경우에는 의도대로 짝수의 <li>가 잘 삭제되었지만, getElementsByClassName으로 <li>을 선택한 경우에는 제대로 적용되지 않았다.
getElementsByClassName으로 메소드를 선택한 경우에 반복문을 한번 실행하여 list[0]인<li class="li">00</li>가 삭제되면, 실시간으로 let list = document.getElementsByClassName('li');이 바뀌게된다. 그러면 이제 list[0]은 <li class="li">11</li>이 되고
list[1]은 <li class="li">22</li>가 된다.
반복문은 한번 반복했으니 list[1]을 확인하는데, list[1]은 짝수가 아님으로 제거되지 않고 남게된다. 여기서 문제가 발생하는 것이다. 원래대로라면 <li class="li">22</li>는 짝수 인덱스의 li여서 삭제가 되어야하는데 list[1]취급을 받게되어 남게되는것이다!
반면에 querySelectAll 메소드를 선택했을 때에는 li가 삭제되어도 그결과가 바로 반영되지 않고
let list = document.getElementsByClassName('li');의 내용이 변하지 않기 때문에 의도대로 잘 작동하는 것이다.
디스코드에 남겨주신 캠프동기님의 질문은 이러했다. 라이브강의 시간에 강사님께서 게시글을 추가하고 삭제하는 예시를 보여주셨는데, 게시글을 삭제하고나서 해당 목록을 콘솔에 다시 찍어봤는데 목록에 그대로 존재하더라는 것이다!!
실습해본 것과 같이 querySelectAll로 불러온 dom을 domPosts에 저장하고, domPosts를 통해 삭제했는데도 콘솔에 그것을 확인했더니 결과에 차이가 없다. 이것이 공부했던 것처럼 오류를 방지하기 위해 querySelectAll이 non-live로 동작하기 때문이다.
domPosts = document.querySelectorAll('.post');
console.log(domPosts);

domPosts를 수정 후 재할당하고 확인하면 수정된 nodelist가 보이는 모습이 확인된다.

getElementsByClassName을 사용하면 삭제된 값이 live로 적용된것이 console을 통해 확인되지만, 게시글이 의도대로 모두 지워지지 않은 것을 볼 수 있다.
+)그래서 getElementsByClassName을 사용하면 안되느냐?
하는것도 궁금해서 구글링해봤는데 ( 나름 영어로 dont' use getElementsByClassName 이라고 검색도 해봤다 ) 딱히 사용해선 안된다는 내용은 없었다. 오늘 배운것을 생각해서 주의하면서 사용하면 되지 않을까 싶다.
의문이 생겼을 때 바로 정리하지 않은 것을 조금 후회했다. 제일 처음에 동기님의 질문글을 봤을 때 바로 답이 생각나지 않았기 때문이다. 심지어 처음에는 엉뚱하게 콘솔의 디버깅 방식이라는 둥 이상한 소리도 남겨서 좀 부끄러웠다. ( 서치했더니 답을 못찾겠고, GPT에게 물어봤더니 저렇게 말해줬다. 평소에도 GPT사용시 그 답변내용에 주의해야 한다는건 알지만 이번에는 진짜인줄 알았다. )
생각이 바뀐 이유는 아무래도 찜찜했기 때문이다. 그래서 백엔드개발자 지인에게 물어봤는데, 그분도 js를 많이 아는것은 아니지만 그런식으로 동작하는것은 아무래도 이상한것 같다고 서로 토론만 3시간을 했다. ( 정작 노트북을 켤때는 오늘은 꼭 투두리스트 local session에 저장하기를 성공 해보자고 다짐했는데.. )토론의 막바지에 문득 처음에 dom method를 공부하면서 class를 선택하는 두가지 방법에 대해 찾아봤을 때 live, none-live를 본 것 같은데..? 하는 생각이 나면서 그제서야 올바른 답을 찾을 수 있었다.
평소 학습할 내용을 미룬 벌을 이렇게 받는구만 하는 생각도 들고 그래도 끝내 답을 찾아내서 뿌듯하다는 생각도 들었다. 물론 이게 답이 아닐 수도 있지만 (•᷄- •᷅ ;) 미룬 벌을 받은거라면 열심히 고민한 보상도 언젠가 받는 날이 오지 않을까? 못받으면 까비고~