진취적 삶
39 DOM 본문
DOM 은 HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할수있는 API 프로퍼티와 메서드를 제공하는 트리 자료구조이다 .
39.1 노드
39.1.1 HTML 요소와 노드 객체
HTML 요소는 HTML 문서를 구성하는 개별적인 요소를 의미한다.
<div class = 'greeting'>hello </div>
- 시작 태크
- 어트리뷰트 이름
- 어트리뷰트 값
- 콘텐츠
- 종료 태크
html 요소는 랜더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환된다.
HTML 요소의 어트리뷰트는 어트리뷰트 노드로
HMTL 요소의 텍스트 콘텐츠는 텍스트 노드로 변환된다.
HTML 요소 간에는 중첩 관계에 의해 계층적인 부자관계가 형성된다.
트리 자료구조
최상위 구조는 부모 노드가 없으며 루트 노드라 한다.
자식 노드가 없는 노드를 리프 노드라 한다.
노드 객체들로 구성된 트리 자료구조를 DOM 이라 한다.
39.1.2 노드 객체의 타입
문서노드
DOM 트리의 최상위에 존재하는 루트 노드로서 document 객체를 가리킨다.
document 객체는 브라우저가 렌더링한 HTML 문서 전체를 가리키는 객체로서 전역 객체 window의 document 프로퍼티에 바인딩 되어있다.
브라우저 환경의 모든 js 코드는 script 태그에 분리되어있어도 하나의 전역 객체 window를 공유한다.
요소,어트리뷰트 ,텍스트 노드에 접근하려면 문서 노드를 통해야한다.
요소노드
HTML 요소를 가리키는 객체다. HTML 요소간의 중첩에 의해 부자 관계를 가지며 이 부자관계를 통해 정보를 구조화 한다.
어트리뷰트 노드
HTML 요소의 어트리뷰트를 가리키는 객체다 .요소 노드는 부모 노드와 연결되어 있지만
어트리뷰트 노드는 부모 노드와 연결되어있지 않다 .
텍스트 노드
HTML 요소의 텍스트를 가리키는 객체다 . 요소노드의 자식 노드이며,자식 노드를 가질수 없는 리프 노드이다. 텍스트 노드에 접근하려면 부모 노드인 요소 노드에 접근해야한다.
39.1.3 노드 객체의 상속 구조
DOM 은 HTML 문서의 계층적 구조와 정보를 표현하며, 이를 제어할수 있는 API즉 프로퍼티와 메서드를 제공하는 트리 자료구조이다.
DOM을 구성하는 노드 객체는 자신의 구조와 정보를 제어할수 있는 DOM API를 사용할수 있다.
모든 노드 객체는 Object ,EventTarget ,Node 인터페이스를 상속받는다 .
input 요소 노드 객체의 특성 프로토 타입을 제공하는 객체
객체 | Object |
이벤트를 발생시키는 객체 | EventTarget |
트리 자료구조의 노드 객체 | Node |
브라우저가 랜더링 할수 있는 웹문서의 요소 | Element |
웹문서의 요소 중에서 HTML요소를 표현하는 객체 | HTMLElement |
HTML 요소중에서 input 요소를 표현하는 객체 | HTMLInputElement |
노드 객체에는 공통적으로 갖는 기능이있다.
모든 객체는 공통적으로 이벤트를 발생시키는 기능은 EventTarget 에서 제공
트리 탐색 기능(Node.parentNode) 은 Node에서
style 프로퍼티 같은 html 요소가 공통적으로 갖는 기능 HTMLElement에서 제공
DOM 은 HTML 문서의 계층적 구조와 정보를 표현하는 것은 물론 노드 객체의 종류 즉 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 DOM API로 제공한다.
이 DOM API를 통해 HTML 구조나 내용또는 스타일 등을 동적으로 조작할수 있다.
39.2 요소 노드 취득
HTML 구조나 내용또는 스타일등을 동적으로 조작하려면 요소노드를 취득해야한다.
텍스트 노드는 요소노드의 자식노드이고 어트리뷰트 노드는 요소노드와 연결되어있다.
39.2.1 id를 이용한 요소 노드 취득
Document.prototype.getElementById 메서드는 인수로 전달한 id 어트리뷰트 값을 갖는 하나의 요소노드를 탐색하여 반환 . Document.prototype의 프로퍼티이기에 반드시 노드인 document를 통해 호출해야한다.
id 값은 HTML 문서 내에서 유일한 값이어야 한다 .
인수로 전달받은 값이 존재하지 않을경우 getElementById 메서드는 null 반환
39.2.2 태그 이름을 이용한 요소 노드 취득
Document.prototype/Elemet.prototype.getElementsByTagName 메서드는 인수로 전달한 태그 이름을 갖는 모드 요소 노드들을 탐색하여 반환한다.
Elements가 복수형인 것에서 알수 있듯이 getElementsByTagName은 여러개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 HTMLCollection 객체를 반환한다.
<body>
<ul>
<li id="apple">apple</li>
<li id="banana">banana</li>
<li id="orange">orange</li>
</ul>
<script>
const $elems = document.getElementsByTagName("li");
[...$elems].forEach((elem) => {
elem.style.color = "red";
});
</script>
</body>
Document.prototype.getElementsByTagName 메서드는 DOM의 루트노드인 문서노드 ,즉
document를 통해 호출하며 DOM 전체에서 요소 노드를 탐색하여 반환
Element.prototype.getElementsByTagName은 특정 요소노드를 통해 호출하며, 특정 요소 노드의
자손 노드중에서 요소노드를 탐색하여 반환
<body>
<ul id="fruits">
<li>apple</li>
<li>banana</li>
<li>orange</li>
</ul>
<ul>
<li>html</li>
</ul>
<script>
//DOM 전체에서 태그 이름이 li 인 요소를 모두 탐색하여 반환해라
const $listFromDocument = document.getElementsByTagName("li");
console.log($listFromDocument); //HTMLCollection(4) [li, li, li, li]
const $fruits = document.getElementById("fruits");
const $listFromFruits = $fruits.getElementsByTagName("li");
console.log($listFromFruits); //HTMLCollection(3) [li, li, li]
</script>
</body>
39.2.3 class 이용한 요소 노드 취득
Document.prototype/Element.prototype.getElementByClassName
class 어트리뷰트 값을 갖는 모든 요소 노드들을 탐색하여 반환한다. 인수로 전달할 class 값은공백으로 구분하여 여러개의 class 지정할수 있다.
<body>
<ul>
<li class="fruit apple">apple</li>
<li class="fruit banana">banana</li>
<li class="fruit orange">orange</li>
</ul>
<script>
const $elems = document.getElementsByClassName("fruit");
[...$elems].forEach((elem) => {
elem.style.color = "red";
});
const $apple = document.getElementsByClassName("fruit apple");
[...$apple].forEach((elem) => {
elem.style.color = "blue";
});
</script>
</body>
39.2.4 CSS 선택자를 이용한 요소 노드 취득
CSS 선택자는 스타일을 적용하고자 하는 HTML 요소를 특정할때 사용하는 문법이다 .
//전체 선택자
*{...}
//P 태그 선택자
P{...}
//id 선택자 id 값이 foo
#id{...}
//class 선택자 class 값이 foo
.foo{..}
//어트리뷰트 선택자 input 요소중에 type 어트리뷰트 값이 text 인 요소를 모두선택
input[type=text]{...}
// 후손 선택자 div 요소의 후손중에 p 요소
div p {...}
//자식 선택자 : div 요소의 자식 요소중 p
div >p {...}
//인접 형제 선택자 :p요소의 형제 요소중에 p 요소 바로 뒤에 위치
p + ul {...}
//일반 형제 선택자 : p요소의 형제중에 p 요소 e뒤에 위치하는 ul요소를 모두 선택
p ~ ul {...}
//hover 상태인 a 요소를 모두 선택
a:hover { ..}
querySelector 메서드는 인수로 전달한 CSS 선택자를 만족시키는 하나의 요소 노드를 탐색하여 반환한다.
- 인수로 전달한 css 선택자를 만족시키는 요소 노드가 여러 개인 경우 첫번째 요소노드만 반환
- 인수로 전달된 css 선택자를 만족시키는 요소 노드가 존재하지 않는경우 null 반환
- 인수로 전달한 css선택자가 문법에 맞지않는 경우 DOMException 에러가 발생
querySelectorAll 메서드는 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색하여 반환
여러 개의 노드 객체를 갖는 DOM 컬렉션 객체인 NodeList 객체를 반환한다.
<body>
<ul>
<li class="apple">apple</li>
<li class="banana">banana</li>
<li class="orange">orange</li>
</ul>
<script>
//ul 요소의 자식 요소인 li 요소를 모두 탐색하여 반환
const $elems = document.querySelectorAll("ul>li");
console.log($elems); //NodeList(3)
$elems.forEach((elem) => {
elem.style.color = "red";
});
</script>
</body>
id 어트리뷰트가 있는 요소 노드를 취득하는 경우에는 getElementById 메서드를 사용하고 그 외의 경우에는 querySelector ,querySelectorAll 을 사용하는것을 권장
39.2.6 HTMLCollection 과 NodeList
DOM 컬렉션 객체인 HTMLCollection 과 NodeList 는 DOM API 가 여러개의 결과값을 반환하기 위한 DOM컬렉션 객체다 . 모두 유사 객체이면서 이터러블이다.
for .. of 문으로 순회할수 있으며 스프레드 문법을 사용하여 간단히 배열로 변환할수 있다.
중요한 특징은 노드 객체의 상태 변화를 실시간으로 반영하는 살아있는 객체라는것
HTMLCollection 언제나 live 객체로 동작한다. NodeList는 대부분의 경우 노드 객체 상태 변화를 실시간으로 반영하지 않고 과거의 정적 상태를 유지하는 non-live 객체로 동작하지만 경우에 따라 live 객체로 동작할때도 있다 .
HTMLCollection
getElementsByTagName ,getElementsByClassName 메서드가 반환하는 HTMLCollection 객체는
노드 객체의 상태 변화를 실시간으로 반영하는 살아있는 DOM 컬렉션 객체
<body>
<ul id="fruits">
<li class="red">Apple</li>
<li class="red">banana</li>
<li class="red">orange</li>
</ul>
<script>
//class 값이 red 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환
const $elems = document.getElementsByClassName("red");
//이 시점에 HTMLCollection 객체에는 3개의 요소노드가 담겨 있다.
console.log($elems);
//HTMLCollection 객체의 모든 요소의 class 값을 'blue로 변경 '
for (let i = 0; i < $elems.length; i++) {
$elems[i].className = "blue";
}
//HTMLCollection 객체의 요소가 1개로 변경
console.log($elems); //HTMLCollection [li.red]
</script>
</body>
- 첫번째의 반복(i==0)의 경우 red 에서 blue로 변경할때 getElementByClassName 메서드의 인자로 전달한 ‘red’와 더는 일치하지 않기에 $elems에서 실시간으로 제거 이처럼 HTMLCollection 객체는 실시간으로 노드 객체의 상태 변경을 반영하는 살아있는 DOM 컬렉션 객체
- 두번째의 반복(i==1) 의 경우 첫 번째 반복에서 첫번째 li요소는 $elem 에서 제거 따라서 $elems[1] 은 세번째 li 요소다 세번째 li요소값도 blue로 변경되고 마찬가지로 HTMLCollection 객체인 $elems에서 실시간 제외
- 세번째 반복의(i==2) 경우HTMLCollection 객체에 노드 객체가 남아있지 않을때까지 무한 반복하는 방법으로 해결가능
- 두번재 li요소만 남았다. $elems.length 1 이므로 조건식이 false 로 평가되어 종료
while ($elems.length > i) {
$elems[i].className = "blue";
}
그냥 HTMLCollection을 안쓰는게 답
NodeList
HTMLCollection 객체의 부작용을 해결하기 위해 getElementsByTagName ,getElementsByClassName 메서드 대신 querySelector 메서드를 사용하는 방법도 있다.
querySelectorAll 메서드는 DOM 컬렉션 객체인 NodeList 객체를 반환한다.
NodeList 객체는 실시간으로 노드 객체의 상태 변경을 반영하지 않는 객체다 .
HTMLCollection 과 NodeList 객체는 모두 유사 배열 객체이면서 이터러블 .
스프레드 문법이나 Array.from 메서드를 사용하여 간단히 배열로 변환 가능
<body>
<ul id="fruits">
<li>Apple</li>
<li>banana</li>
</ul>
</body>
<script>
const $fruits = document.getElementById("fruits");
//childNodes 프로퍼티는 NodeList 객체를 반환한다.
const { childNodes } = $fruits;
//스프레드 문법을 사용하여 NodeList 객체를 배열로 변환
[...childNodes].forEach((childNode) => {
$fruits.removeChild(childNode);
});
console.log(childNodes); //NodeList(0)
</script>
39.3 노드 탐색
DOM 트리 상의 노드를 탐색할수 있도록 Node, Element 인터페이스는 트리 탐색 프로퍼티를 제공
노드 탐색 프로퍼티는 모두 접근자 프로퍼티이다 . 단 setter 없이 getter 만이 존재 한다 .
39.3.1 공백 텍스트 노드
HTML 요소 사이의 스페이스 탭 줄바꿈 등의 공백 문자는 텍스트 노드를 생성한다.
노드를 탐색할 때는 공백 문자가 생성한 공백 텍스트 노드에 주의해야한다.
39.3.2 자식 노드 탐색
프로퍼티 설명
Node.prototype.childNodes | 자식 노드를 모두 탐색하여 DOM 컬렉션 객체인 NodeList에 담아 반환한다. |
childNodes 프로퍼티가 반환한 NodeList 에는 요소 노드뿐만 아니라 텍스트 노드도 포함되어 있다. | |
Element.prototype.children | 자식 노드중에서 요소 노드만 모두 탐색하여 DOM 컬렉션 객체인 HTMLCollection 에 담아 반환한다. |
children 프로퍼티가 반환한 HTMLCollection 에는 텍스트 노드가 포함되지 않는다. |
<script>
const $fruits = document.getElementById("fruits");
//$fruits 요소의 모든 자식 노드를 취득
//childNodes 프로퍼티가 반환한 NodeList 에는 요소 노드뿐만 아니라 텍스트 노드도 포함
console.log($fruits.childNodes); //NodeList(7) [text, li.apple, text, li.banana, text, li.orange, text]
//$fruits 요소의모든 자식 노드를 탐색
//children 프로퍼티가 반환한 HTMLCollection 에는 요소 노드만 포함
console.log($fruits.children); //HTMLCollection(3) [li.apple, li.banana, li.orange]
console.log($fruits.firstChild); //#text
console.log($fruits.lastChild); //#text
console.log($fruits.firstElementChild); // li.apple
console.log($fruits.lastElementChild); //li.orange
</script>
39.3.3 자식 노드 존재확인
Node.prototype.hasChildNodes 메서드 사용 텍스트 노드를 포함하여 자식의 노드의 존재확인
<script>
const $fruits = document.getElementById("fruits");
console.log($fruits.hasChildNodes()); // true
</script>
39.3.4 요소 노드의 텍스트 노드 탐색
요소 노드의 텍스트 노드는 요소 노드의 자식노드이다.
요소 노드의 텍스트 노드는 firstChild 프로퍼티로 접근 가능
<script>
console.log(document.getElementById("foo").firstChild); //hello
</script>
39.3.5 부모 노드 탐색
Node.prototype.parentNode 사용
텍스트 노드는 DOM트리의 최종단 노드인 leaf node 이므로 부모노드가 텍스트 노드인 경우는 없다.
<script>
const $banana = document.querySelector(".banana");
console.log($banana.parentNode); //ul #fruits
</script>
39.3.6. 형제 노드 탐색
<script>
//노드 탐색의 기점이 되는 $fruits 요소취득
const $fruits = document.getElementById("fruits");
const { firstChild } = $fruits;
//$fruits 요소의 첫번째 자식 노드를 탐색
//firstChild 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드 반환 가능
console.log(firstChild); //#text
const { nextSibling } = firstChild;
//$fruits 요소의 첫번째 자식 노드의 다음 형제 노드 탐색
//nextSibling 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드 반환 가능
console.log(nextSibling); //li.apple
const { previousSibling } = nextSibling;
//$fruits 요소의 첫번째 자식 노드의 다음 형제 노드 탐색의 이전 형제 노드 탐색
//previousSibling 프로퍼티는 요소 노드뿐만 아니라 텍스트 노드 반환 가능
console.log(previousSibling); //#text
const { firstElementChild } = $fruits;
////$fruits 요소의 첫번째 자식 노드를 탐색
//firstElementChild는 요소 노드만 반환
console.log(firstElementChild); //li.apple
const { nextElementSibling } = firstElementChild;
console.log(nextElementSibling); //li.banana
const { previousElementSibling } = nextElementSibling;
console.log(previousElementSibling); //li.apple
</script>
39.4 노드 정복 취득
프로퍼티 설명
Node.prototype.nodeType | 노드 타입을 나타내는 상수를 반환 |
- Node.ELEMENT_NODE:상수 1 반환 *Node.TEXT_NODE : 상수 3 반환 *Node.DOCUMENT_NODE :상수 9반환 | | Node.prototype.nodeName | 노드 이름을 문자열로 반환
- 요소: 대문자 문자열로 태그 이름
- 텍스트 노드:문자열 #text반환
- 문서 노드 :문자열 #document 반환 |
<script>
console.log(document.nodeType); //9
console.log(document.nodeName); // #document
const $foo = document.getElementById("foo");
console.log($foo.nodeType); //1
console.log($foo.nodeName); //DIV
const $textNode = $foo.firstChild;
console.log($textNode.nodeType); //3
console.log($textNode.nodeName); //text
</script>
39.5 요소 노드의 텍스트 조작
39.5.1 nodeValue
setter 와getter 모두 존재하는 접근자 프로퍼티이다.
노드 객체의 값이란 텍스트 노드의 텍스트다.
39.5.2 textContent
요소 노드의 콘텐츠 영역 내의 텍스트를 모두 반환한다 .
<script>
const $foo = document.getElementById("foo");
console.log($foo.textContent == $foo.firstChild.nodeValue); //true
</script>
innerText 프로퍼티의 경우
- innerText 프로퍼티는 CSS에 순종적이다 예를들어 InnerText 프로퍼티는 CSS에 의해 비표시로 지정된 요소 노드의 텍스트를 반환하지 않는다.
- innerText 프로퍼티는 CSS를 고려해야 하므로 textContent 프로퍼티 보다 느리다 .
39.6 DOM 조작
새로운 노드를 생성하여 DOM 에 추가하거나 기존 노드를 삭제 또는 교체하는것을 말한다.
39.6.1 innerHTML
<body>
<div id="foo">hello <span>world!</span></div>
</body>
<script>
console.log(document.getElementById("foo").innerHTML); //hello <span>world!</span>
</script>
textContent의 경우 프로퍼티를 참조하면 HTML 마크업 무시하고 텍스트만 반환
innerHTML 의 경우 HTML 마크업이 포함된 문자열을 그대로 반환
innerHTML 프로퍼티에 할당한 HTML 마크업 문자열은 렌더링 엔진에 의해 파싱되어 요소 노드의 자식으로 DOM 에 반영된다.
입력받은 데이터를 그대로 innerHTML 프로퍼티에 할당하는것은 크로스 사이트 스크립팅 공격에 취약하다 .
HTML 새니티제이션 : 사용자로부터 입력받은 데이터에 의해 발생 할수 있는 크로스 사이트 스크립팅 공격을 예방하기 위해 잠재적 위험을 제거하는 기능을 말한다. DOMPurify 라이브러리를 사용하는것을 권장한다.
innerHTML 프로퍼티의 단점은 요소 노드의 innerHTML 프로퍼티에 HTML 마크업 문자열을 할당하는 경우 요소 노드의 모든 자식 노드를 제거하고 할당한 HTML 마크업 문자열을 파싱하여
DOM 을 변경한다는 것이다 .
innerHTML 프로퍼티에 HTML 마크업 문자열을 할당하면 유지되어도 좋은 기존의 자식 노드까지 모두 제거하고 다시 처음부터 새롭게 자식 노드를 생성하여 DOM에 반영한다.
39.6.2 insertAdjacentHTML 메서드
기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소를 삽입한다.
insertAdjacentHTML 메서드는 두번째 인수로 전달한 HTML 마크업 문자열을 파싱하고 그 결과로 생성된 노드를 첫 번째 인수로 전달한 위치에 삽입하여 DOM에 반영한다.
<body>
<!--beforebegin-->
<div id="foo">
<!--afterbegin-->
text
<!--beforeend-->
</div>
<!--afterend-->
</body>
<script>
const $foo = document.getElementById("foo");
$foo.insertAdjacentHTML("beforebegin", "<p>beforebegin</p>");
$foo.insertAdjacentHTML("afterbegin", "<p>afterbegin</p>");
$foo.insertAdjacentHTML("beforeend", "<p>beforeend</p>");
$foo.insertAdjacentHTML("afterend", "<p>afterend</p>");
</script>
39.6.3 노드 생성과 추가
innerHTML 과 insertAdjacentHTML 메서드는 HTML 마크업 문자열을 파싱하여 노드를 생성하고 DOM에 반영한다.
<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>
요소 노드 생성
createElement 메서드는 요소 노드를 생성하여 반환한다.
매개변수로 태그 이름을 나타내는 문자열을 인수로 전달 .
요소 노드만 생성할뿐 DOM에 추가 하지는 않는다. DOM에 추가하는 처리가 별도로 필요함
const $li = document.createElement('li')
텍스트 노드 생성
createTextNode 메서드는 텍스트 노드를 생성하여 반환한다.
요소 노드의 자식 노드로 추가되지 않고 홀로 존재하는 상태다 .
const textNode = document.createTextNode('banana')
텍스트 노드를 요소 노드의 자식노드로 추가
appendChild 매개변수 childNode에게 인수로 전달한 노드를 appendChild 메서드를 호출한 노드의 마지막 자식 노드로 추가
$li.appendChild(textNode)
appendChild메서드를 통해 요소 노드와 텍스트 노드를 부자관계로 연결 기존 DOM에 추가되지는 않은 상태
요소 노드에 자식 노드가 하나도 없을 경우에는 textContent 프로퍼티를 사용하는 편이 더욱 간편
$li.appendChild(document.createTextNode('banana'))
$li.textContent = 'banana'
요소 노드를 DOM 에 추가
텍스트 노드와 부자 관계로 연결한 요소 노드를 $fruits 요소 노드의 마지막 자식 요소로 추가
$fruits.appendChild($li)
위과정에서 DOM에 한번 추가하므로 DOM은 한번 변경됨 이때 리플로우와 리페인팅 실행
39.6.4 복수의 노드 생성과 추가
DOM 을 여러번 변경하는 문제를 회피하기 위해서 컨테이너 요소를 사용해 보자
컨테이너 요소를 사용할경우 DOM 은 한번만 변경된다 .
<body>
<ul id="fruits"></ul>
<script>
const $fruits = document.getElementById("fruits");
const $container = document.createElement("div");
["a", "b", "c"].forEach((text) => {
//1 요소 노드 생성
const $li = document.createElement("li");
//2 텍스트 노드 생성
const textNode = document.createTextNode(text);
//3 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
$li.appendChild(textNode);
//4
$container.appendChild($li);
});
$fruits.appendChild($container);
</script>
</body>
위와 같은 코드는 불필요한 컨테이너 요소(div)가 추가되는 부작용이 있다.
DocumentFragment 노드를 통해 해결할수 있다. 부모노드가 없어서 기존 DOM 가는 별도로 존재한다는 특징이 있다.
여러개의 요소 노드를 DOM에 추가하는 경우 DocumentFragment 노드를 사용하는것이 효율적
39.6.5 노드 삽입
마지막 노드로 추가
appendChild 메서드는 인수로 전달받은 노드를 자신을 호출한 노드의 마지막 자식 노드로DOM에추가
<script>
const $li = document.createElement("li");
$li.appendChild(document.createTextNode("orange"));
document.getElementById("fruits").appendChild($li);
</script>
지정한 위치에 노드 삽입
Node.prototype.insertBefore(newNode,childNode)메서드는 첫번째 인수로 전달받은 노드를 두번째 인수로 전달받은 노드 앞에 삽입
<script>
const $fruits = document.getElementById("fruits");
const $li = document.createElement("li");
$li.appendChild(document.createTextNode("orange"));
$fruits.insertBefore($li, $fruits.lastElementChild);
</script>
39.6.6 노드 이동
appendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드 추가
<script>
const $fruits = document.getElementById("fruits");
const [$apple, $banana] = $fruits.children;
$fruits.appendChild($apple);
$fruits.insertBefore($banana, $fruits.lastElementChild);
</script>
39.6.7 노드 복사
Node.prototype.cloneNode 메서드는 노드의 사본을 생성하여 반환한다.
매개변수 deep 에 true를 인수로 전달하여 깊은 복사하여 모든 자손노드가 포함된 사본을
생성하고 ,false 인수로 전달하거나 생략하면 얕은 복사하여 노드 자신만의 사본을 생성한다.
얕은 복사로 생성된 요소 노드는 자손 노드를 복사하지 않으므로 텍스트 노드도 없다 .
<script>
const $fruits = document.getElementById("fruits");
const $apple = $fruits.firstElementChild;
//$apple 요소를 얕은 복사하여 사본을 생성 ,텍스트 노드가 없는 사본이 생성
const $shallowClone = $apple.cloneNode(false);
$shallowClone.textContent = "banana";
//사본 요소 노드를 $fruits 마지막 노드로 추가
$fruits.appendChild($shallowClone);
//fruits 요소를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성
const $deepClone = $fruits.cloneNode(true);
$fruits.appendChild($deepClone);
</script>
39.6.8 노드 교체
Node.prototype.repalceChild (newChild,oldChild)메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다.
replaceChild 는 자신을 호출한 노드의 자식노드인 oldChild 노드를 newChild로 교체
<script>
const $fruits = document.getElementById("fruits");
//기존 노드와 교체할 요소 노드를 생성
const $newChild = document.createElement("li");
$newChild.textContent = "banana";
$fruits.replaceChild($newChild, $fruits.firstElementChild);
</script>
39.6.9 노드 삭제
Node.prototype.removeChild 메서드는 Child 매개변수에 인수로 전달한 노드를 DOM에서 삭제
<script>
const $fruits = document.getElementById("fruits");
//fruits 요소 노드의 마지막 요소를 DOM에서 삭제
$fruits.removeChild($fruits.lastElementChild);
</script>
39.7 어트리뷰트
39.7.1 어트리뷰트 노드와 attributes 프로퍼티
HTML 요소의 여러 개의 어트리뷰트를 가질수 있다. HTML 요소의 동작을 제어하기 위한 추가적인 정보를 제공하는 HTML 어트리뷰트는 HTML 요소의 시작 태그에 정의한다 .
HTML 문서가 파싱될때 HTML 요소의 어트리뷰트는 어트리뷰트 노드로 변환되어 요소 노드와 연결된다. 어트리뷰트당 하나의 어트리뷰트 노드가 생성된다.
모든 어트리뷰트 노드의 참조는 유사 배열 객체이자 이터러블인 NameNodeMap 객체에 담겨서 요소 노드의 attributes 프로퍼티에 저장된다 .
attributes 프로퍼티는 getter 만 존재하는 읽기전용 접근자 프로퍼티이며 ,
요소 노드의 모든 어트리뷰트 노드의 참조가 단긴 NameNodeMap 객체를 반환한다.
<body>
<input id="user" type="text" value="suha" />
</body>
<script>
const { attributes } = document.getElementById("user");
console.log(attributes);
//NamedNodeMap {0: id, 1: type, 2: value, id: id, type: type, value: value, length: 3}
console.log(attributes.id.value); //user
console.log(attributes.type.value); //text
console.log(attributes.value.value); //suha
</script>
39.7.2 HTML 어트리뷰트 조작
HTML 어트리뷰트 값을 참조하려면 Element.prototype.getAttribute 메서드를 사용하고
HTML 어트리뷰트 값을 변경하려면 Element.prototype.setAttribute 메서드를 사용한다.
<body>
<input id="user" type="text" value="suha" />
</body>
<script>
const $input = document.getElementById("user");
//value attribute 값 취득
const inputValue = $input.getAttribute("value");
console.log(inputValue);
//value 어트리뷰트 값 변경
$input.setAttribute("value", "foo");
console.log($input.getAttribute("value"));
</script>
39.7.3 HTML 어트리뷰트 VS DOM 프로퍼티
HTML 어트리뷰트의 역할은 HTML 요소의 초기상태를 지정하는것이다.
즉 HTML 어트리뷰트 값은 HTML 요소의 초기 상태를 의미하며 이는 변하지 않는다 .
요소 노드는 상태를 가지고 있다.
input 요소의 입력필드에 ‘abc’ 입력한 경우 사용자 의해 변경된 최신상태를 관리해야하며
또한 HTML 어트리뷰트로 지정한 초기상태도 관리해야한다. 초기 상태값을 관리하지 않을경우
웹페이지 새로고침하면 초기 상태를 표기할수 없다.
요소 노드는 2개의 상태 ,즉 초기와 최신 상태를 관리해야한다. 요소 노드의 초기상태는 어트리뷰트 노드가 관리하며 요소노드의 최신 상태는 DOM 프로퍼티가 관리한다.
어트리뷰트 노드
HTML 어트리뷰트로 지정한 HTML 요소의 초기 상태는 어트리뷰트 노드에서 관리한다.
어트리 뷰트가 관리하는 초기 상태값을 취득하거나 변겨하려면 getAttributes,setAttributes 사용하면 된다 .
DOM 프로퍼티
사용자가 입력한 최신상태는 HTML 어트리뷰트에 대응하는 요소 노드의 DOM 프로퍼티가 관리한다. DOM 프로퍼티는 사용자의 입력에 의한 상태변화에 반응하여 언제나 최신 상태를유지
사용자 입력에 의한 상태 변화와 관계없는 id 어트리뷰트와 id 프로퍼티는 사용자 입력과 관계없이 항상 동일한 값을 유지한다.
39.8 스타일
39.8.1 인라인 스타일 조작
HTMLElement.prototype.style 프로퍼티는 setter 와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 인라인 스타일을 취득하거나 추가 또는 변경한다.
<script>
const $div = document.querySelector("div");
//인라인 스타일 취득
console.log($div.style);
$div.style.color = "blue";
$div.style.width = "100px";
$div.style.height = "100px";
$div.style.backgroundColor = "purple";
</script>
CSSStyleDeclaration 타입 객체를 반환
다양한 CSS 프로퍼티에 대응하는 프로퍼티를 가지고 있으며, 이 프로퍼티에 값을 할당하면 해당 CSS 프로퍼티가 인라인 스타일로 HTML 요소에 추가되거나 변경된다.
CSSStyleDeclaration객체의 프로퍼티는 카멜케이스
CSS프로퍼티는 케밥 케이스
39.8.2 클래스 조작
<style>
.box {
width: 100px;
height: 100px;
background-color: antiquewhite;
}
.red {
color: red;
}
.blue {
color: blue;
}
</style>
</head>
<body>
<div class="box red">hello world</div>
</body>
<script>
const $box = document.querySelector(".box");
console.log($box.className);
$box.className = $box.className.replace("red", "blue");
</script>
classList
class 어트리뷰트의 정보를 담은 DOMTokenList 객체 반환
<body>
<div class="box red">hello world</div>
</body>
<script>
const $box = document.querySelector(".box");
console.log($box.className);
$box.classList.replace("red", "blue");
</script>
DOMTokenList 객체는 class 어트리뷰트의 정보를 나타내는 컬렉션 객체로서 유사배열 객체이면서 이터러블이다 .
- add (…className)
$box.classList.add('foo') // -> class ="foo"
$box.classList.add('bar','baz') // -> class = 'foo bar baz'
- remove(..className)
$box.classList.remove('foo') // -> class = 'bar baz'
- item (index) item 메서드는 인수로 전달한 index 에 해당하는 클래스를 class 어트리뷰트에서 반환한다.
$box.classList.item(0) // bar
- contains(className)
$box.classList.contains('bar') //true
- replace (oldClassName,NewClassName)
$box.classList.repalce('red','blue')/ class = "box blue"
- toggle (className [.force]) class 어트리뷰트에 인수로 전달한 문자열과 일치하는 클래스가 존재하면 제거하고 존재하지 않으면 추가
$box.classList.toggle('foo') //class = 'box blue foo'
$box.classList.toggle('foo') //class ='box blue '
'개발 도서 > 자바스크립트 deepdive' 카테고리의 다른 글
37 set 과 map (0) | 2023.07.13 |
---|---|
38 브라우저의 렌더링 과정 (0) | 2023.07.13 |
40 이벤트 (0) | 2023.07.13 |
41 타이머 (0) | 2023.07.13 |
42 비동기 프로그래밍 (0) | 2023.07.13 |