From b42328bf3aa81ede4f32b70ca735434b981811ef Mon Sep 17 00:00:00 2001 From: sehyun Date: Wed, 17 Jan 2024 13:42:15 +0900 Subject: [PATCH] =?UTF-8?q?docs:=2039.2.4=20~=2039.2.6=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\353\202\230\354\204\270\355\230\204.md" | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git "a/docs/39_DOM/\353\202\230\354\204\270\355\230\204.md" "b/docs/39_DOM/\353\202\230\354\204\270\355\230\204.md" index b913dda0..8381ecaf 100644 --- "a/docs/39_DOM/\353\202\230\354\204\270\355\230\204.md" +++ "b/docs/39_DOM/\353\202\230\354\204\270\355\230\204.md" @@ -70,3 +70,135 @@ HTML 요소에 `id` 어트리뷰트를 부여하면 `id` 값과 동일한 이름 `getElementsByClassName` 메서드는 인수로 전달한 `class` 값을 갖는 모든 요소 노드들을 탐색해 반환한다. `getElementsByTagName` 메서드와 마찬가지로 HTMLCollection 객체를 반환하며, `Document.prototype`와 `Element.prototype`에 정의된 메서드가 있다. 인수로 전달된 `class` 값을 갖는 요소가 없다면 빈 HTMLCollection 객체를 반환한다. + +#### CSS 선택자를 이용한 요소 노드 취득 + +- `* { ... }`: 전체 선택자 (모든 요소를 선택) +- `p { ... }`: 태그 선택자 (모든 p 태그 요소를 모두 선택) +- `#foo { ... }`: id 선택자 (id 값이 'foo'인 요소를 모두 선택) +- `.foo { ... }`: class 선택자 (class 값이 'foo'인 요소를 모두 선택) +- `input[type=text] { ... }`: 어트리뷰트 선택자 (input 요소 중에 type 어트리뷰트 값이 'text'인 요소를 모두 선택) +- `div p { ... }`: 후손 선택자 (div 요소의 후손 요소 중 p 요소를 모두 선택) +- `div > p { ... }`: 자식 선택자 (div 요소의 자식 요소 중 p 요소를 모두 선택) +- `p + ul { ... }`: 인접 형제 선택자 (p 요소의 형제 요소 중에 p 요소 바로 뒤에 위치하는 ul 요소를 선택) +- `p ~ ul { ... }`: 일반 형제 선택자 (p 요소의 형제 요소 중에 p 요소 뒤에 위치하는 ul 요소를 모두 선택) +- `a: hover { ... }`: 가상 클래스 선택자 (hover 상태인 a 요소를 모두 선택) +- `p::before { ... }`: 가상 요소 선택자 (p 요소의 콘텐츠의 앞에 위치하는 공간을 선택, 일반적으로 content 프로퍼티와 함께 사용됨) + +`querySelector` 메서드는 인수로 전달한 CSS 선택자를 만족시키는 하나의 요소 노드를 탐색해 반환한다. +여러 개가 있을 경우 첫번째만 반환하며, 인수로 전달한 선택자를 만족시키는 요소 노드가 없는 경우 `null`을 반환한다. + +`querySelectorAll` 메서드는 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색해 반환한다. +여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 NodeList 객체를 반환하며 이는 유사 배열 객체이면서 이터러블이다. + +`getElementsByTagName`, `getElementsByClassName` 메서드와 마찬가지로 `querySelector`, `querySelectorAll` 메서드는 `Document.prototype`에 정의된 메서드와 `Element.prototype`에 정의된 메서드가 있다. + +#### 특정 요소 노드를 취득할 수 있는지 확인 + +`Element.prototype.matches` 메서드는 인수로 전달한 CSS 선택자를 통해 특정 요소를 취득할 수 있는지 확인한다. + +```html + + + + + + + +``` + +#### `HTMLCollection`과 `NodeList` + +DOM 컬렉션 객체인 `HTMLCollection`과 `NodeList`는 DOM API가 여러 개의 결과값을 반환하기 위한 DOM 컬렉션 객체다. +둘 다 유사 배열 객체이면서 이터러블이므로 `for ... of` 문으로 순회할 수 있으며 스프레드 문법을 사용해 간단히 배열로 변환할 수 있다. +**두 객체의 중요한 특징은 노드 객체의 상태 변화를 실시간으로 반영하는 살아 있는 객체라는 것이다.** +`HTMLCollection`은 언제나 live 객체로 동작한다. +`NodeList`는 대부분의 경우 non-live 객체로 동작하지만 경우에 따라 live 객체로 동작할 때가 있다. + +`getElementsByTagName`, `getElementsByClassName` 메서드가 반환하는 `HTMLCollection` 객체는 노드 객체의 상태 변화를 실시간으로 반영하는 살아 있는 DOM 컬렉션 객체다. + +```html + + + + + + + +``` + +위 예제의 결과 모든 `li` 요소의 `class` 값이 `blue`로 변경되어야 할 것 같지만, 예상대로 동작하지 않는다. + +1. 첫번째 반복(`i === 0`): `elems[0]`의 `class` 값이 `red`에서 `blue`로 변경된다. 이때 이 요소는 `getElementsByClassName`의 인자로 전달한 `red`와 일치하지 않게 되기 때문에 `elems`에서 실시간으로 제거된다. +2. 두번째 반복(`i === 1`): 첫번째 반복에서 첫번째 `li` 요소는 `elems`에서 제거되었으므로 `elems[1]`은 세번째 `li` 요소다. 이 세번째 요소의 `class` 값도 `blue`로 변경된 후 실시간으로 제외된다. +3. 세번째 반복(`i === 2`): `elems`에는 두번째 `li` 요소만 남아 있다. 이때 `elems.length`의 값은 `1`이므로 `for`문의 조건식 `i < elems.length`가 거짓으로 평가되어 반복이 종료된다. + +이처럼 `HTMLCollection` 객체는 실시간으로 상태 변경을 반영하므로 이를 `for`문으로 순회하면서 노드 객체의 상태를 변경할 때 주의해야 한다. +이 문제는 다음과 같은 방법으로 해결할 수 있다. + +```javascript +// for문을 역방향으로 순회 +for (let i = elems.length - 1; i >= 0; i--) { + elems[i].className = 'blue'; +} + +// while문으로 HTMLCollection에 요소가 남아 있지 않을 때까지 무한 반복 +let i = 0; +while (elems.length > i) { + elems[i].className = 'blue'; +} + +// 더 간단한 해결책: 부작용을 발생시키는 원인인 HTMLCollection 객체를 사용하지 않기 +[...elems].forEach((elem) => (elem.className = 'blue')); +``` + +`HTMLCollection` 객체의 부작용을 해결하기 위해 `getElementsByTagName`, `getElementsByClassName` 대신 `querySelectorAll` 메서드를 사용하는 방법도 있다. +이 메서드는 DOM 컬렉션 객체인 `NodeList` 객체를 반환하며, 이는 실시간으로 노드 객체의 상태 변경을 반영하지 않는 객체다. +`NodeList` 객체는 `NodeList.prototype.forEach` 메서드를 상속받아 사용할 수 있다. +하지만 **`childNodes` 프로퍼티가 반환하는 `NodeList` 객체는 `HTMLCollection` 객체와 같이 live 객체로 동작하므로 주의가 필요하다.** + +이처럼 `HTMLCollection`이나 `NodeList`는 예상과 다르게 동작할 때가 있기 때문에 노드 객체의 상태 변경과 상관없이 안전하게 배열로 변환한 후 사용하는 것을 권장한다. + +```html + + + + + + + +```