DOM (2) - 요소 노드 취득과 노드 탐색

디아·2022년 7월 1일
0
post-thumbnail

요소 노드 취득

HTML의 구조나 내용 또는 스타일 등을 동적으로 조작하기 위해서는 해당 요소 노드를 취득해야 한다. 다양한 메소드를 통해 노드를 탐색하여 특정 요소 노드를 취득할 수 있다.

1. id를 이용한 요소 노드 취득 - getElementById()

Document.prototype.getElementById 메소드를 통한 취득 방법. getElementById 메소드는 Document.prototype의 프로퍼티이다. 따라서 문서 노드인 document를 통해 호출할 수 있다.

중복된 id 값이 여러 개 있는 경우, 맨 처음에 탐색한 요소 노드를 반환

<!DOCTYPE html>
<html>
	<body>
		<ul>
			<li id="two">First</li>
			<li id="two">Second</li>
			<li id="two">Third</li>
		</ul>
		<script>
			const $elem = document.getElementById('two');

			$elem.style.color = 'blue'
		</script>
	</body>
</html>

인수로 전달된 id 값이 존재하지 않는 경우, TypeError 발생

<!DOCTYPE html>
<html>
	<body>
		<ul>
			<li id="first">First</li>
			<li id="second">Second</li>
			<li id="third">Third</li>
		</ul>
		<script>
			const $elem = document.getElementById('two'); // null

			$elem.style.color = 'blue' // TypeError : Cannot read of properties of null
		</script>
	</body>
</html>
  • html 요소에 id 속성을 부여하면 id 값과 동일한 이름으로 전역 변수가 선언되고 해당 전역 변수에 노드 객체가 할당된다.
    <!DOCTYPE html>
    <html>
    	<body>
    		<div id="bar"></div>
    		<script>
    			console.log(bar === document.getElementById('bar')); // true
    						
    			delete bar;
    			// 전역 프로퍼티는 삭제되지만 전역 변수는 삭제 되지 않는다.
    			console.log(bar); // <div id="bar"></div>
    		</script>
    	</body>
    </html>
  • 이미 id 값과 동일한 이름의 전역 변수가 선언되어 있다면 해당 변수에 노드 객체를 재할당 하지 않는다.
<!DOCTYPE html>
<html>
  <body>
    <div id="bar"></div>
    <script>
      let bar = 'hi';

      // 이미 id 값과 동일한 이름으로 전역 변수가 선언 되어 있으므로 해당 전역 변수에 노드객체를 재할당하지 않는다.
      console.log(bar); // 'hi'
    </script>
  </body>
</html>

2. 태그 이름을 이용한 요소 노드 취득 - getElementsByTagName()

  • getElementsByTagName 메서드가 인자로 전달받은 태그 이름을 가진 모든 요소 노드들을 탐색하여 유사 배열이자 이터러블인 HTMLCollection 객체에 담아 반환한다.
  • HTML 문서의 모든 요소 노드를 취득하고 싶다면 getElementsByTagName 메서드에 * 를 인수로 넘기면 된다.
  • Document.prototype.getElementsByTagName → 루트 노드인 document를 통해 호출하였으므로 전체 HTML 문서, 즉 DOM 전체에서 탐색한다.
  • Element.prototype.getElementsByTagName → 특정 요소를 통해 호출하였으므로 해당 요소의 자식 요소 노드들을 탐색한다.
  • 인수로 전달된 태그 이름을 갖는 요소가 없는 경우 → 빈 HTMLCollection 객체를 반환

3. class를 이용한 요소 노드 취득 - getElementsByClassName()

  • getElementsByClassName 메서드가 인자로 전달받은 class 값을 가진 모든 요소 노드들을 탐색하여 유사 배열이면서 이터러블인 HTMLCollection 객체에 담아 반환한다.
  • Document.prototype.getElementsByClassName → 루트 노드인 document를 통해 호출하였으므로 전체 HTML 문서, 즉 DOM 전체에서 탐색한다.
  • Element.prototype.getElementsByClassName → 특정 요소를 통해 호출하였으므로 해당 요소의 자식 요소 노드들을 탐색한다.
  • 인수로 전달된 클래스 값을 갖는 요소가 없는 경우 → 빈 HTMLCollection 객체를 반환

4. CSS 선택자를 이용한 요소 노드 취득

querySelector()

  • 인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 여러 개인 경우 - 첫번째 요소 노드만 반환
  • 인수로 전달된 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 - null 반환
  • 인수로 전달된 CSS 선택자가 문법에 맞지 않는 경우 - DOMException 에러 발생

querySelectorAll()

  • DOM 컬렉션 객체인 NodeList 객체를 반환. NodeList는 유사 배열이자 이터러블이다.
  • 인수로 전달된 CSS 선택자를 만족시키는 요소가 존재하지 않는 경우 - 빈 NodeList 객체 반환
  • CSS 선택자가 문법에 맞지 않는 경우 - DOMException 에러 발생
  • HTML 문서의 모든 요소 노드를 취득하려면 인수로 * 을 넘겨준다.
  • Document.prototype 프로퍼티의 querySelectorAll 메서드를 사용하면 HTML 문서 전체(DOM 전체)에서 노드 탐색하고, 특정 요소 노드를 통한 Element.prototype 프로퍼티의 메서드를 사용하면 해당 요소 노드의 자식 요소 노드들을 탐색한다.

