자바스크립트에는 여러 주요 기능이 존재하지만, 가장 중요한 기능은 html 문서와의 상호작용을 통한 브라우저에 렌더링하는 기능이 제일 중요할것이다.
이렇게 html 문서와 상호작용하여 브라우저에 렌더링 할 수 있는 이유는 무엇일까?
바로 DOM(Document Object Model)때문이다.
1. DOM이란?
DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다.
즉, 자바스크립트등의 언어가 쉽게 웹 페에지에 접근하여 조작할 수 있게 연결시켜주는 역할을 한다.
또한, DOM은 모두 노드(Node)로 이루어져있는 객체이다.
이러한 노드에는 두가지 종류가 존재한데, 요소(element)노드와 텍스트(text)노드이다.
<div class="modal__actions">
<button class="btn btn--passive">Cancel</button>
<button class="btn btn--success">Add</button>
</div>
예를들어 <div></div>, <button></button>등이 요소노드이고,
<button>앞에 공백, <button>태그 사이에 텍스트등이 텍스트 노드이다.
2. DOM 쿼리 vs DOM Traversing
앞서 언급했듯이 자바스크립는 DOM을 통해 html 문서와 상호작용할 수 있다.
이때 접근하는 방법으로는 쿼리와 탐색(traversing)이 존재한다.
2_1. DOM 쿼리
자바스크립트에는 쿼리를 도와주는 여러 메서드들이 존재한다.
A. querySelector(), getElementById()
- 하나의 요소를 반환한다
- 객체인 DOM 노드의 주소를 반환한다
a. querySelector()
→ css 선택자를 이용해 접근한다
→ 만약 같은 이름의 css 선택자가 여러개 존재한다면, 맨처음에 일치하는 요소로 엑세스한다.
b. getElementById()
→ html 태그의 id를 이용해 접근한다
B. querySelectorAll(), getElementsByTagName()…
- 여러 요소를 반환한다.
- NodeList라는 유사 배열 객체를 반환한다
- querySelectorAll()은 정적인 NodeList(현재 렌더링되어있는 DOM의 스냅샷)을 제공하는 반면, getXByY()는 동적 NodeList를 제공한다 => 정적 노드리스트와 동적 노드리스트에 대해선 밑에서 서술..
이러한 메서드들을 통해 요소에 접근한 후, 그 요소에 대한 프로퍼티(https://joseph0926.tistory.com/11)를 변경할 수 있다.
/* HTML
<h1 id="main-title" class="messiClass">messi</h1>
*/
//js
const title = document.getElementById("main-title");
const titleText = title.textContent;
const titleId = title.id;
const titleClassName = title.className;
console.log(titleText, titleId, titleClassName);
// 결과 => messi main-title messiClass
titleText = "pedri";
console.log(titleText);
// 결과 => pedri
2_2. DOM 탐색하기(Traversing)
DOM 탐색하기란?
쿼리를 통해 이미 선택한 요소를 이용하여 이 요소를 기반으로 자식 요소, 형제 요소등으로 옮겨갈 수 있는 것
traversing의 메서드를 알아보기 전에 용어정리가 필요하다.
<div>
<h2>
<span></span>
</h2>
</div>
1. children
- 직접적인 자식 요소
-> 위의 코드에서 h2는 div의 자식 요소가 맞지만, span은 div의 자식 요소가 아니다
2. descendants
- 직접 or 간접적인 자식 노드를 의미
-> 위의 코드에서 h2와 span 모두 div의 descendants(후손)이다
3. parent
- 직접적인 부모 노드
-> 위의 코드에서 div는 h2의 부모 노드지만, span의 부모 노드는 아니다
4. ancestors
- 직접 or 간접적인 부모 노드
-> 위의 코드에서 div는 h2와 span의 ancestors(조상)노드이다
이제 traversing하는 방법, 즉 메서드를 살펴보자
1. 자식, 후손
<ul>
<li class="list-itme">Item 1</li>
<li class="list-item">Item 2</li>
<li class="list-item">Item 3</li>
</ul>
예를들어 여기서 두번째 li를 선택하고 싶다면 어떻게 해야할까?
→ 기존의 쿼리로는 힘들어보인다..왜냐하면 qureySelector()을 사용한다해도 결국 첫번째 li가 선택되므로…
-> 이럴때 사용하는것이 DOM Traversing이다.
위의 예시에서는 children을 사용했지만, 이외에도 childNodes, firstChild, lastElementChild등을 사용할수도 있다
*참고
children과 childNodes의 차이점은 children은 오직 자식 요소 노드만 접근할수있다, 반면 childNodes는 자식요소노드 뿐만 아니라 텍스트노드도 접근할수있다
2. 부모, 조상
A. 부모
부모 노드에 접근할려면 parentNode or parentElement로 접근하면된다
*참고
parentNode와 parentElement 차이점은 자식과 달리 하나를 제외하면 없다…그하나는 document객체를 접근할려할때 parentElement 을 사용하면 null을 반환하지만, parentNode 사용하면 정상적으로 접근된다
(childNodes와 달리 parentNode다...뒤에 's'가 붙지않는다!!!)
B. 조상
closest() 메서드로 접근
→ 이 메서드는 qureySelector와 같이 css 선택자를 이용한다
→ 이 메서드를 통해 간접적인 부모, 조상노드에 접근할 수 있다
3. 형제
같은 레벨에 있는 요소를 형제라 칭하고 형제요소에 접근하는 방법은 previous(next)Sibling, previous(next)ElementSibling이 존재한다
- previous(next)Sibling
<div>
<header></header>
<h2></h2>
</div>
→ 이는 가장 가까운 노드를 탐색한다…
→ 쿼리로 h2를 선택해 previous(next)Sibling을 사용하면 header를 찾을꺼 같지만, 결과적으로 텍스트 노드가 반환된다.
→ 즉, 요소 노드던, 텍스트 노드던 가장 가까운 노드를 탐색한다
- previous(next)ElementSibling
→ 이는 가장 가까운 요소 노드를 탐색한다.
→ 위의 코드에서 h2를 이용해 header를 탐색할수있게된다
2_결론
그렇다면 DOM 쿼리와 DOM 탐색하기중에 뭘 사용해야할까?
우선 DOM Traversing이 더 좋은 성능을 나타낸다, 하지만, 항상 DOM Traversing을 사용해야하는것은 아니다.
→ 만약, DOM Traversing으로 이미 요소를 찾았는데 html 코드가 변경되어 요소들의 위치(관계)가 변경된다면 이는 js코드를 다시 수정해야한다는 의미가 된다.
→ 또한, 가독성이 좋지않을수있다(ex, const ul = document.body.firstElementChild.nextElementSibling)
=> 결국 상황에 맞게 적절하게 혼용하는 것이 바람직하다
- 항상 관계가 유지되는 경우(ul - li)는 DOM Traversing 사용
- 형제관계등의 복잡한 관계가 아닌 경우 DOM 쿼리 사용
3. live 노드 리스트 vs non-live 노드 리스트
2_1에서 언급한 정적 및 동적 노드리스트에 대한 설명이다.
const itemLi = document.querySelectorAll("li");
console.log(itemLi);
const itemLi2 = document.getElementsByTagName("li");
console.log(itemLi2);
const ul = document.querySelector("ul");
const newLi = document.createElement("li");
ul.appendChild(newLi);
console.log(itemLi);
console.log(itemLi2);
live 노드 리스트의 예시인 getElementsByTagName의 노드리스트는 추가된 요소를 반영하지만, non-live 노드 리스트의 예시인 querySelectorAll의 노드리스트는 반영하지 않는다
→ non-live 노드 리스트가 단점이라는 소리는 아니다,, 상황에 맞게 활용하자
'OLD > Javascript' 카테고리의 다른 글
[Javascript] 자바스크립트 배열_02 (forEach(), map(), 정렬, 필터) (0) | 2023.02.18 |
---|---|
[Javascript] 자바스크립트 배열_01 (Iterable, 유사배열객체, 배열 메서드) (0) | 2023.02.18 |
[Javascript] 속성 vs 프로퍼티 (0) | 2023.02.17 |
[Javascript] 자바스크립트에서의 함수(함수 표현식, 익명함수, Rest 연산자...) (0) | 2023.02.14 |
[Javascript] 윈시 타입(Primitive Type) vs 참조 타입(Reference Type) (0) | 2023.02.12 |