[자바스크립트] 이벤트와 DOM 조작 1편
흔히 페이지를 만들때 HTML, 스타일을 넣고 싶다면 CSS, 기능을 넣어주고 싶다면 자바스크립트를 써야한다고 처음 배운다.
여기서 말하는 기능이란 HTML 요소들을 이래저래 조작하는 것이라 볼 수 있다.
예를 들어, 버튼을 누르면 다른 페이지로 이동이 된다던지, 새로운 요소를 추가한다던지 등 전부 자바스크립트로 가능한 일이다.
이런 자바스크립트에서는 다양한 방법으로 DOM 요소를 조작할 수 있는데 다음과 같은 방법이 있다
1. getElementById: 아이디를 통해 요소를 선택할 수 있게 해주는. 보통 아이디는 페이지 내에서 고유한 것이 국룰이기에 유일한 요소를 선택한다.
const element = document.getElementById("id");
2. getElementsByClassName: 말 그대로 클래스 이름을 통해 요소를 선택할 수 있게 해주는 함수이다. id와 달리 클래스이름의 경우 같은 이름을 가진 여러 요소가 있을 수 있으므로 반환 값은 HTMLCollection이다.
const elements = document.getElementsByClassName("class");
3. getElementsByTagName: 태그 이름을 통해 요소를 선택한다. 마찬가지로 같은 이름을 가진 여러 요소가 있을 수 있으므로 반환 값은 HTMLCollection이다.
const elements = document.getElementsByTagName("div");
4. querySelector: 가장 많이 쓰는 친구다. 이전에 소개한 함수에 비해 클래스이름, 아이디, 태그이름 등 문서내 모든 요소를 찾는데 사용할 수 있으므로 CSS 선택자를 통해 요소를 선택한다. 즉, #아이디, .클래스이름, p, div 등으로 모두 찾는게 가능하다.주의해야할 점은 여러요소가 있더라도 처음 마주치는 하나의 요소만 반환한다는 것이다.
const element = document.querySelector(".class");
5. querySelectorAll: querySelector가 처음 일치하는 하나의 요소만 반환한다면 이 함수는 이름에서 부터 느껴지듯이 CSS 선택자를 통해 일치하는 모든 요소를 list의 형태로 반환한다. 여기서 클래스이름과 태그이름을 통한 검색 함수와 다른 점은 HTMLCollection이 아닌, NodeList의 형태로 반환한다는 것이다.
const elements = document.querySelectorAll(".class");
HTMLCollection VS NodeList
위에서 계속 언급하다보니 이 둘의 차이를 아는게 꽤 중요하다는 것을 짐작하셨을 것이다.
일단 이 둘 모두 유사 배열 객체라는 건데, 대충 둘 다 배열 비슷한거라서 반복가능하고, 배열로 변환하는게 정신건강에 좋다는 것이다.
왜냐하면 HTMLCollection의 경우 요소를 인덱스나, 이름, ID로 접근할 수 있지만, NodeList에 경우 인덱스를 통해서만 접근할 수 있기 때문이다. 또한, HTMLCollection의 경우 배열의 내장 메서드(ex. forEach, map)를 사용할 수 없으며, NodeList에 경우는 ES6부터 forEach를 지원하기 시작했다. 그렇다하더라도 일단 필자가 생각하기에는 그냥 둘다 배열로 변환하는게 개발할때는 마음이 편하지 않을까 싶다.
# HTMLCollection 예시
<div class="myClass">Element 1</div>
<div class="myClass">Element 2</div>
<div class="myClass">Element 3</div>
<script>
// 'myClass' 클래스를 가진 모든 요소를 선택하여 HTMLCollection 반환
const elements = document.getElementsByClassName('myClass');
console.log(elements.length); // 3
console.log(elements[0].textContent); // "Element 1"
// 요소를 이름으로 접근
document.body.appendChild(document.createElement('div')).className = 'myClass';
console.log(elements.length); // 4, 실시간 업데이트
</script>
# NodeList 예시
<body>
<div class="myClass">Element 1</div>
<div class="myClass">Element 2</div>
<div class="myClass">Element 3</div>
<script>
const elements = document.querySelectorAll('.myClass');
console.log(elements.length); // 3
document.body.appendChild(document.createElement('div')).className = 'myClass';
console.log(elements.length); // 여전히 3
</script>
</body>
그리고 HTMLCollection과 NodeList의 가장 큰 차이점은 동적이냐 정적이냐라고 생각한다.
예시를 보면 눈치채셨겠지만 HTMLCollection의 경우 HTML 문서의 변화를 실시간으로 반영한다.
즉, DOM에 변경 사항이 있다면 HTMLCollection의 내용도 자동으로 업데이트된다는 것이다.
반대로, NodeList의 경우 정적일수도 있고 동적일수도 있다. 왜냐하면 childNodes()라는 함수도 있는데 이걸 이용하면 라이브 NodeList를 반환하기 때문이다. 하지만 대중적으로 querySelector 훨씬 많이 쓰이기에 NodeList의 경우 웬만하면 정적(static)요소일 가능성이 크다고 볼 수 있다.
그렇다면 HTMLCollection이 더 좋은 거 아니냐? 할 수 있는데 그렇지 않을 수 있다는 거다.
왜냐하면 HTMLCollection은 DOM이 변경될 때마다 업데이트되기 때문에 많은 요소를 포함하거나 너무 빈번하게 변경되는 DOM을 다룰 때 메모리 사용량이 늘어날 수 있기 때문이다.
그리고 이러한 변경 사함을 계속 추적한다는 것은 메모리가 계속 살아있다는 뜻이고 이는 곧 많은 양의 데이터를 다루거나 오래 실행되는 어플리케이션에서는 메모리 누수 문제가 발생할 수 있다는 것이다.
반면, NodeList의 경우 라이브 업데이트가 필요하지 않기에 메모리 사용량이 상대적으로 적고, 메모리 누수 문제가 덜 발생할 수 있다. 결국 요점은 이러한 둘의 특성을 이해하면 코드의 동작 방식을 예측하고, 의도치 않은 버그를 줄이는데 도움이 될 수 있다는 것이다.