querySelector, querySelectorAll 메서드가 getElementBy 메서드보다 처리속도가 느리지만 권장하는 이유

  • 처리속도는 느리지만 querySelector가 더 구체적인 조건으로 노드를 선택할 수 있다.
    • getElementBy 메서드로 요소를 가져오려면 필요하지 않은 class나 id를 꼭 부여해야만 한다.
  • getElementsByClassName, getElementsByTagName 메서드는 유사 배열인 HTMLCollection 객체를 반환한다. 단일 요소만 가져오고 싶은 경우 [0] 을 붙여야 한다. 이는 그리 보기 좋은 코드가 되지 못한다.

👉 id 요소 가져올때는 getElementById를 사용하고, 그 외에는 querySelector를 사용하는 것이 성능 측면에서는 조금 떨어질지몰라도 생산성, 편의성 측면에서 좋다.

특정 요소 노드 취득가능 유무 확인하기

Element.prototype.matches 메서드로 확인가능. 인수로 전달한 CSS 선택자로 요소 노드 취득 가능한지 확인할 수 있다.

...
<ul id="colors">
	<li class="red">Red</li>
	<li class="yellow">Yellow</li>
	<li class="blue">Blue</li>
</ul>
...
<script>
	const $yellow = document.querySelector('.yellow')

	// $yellow 노드는 '#colors > li.yellow'로 취득할 수 있다.
	console.log($yellow.matches('#colors > li.yellow') // true

	// $yellow 노드는 '#colors > li.banana'로 취득할 수 없다.
	console.log($yellow.matches('#colors > li.banana') //false
</script>

HTMLCollection

노드 객체의 상태 변화를 실시간으로 반영하는 live 객체

<html>
	<body>
		<ul>
			<li class="red">First</li>
			<li class="red">Second</li>
			<li class="red">Third</li>
		<ul>
		<script>
			const $elems = document.getElementsByClassName('red');
			for(let i = 0; i < $elems.length; i++){
				$elems[i].className = 'blue'
			}	

			console.log($elems) // HTMLCollection(1) [li.red]
		// 왜 이런 결과가 나오는가?
        // 반복할 때마다 $elems가 변하기 때문
		</script>
	</body>
</html>
  • 실시간으로 노드 객체의 상태 변경을 반영하여 요소를 제거할 수 있으므로 for문으로 순회하면서 노드 객체 상태 변경 시에는 주의해야 한다.
  • 이런 부작용을 피하는 방법은 HTMLCollection 객체를 사용하지 않고 이를 배열로 변환하여 forEach, map 등의 고차함수를 사용하는 것이다.

NodeList

실시간으로 노드 객체의 상태 변경을 반영하지 않는 non-live 객체. 단, childNodes 프로퍼티가 반환하는 NodeList 객체는 live 객체.

  • HTMLCollection 객체와 마찬가지로 live 객체의 상태 변경 부작용을 피하기 위해 배열로 변환하여 배열 고차함수를 활용하는 것이 좋다.

노드 탐색

노드 탐색 프로퍼티는 참조만 가능한 읽기 전용 프로퍼티.

1. 공백 텍스트 노드

HTML문서에 스페이스, 탭, 엔터 키로 공백을 입력하면 공백 텍스트 노드가 생긴다.

2. 자식 노드 탐색

자식 노드 존재 확인

  • Node.prototype 프로퍼티인 hasChildNodes() 메서드 사용하여 확인가능
  • 자식 노드 존재할 시 true, 존재하지 않을 시 false 반환
  • 텍스트 노드를 포함하여 자식 노드 존재 확인한다. 즉, 공백 텍스트 노드도 포함한다는 의미.
    • 공백 텍스트 노드를 제외한 자식 노드 확인 위한 방법 : children.length, childElementCount

3. 부모 노드 탐색

Node.prototype.parentNode 프로퍼티로 탐색 가능

4. 형제 노드 탐색

DOM 인터페이스프로퍼티설명텍스트 노드 포함 유무
Node.prototypepreviousSibling형제 노드 중 자신의 이전 형제 노드 반환O
Node.prototypenextSibling형제 노드 중 자신 다음 형제 노드 반환O
Element.prototypepreviousElementSibling형제 노드 중 자신의 이전 형제 노드 반환X
Element.prototypenextElementSibling형제 노드 중 자신 다음 형제 노드 반환X

노드 정보 취득

노드 정보 프로퍼티도 참조만 가능한 읽기 전용 프로퍼티이다.

DOM 인터페이스프로퍼티설명
Node.prototypenodeType노드 타입을 나타내는 상수 반환
· 1 : Node.ELEMENT_NODE. 요소 노드 타입
· 3 : Node.TEXT_NODE. 텍스트 노드 타입
· 9: Node.DOCUMENT_NODE. 문서 노드 타입
Node.prototypenodeName노드 이름을 문자열로 반환
- 요소 노드 : 태그 이름을 대문자 문자열로 반환. "UL", "LI", “DIV”
- 텍스트 노드 : 문자열 #text 반환
- 문서 노드 : 문자열 #document 반환




참고자료
getElementById와 querySelector, 어느 것을 사용할까?

profile
얼레벌레 프론트엔드 개발자

0개의 댓글