1. 요소 노드의 텍스트 조작
nodeValue : setter, getter 모두 존재하는 접근자 프로퍼티이다.
따라서 nodeValue 프로퍼티는 참조와 할당 모두 가능함
노드 객체의 nodeValue 프로퍼티를 참조하면 노드 객체의 값을 반환함
노드 객체의 값 이란 텍스트 노드의 텍스트이다. 따라서 텍스트 노드가 아닌 노드, 즉 문서 노드나 요소 노드의 nodeValue 프로퍼티를 참조하면 null 반환한다.
텍스트 노드의 nodeValue 프로퍼티를 참조할 때만 텍스트 노드의 값, 즉 텍스트를 반환한다.
2. textContent
- Node.prototype.textContent 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경한다.
- 요소 노드의 textContent 프로퍼티를 참조하면 요소 노드의 콘텐츠 영역 내의 텍스트를 모두 반환한다. 이때 HTML 마크업은 무시된다.
<!DOCTYPE html>
<html>
<body>
<div id="foo">Hello <span>world!</span></div>
</body>
<script>
// # foo 요소 노드의 텍스트를 모두 취득함
// 이때 HTMl 마크 무시됨
console.log(document.getElementById('foo').textContent); // Hello world!
</script>
</html>
3. DOM 조작
- 새로운 노드를 생성하거나 DOM 에 추가하거나 기존 노드를 삭제, 교체하는 것을 말함
- DOM 조작에 의해 DOM에 새로운 노드가 추가되거나 삭제되면 리플로우와 리페인트가 발생하는 원인이 되므로 성능에 영향을 준다.
- 따라서 복잡한 콘텐츠를 다루는 DOM 조작은 성능 최적화를 위해 주의해서 다루어야 함
3-1. innerHTML
- Element.prototype.innerHTML 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 HTML 마크업을 취득하거나 변경한다.
- 요소 노드의 콘텐츠 영역 내에 포함된 모든 HTML 마크업을 문자열로 반환
- HTML 마크업 문자열로 간단히 DOM 조작 가능
- 주의! 사용자로부터 입력받은 데이터를 그대로 innerHTML 프로퍼티에 할당하는 것은 크로스사이트 스크립트 공격에 취약하므로 위험-> 알성 코드가 포함되어있다면 파싱 과정에서 그대로 실행될 위험이 있다.
->> 간단하고 직관적이라는 장점이 있으나 크로스사이트 스크립티 공격에 취약한 단점이 있음
3-2. insertAdjacentHTML 메서드
- Element.prototype.insertAdjacentHTML(position, DOMString) 메서드는 기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다.
- 두 번째 인수로 전달한 HTML 마크업 문자열(DOMString)을 파싱하고 그 결과로 생성된 노드를 첫 번째 인수로 전달한 위치(position)에 삽입하여 DOM에 반영한다.
- 첫 번째 인수로 전달할 수 있는 문자열은 'beforebegin', 'afterbegin', 'beforeend', 'afterend'의 4가지다.
<!DOCTYPE html>
<html>
<body>
<!-- beforebegin -->
<div id="foo">
<!-- afterbegin -->
text
<!-- beforeend -->
</div>
<!-- afterend -->
</body>
<script>
const $foo = document.getElementById('foo')
$foo.insertAdjacentHTML('beforebegin', '<p>beforebigin</p>')
$foo.insertAdjacentHTML('afterbegin', '<p>afterbigin</p>')
$foo.insertAdjacentHTML('beforeend', '<p>beforeend</p>')
$foo.insertAdjacentHTML('afterend', '<p>afterend</p>')
</script>
</html>
- 기존 요소에는 영향을 주지 않고 새롭게 삽입될 요소만을 파싱하여 자식 요소로 추가하므로 innerHTML 보다 효율적이고 빠르다.
- 단, HTML 마크업 문자열을 파싱하므로 크로스 사이트 스크립팅 공격에 취약하다는 점은 동일
3-3. 노드 생성과 추가
DOM은 노드를 직접 생성/삽입/삭제/치환하는 메서드도 제공한다
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 1. 요소 노드 생성
const $li = document.createElement('li');
// 2. 텍스트 노드 생성
const textNode = document.createTextNode('Banana');
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);
// 4. $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($li);
</script>
</html>
-요소노드생성:
- Document.prototype.createElement(tagName) 메서드는 요소 노드를 생성하여 반환한다.
- 매개변수에는 태그 이름을 나타내는 문자열을 인수로 전달
// 1. 요소 노드 생성
const $li = document.createElement('li');
-텍스트 노드 생성:
- Document.prototype.createTextNode(text) 메서드는 텍스트 노드를 생성하여 반환한다.
- 매개변수에는 텍스트 노드의 값으로 사용할 문자열을 인수로 전달
- 요소노드의 자식 노드로 추가되지 않고 홀로 존재하는 상태임
- 텍스트 노드를 요소 노드에 추가하는 처리가 별도로 필요
// 2. 텍스트 노드 생성
const textNode = document.createTextNode('banana');
-텍스트노드를 요소 노드의 자식 노드로 추가:
- Node.prototype.appendChild(childNode) 메서드는 매개변수 childNode에게 인수로 전달한 노드를 appendChild 메서드를 호출한 노드의 마지막 자식 노드로 추가한다.
// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);
-요소 노드를 DOM에 추가:
- Node.prototype.appendChild 메서드를 사용하여 텍스트 노드와 부자 관계로 연결한 요소 노드를 #fruits 요소 노드의 마지막 자식 요소로 추가한다.
- 이 과정에서 비로소 새롭게 생성한 요소 도느가 DOM에 추가
// 4. $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.appendChild($li);
요소 노드에 자식 노드가 하나도 없는 경우에는 텍스트 노드를 생성하여 요소 노드읮 ㅏ식 노드로 텍스트 노드를 추가하는 ㄱ덧보다
textContent 프로퍼티를 사용하는 게 더욱 간편 할 것
4. 복수의 노드 생성과 추가
- DocumentFragment 노드는 문서, 요소, 어트리뷰트, 텍스트 노드와 같은 노드 객체의 일종으로, 부모 노드가 없어서 기존 DOM과는 별도로 존재한다는 특징있음
- 자식 노드들의 부모 노드로서 별도의 서브 DOM을 구성하여 기존 DOM에 축가하기 위한 용도로 사용
- 기존 DOM과는 별도로 존재하므로 DocumentFragment 노드에 자식 노드를 추가하여도 기존 DOM에는 어떠한 변경도 발생하지 않는다. 또한 DocumentFragment 노드를 DOM에 추가하면 자신은 제거되고 자신의 자식 노드만 DOM에 추가됨
5. 노드 삽입
- 마지막 노드로 추가 : Node.prototype.appendChild 메서드는 인수로 전달받은 노드를 자신을 호출한 노드의 마지막 자식 노드로 DOM에 추가한다
- 지정한 위치에 노드 삽입 : Node.prototype.insertBefore(newNode, childNode) 메서드는 첫 번째 인수로 전달받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 요소 노드 생성
const $li = document.createElement('li');
// 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
$li.appendChild(document.createTextNode('Orange'));
// $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
$fruits.insertBefore($li, $fruits.lastElementChild);
// Apple - Orange - Banana
</script>
</html>
6. 노드 이동
DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉, 노드가 이동
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits')
// 이미 존재하는 요소 노드를 취득
const [$apple, $banana] = $fruits.children;
// 이미 존재하는 $apple 요소 노드를 #fruits 요소 노드의 마지막 노드로 이동
$fruits.appendChild($apple);
$fruits.insertBefore($banana, $fruits.lastElementChild);
// Orange - Banana - Apple
</script>
</html>
7. 노드 복사
- Node.prototype.cloneNode([dee: true | false]) 메서드는 노드의 사본을 생성하여 반환한다.
- 매개 변수 deep에 true면 깊은복사하여 모든 자손 노드가 포함된 사본을 생성하고,
- false거나 생략하면 얕은 복사를 하여 노드 자신만의 사본을 생성.
얕은 복사로 생성된 요소는 자손 노드를 복사하지 않으므로 텍스트 노드도 없다.
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
const $apple = $fruits.firstElementChild;
// $apple 요소를 얕은 복사하여 사본을 생성. 텍스트 노드가 없는 사본이 생성
const $shallowClone = $apple.cloneNode();
// 사본 요소 노드에 텍스트 추가
$shallowClone.textContent = 'Banana';
// 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
$fruits.appendChild($shallowClone);
// #fruits 요소를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성
const $deepClone = $fruits.cloneNode(true);
// 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
$fruits.appendChild($deepClone);
</script>
</html>
8. 노드 교체
- Node.prototype.replaceChild(newChild, oldChild) 메서드는 자신을 호출한 노드의 자식 노드를 다은 노드로 교체한다.
- 첫 번째 매개변수는 newChild에는 교체할 새로운 노드를 인수로 전달하고,
두 번째 매개변수 oldChild에는 이미 존재하는 교체될 노드를 인수로 전달
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// 기존 노드와 교체할 요소 노드를 생성
const $newChild = document.createElement('li');
$newChild.textContent = 'Banana';
// #fruits 요소 노드의 첫 번째 요소 노드를 $newChild 요소 노드로 교체
$fruits.replaceChild($newChild, $fruits.firstElementChild);
</script>
</html>
9. 노드 삭제
- Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다.
- 인수로 전달한 노드는 removeChild 메서드를 호출한 노드의 자식 노드
<!DOCTYPE html>
<html>
<body>
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
</ul>
</body>
<script>
const $fruits = document.getElementById('fruits');
// #fruits 요소 노드의 마지막 요소를 삭제
$fruits.removeChild($fruits.lastElementChild);
</script>
</html>
10. 어트리뷰트
- 동작을 제어하기 위한 추가적인 정보를 제공하는 HTML 어트리뷰트
- 시작태그에 어트리뷰트 이름 = "어트리뷰트 값" 형식으로 정의함
- 공통적으로 사용가능한 것도 있고 특정 HTML 요소에만 사용가능한 어트리뷰트도 있음
- HTML 문서가 파싱될 때 HTML 요소의 어트리뷰트(이하 HTML 어트리뷰트)는 어트리뷰트 노드로 변환되어 요소 노드와 연결된다.
- 이때 HTML 어트리뷰트당 하나의 어트리뷰트 노드가 생성된다.
- 모든 어트리뷰트 노드의 참조는 유사 배열 객체이자 이터러블인 NameNodeMap 객체에 담겨서 요소 노드의 attributes 프로퍼티에 저장
- 따라서 요소 노드의 모든 어트리뷰터 노드는 요소 노드의 Element.prototype.attributes 프로퍼티로 취득할 수 있다.
- getter만 존재하는 읽기 전용 접근자 프로퍼티이며, 요소 노드의 모든 어트리뷰트 노드의 참조가 담긴 NameNodeMap 객체를 반환
11. HTML 어트리뷰트 조작
Element.prototype.getAttribute/setAttribute 메서드를 사용하면 attributes 프로퍼티를 통하지 않고 요소 노드에서 메서드를 통해 직접 HTML 어트리뷰트 값을 취득하거나 변경할 수 있어서 편리
- Element.prototype.hasAttribute - 특정 어트리뷰트가 존재하는지 확인
- Element.prototype.removeAttribute - 특정 어트리뷰트 삭제
12. HTML 어트리뷰트 vs DOM 프로퍼티
요소 노드 객체에는 HTML 어트리뷰트에 대응하는 DOM 프로퍼티가 존재한다. 이 DOM 프로퍼티들은 HTML 어트리뷰트 값을 초기값으로 가지고 있다
- DOM 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티다. 따라서 DOM 프로퍼티는 참조와 변경이 가능
- HTML 어트리뷰트는 DOM에서 중복 관리되고 있는 것처럼 보이지만 그렇지 않다.
- 요소 노드는 상태를 가지고 있다. 요소녿는 2개의 상태 (초기 상태-최신 상태)를 관리해야함...
- 요소 노드의 초기상태는 어트리뷰트 노드가 관리하며 요소 노드의 최신상태는 DOM 프로퍼티가 관리함
-어트리뷰트 노드 :
- HTML 어트리뷰트로 지정한 HTML 요소의 초기 상태는 어트리뷰트 노드에서 관리한다.
- 사용자의 입력에 의해 상태가 변경되어도 변하지 않고 결과는 언제나 유지
- 즉 HTML 어트리뷰트로 지정한 HTML 요소의 초기 상태를 그대로 유지
- setAttribute 메서드
-DOM 프로퍼티 :
- 사용자가 입력한 최신 상태는 HTML 어트리뷰트에 대응하는 요소 노드의 DOM 프로퍼티가 관리
- DOM 프로퍼티는 사용자의 입력에 의한 상태 변화에 반응하여 언제나 최신 상태를 유지
- 동적으로 변경됨...
- 사용자가 상태를 변경하는 행위와 같음
- 모든 DOM 프로퍼티가 사용자의 입력에 의해 변경된 최신상태를 반영하는 것은 아님...-> 사용자 입력에 관계 없는 프로퍼티나 어트리뷰트는 항상 동일한 값을 유지한다. . .
13. HTML 어트리뷰트와 DOM 프로퍼티의 대응관계
-> 대부분의 HTML 어트리뷰트는 HTML 어트리뷰트 이름과 동일한 DOM 프로퍼티와 1:1로 대응한다.
-> 단, 다음과 같이 HTML 어트리뷰트와 DOM 프로퍼티가 언제나 1:1로 대응하는 것은 아니며, 키가 반드시 일치하는 것도 아니다.
- id 어트리뷰트와 id프로퍼티는 1:1대응하며, 동일한 값으로 연동한다.
- input 요소의 value 어트리뷰트는 value 프로퍼티와 1:1 대응한다. 하지만 value 어트리뷰트는 초기상태를, value 프로퍼티는 최신상태를 갖는다.
- class 어트리뷰트는 className, classList 프로퍼티와 대응한다.
- for 어트리뷰트는 htmlFor 프로퍼티와 1:1 대응한다.
- td 요소의 colspan 어트리뷰트는 대응하는 프로퍼티가 존재하지 않는다.
- textContent 프로퍼티는 대응하는 어트리뷰트가 존재하지 않는다.
- 어트리뷰트 이름은 대소문자를 구별하지 않지만 대응하는 프로퍼티 키는 카멜 케이스를 따른다.
14. DOM 프로퍼티 값의 타입
- getAttribute 메서드로 취득한 어트리뷰트 값은 언제나 문자열이다.
- 하지만 DOM 프로퍼티로 취즉한 최신 상태 값은 문자열이 아닐 수 있다.
'Deep Dive 정리' 카테고리의 다른 글
[JS Deep Dive] 41장 - 타이머 (0) | 2024.12.26 |
---|---|
[JS Deep Dive] 40장 - 이벤트 (0) | 2024.12.26 |
[JS Deep Dive] 39장 - DOM (2) (0) | 2024.11.21 |
[JS Deep Dive] 39장 - DOM (0) | 2024.11.21 |
[JS Deep Dive] 35장 - 스프레드 문법 (0) | 2024.11.14 |