what is HTMLcollection? NodeList?

KIP·2022년 6월 12일
0
post-thumbnail

querySelector와 getElementById쓰다보면 드는 생각
아이디를 불러올 때 둘 다 사용가능한데, 무슨 차이가 있지?

알아야 할 개념: HTMLcollection/NodeList

HTMLcollection
https://developer.mozilla.org/ko/docs/Web/API/HTMLCollection

"HTML DOM 내의 HTMLCollection은 문서가 바뀔 때 실시간으로 업데이트됩니다."

HTMLCollection은 리턴 결과가 복수인 경우에 사용하게 되는 객체다

nodeList
https://developer.mozilla.org/ko/docs/Web/API/NodeList

경우에 따라, NodeList는 라이브 콜렉션으로, DOM의 변경 사항을 실시간으로 콜렉션에 반영합니다. 예를 들어, Node.childNodes 는 실시간입니다

다른 경우 NodeList는 정적 콜렉션입니다. DOM을 변경해도 콜렉션 내용에는 영향을 주지 않습니다. document.querySelectorAll() 은 정적 NodeList를 반환합니다.


getElementById는 반환값이 element지만,
tagName과 ClassName은 반환값이 HTMLcollection이다.
querySelectorAll과 getElementsByName은 Nodelist이다.

글 만으로는 정확한 이해가 가지 않는다.
반환값이 다른 3개의 경우를 예제로 확인해보자.
서로같은 div>li+li>li구조로 만들었다.

<div id= "element">
        <li>ele</li>
        <li>
            <li>ele1</li>
        </li>
    </div>
    <div id= "element">
        <li>ele</li>
        <li>
            <li>ele1</li>
        </li>
    </div>
    <hr>
    <div name="Node" >
        <li>node</li>
        <li>
            <li>node1</li>
        </li>
    </div>
    <div name="Node" >
        <li>node</li>
        <li>
            <li>node1</li>
        </li>
    </div>
    <hr>
    <div class="Html">
        <li>html</li>
        <li>
            <li>html1</li>
        </li>
    </div> 
    
    <div class="Html">
        <li>html</li>
        <li>
            <li>html1</li>
        </li>
    </div>
  
    </div>
    <script>
    window.onload= () =>{ 
        const element = document.getElementById('element');
        const Node = document.getElementsByName('Node');
        const Html = document.getElementsByClassName('Html')
        console.log(element, Node, Html)
        
    }

console

프로토타입까지 확인해보니 3개 전부다 다른 메서드를 가지고 있다.
element를 반환하는 경우, 같은 id값이라도 하나의 태그만을 보인다. 즉, unique한 id가 아닐경우는 원하는 태그에 접근하지 못할 수도 있다.

const element = document.getElementById('element');
        const Node = document.getElementsByName('Node');
        const Html = document.getElementsByClassName('Html') // + tagName
       
       console.log(element, Node, Html)

        console.log(element.length) // undefined
        console.log("node의 길이입니다==>" + Node.length) //2
        console.log("html의 길이입니다===>" + Html.length) //2
        console.log("element입니다 ==>" + element.childNodes, element.children) // 67 번
        
        const arrNode = Array.from(Node);
        console.log(arrNode.map((v) => v.children)) //70
        console.log(arrNode.map((v) => v.childNodes)) //71
        
        const arrHtml = Array.from(Html);
        console.log(arrHtml.map((v) => v.children))

먼저 HTML과 NODE모두 유사배열객체로, 배열과 유사한 객체 => 우리가 알던 built-in함수에 대해서 자유로이 쓰지 못하는 상태다(prototype을 확인해보면 쓸 수 있는 메서드를 알 수있다).
따라서 Array.from함수로 얕은 복사를 통해 array형태로 만들어준다.

(Array.from() 메서드는 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운Array 객체를 만듭니다.)



정리 및 새로운 사실

정리하자면, 우리가 쓰는 documen의 id, name, className, queryselector.. 등을 사용할 때 반환 되는 값들이 다르고, id는 element를 반환하기 때문에 유니크한 값이 필요.

id가 아닌 class나 전체 태그에 대해서 배열 속성을 사용하고 싶다면, 반환 값을 살펴보고, 유사배열객체 -> 배열의 형태로 바꿔준다 (Array.from(값))

이 외에 역순으로 for문을 돌린다거나 타개책은 있다고 한다.

*주의해야할점


   const Node = document.getElementsByName('Node');
   const Html = document.getElementsByClassName('Html') // + tagName
        
    for(let i = 0; i<Html.length ; i++){
            Html[i].className= "Html"
            console.log('회전')
            
        }
        for(let i = 0; i<Node.length ; i++){
            Node[i].childNodes[1].className = "hi"
            Node[i].childNodes[3].className = "hi"
            console.log('회전')

for문을 이용해서 이렇게 className을 바꾼다면 실수다. 위에서 말한 라이브 콜렉션을 생각해야한다. 처음 for문이 한번 돌아간다고 생각하면

Html[0].className= "editClass"

이 되는데, 이때 다시 for문을 돌리면 우리가 생각한

Html[1].className= "editClass"

은 변하지 않는다.
정확하진 않지만, for문을 다시 돌릴 때, 실시간으로 바뀌는 상태에 의해
i = 1을 바라보는데 Html.length는 2->1개가 된다
(첫번째의 className이 editClass로 바뀐 상태기 때문에)
이게 아닐지라도 실시간으로 DOM을 관찰하는 구조는 확실히 에러를 낼 이유가 충분하다.

0개의 댓글