DOM = Document Object Model
JavaScript-Browser는 항상 상호작용을 한다.
HTML document가 다운로드되면 브라우저는 이걸 분석하고 랜더링한다.(Parse & Render)
Web API를 사용 가능 → JavaScript가 브라우저와 상호작용할 수 있게 됨. → DOM은 결국 로드 및 렌더링 된 HTML 코드. 정확히 말하자면, JavaScript와 이용할 수 있는 브라우저가 배후에서 만든 코드를 나타낸다.
JavaScript는 DOM을 이용해서 접근할 수 있다.
DOM은 브라우저에 엄격히 묶여있지 않다. Python이나 다른 도구를 이용해서 HTML을 읽어들여올 수 있다.
JavaScript는 Hosted된 언어이다 => 브라우저가 JavaScript를 실행할 환경을 제공해주고 있기 때문
alert는 window.alert와 동일하게 작동한다.document도 window안에 포함되어 있다!<!DOCTYPE html>
<html lang="en">
<head>
<!-- html과 head 사이의 빈 공간(indent)도 사실 브라우저의 노드로 변환되어 노드 트리의 일부가 되었다!(Text Node) -->
<title>DOM Interaction</title>
</head>
<body>
<header>
<h1>Dive into the DOM</h1>
</header>
<main>
<p>There's a lot to it!</p>
</main>
</body>
</html>
<html> → html 노드. 최상위 노드 - <head>와 <body>가 자식 노드<head> - <title>이 자식 노드<title><body> - <header>가 자식 노드<header> - <h1>이 자식 노드<h1><main> - <p>가 자식 노드<p><h1>Dive into the DOM!</h1> == $0라고 나와있는데 console에서 $0를 입력하면 선택한 요소를 볼 수 있다.querySelector(), getElementByID()
querySelectorAll(), getElementByTagName(),...
getElementsByTagName : 특정 HTML 태그가 있는 모든 요소를 제공querySelectorAll은 정적 NodeList, 즉 현재 렌더링 된 DOM의 스냅샷을 제공하는 반면. getElementsByTagName 종류의 메서드는 동적 NodeList를 제공한다. → 요소를 추가하거나 제거하는 경우 getElementsByTagName은 반영이 되지만 querySelectorAll은 반영되지 않는다.console.dir(document.getElementById("main-title"));
const h1 = document.getElementById("main-title");
document.getElementsByClassName("list-item");
// 요즘엔 querySelector를 사용하는게 더 보편적임
document.querySelectorAll(".list-item");
document.querySelectorAll("ul li:first-of-type"); // 첫번째로 나와있는 항목을 선택하는 CSS 선택자가 된다.
const ul = document.querySelector("ul");
ul.querySelector("li"); // 자식 노드 Select. 단 가장 첫번째 것을 선택.
document.body; // <body> 요소 노들르 선택
document.head; // <head> 요소를 선택
document.documentElement; // <html> 요소를 선택
<p id="welcome-text" class="text-default">Welcome!</p>
const p = document.getElementById("welcome-text");
p.textContent; // "Welcome!"
p.id; // "welcome-text"
p.className; // "text-default"
p.className = "new-class"; // <p.. class="new-class">
p.style.backgroundColor = "orange";
p.style.color = "white";
console.dir(p1);
<input id="input-1" class="input-default" value="Enter text..." />
id="input-1", class="input-default", value="Enter text..." → HTML 태그에 추가되는 것은 해당 태그의 속성. 브라우저가 이 속성으로 수행하는 작업은 DOM 개체를 생성(태그 이름을 기반으로)만약 const input을 통해서 input 변수에 저장을 한다면, input을 통해서 id, className, value를 읽을 수 있다.
input.id;
input.className;
input.value;
// 예시
const input = document.querySelector("input");
console.dir(input);
input.value = "hello world~~"; // 사용자 인터페이스에는 반영 됨. 하지만 Elements창(HTML)에서 본 input의 value값은 default value로 그대로이다!
// 이전의 속성이므로 리셋한 것이 반영되지 않음.
// id, class
const h1 = document.getElementById("main-title");
h1.id; // main-title
h1.id = "new-id"; // Element창(HTML)으로 가서 보면, id 속성값이 변경되어 있음을 알 수 있다.
// class 또한 변경된 값이 속성값에 반영됨.
만약, 속성을 변경하고 싶다면?
const input = document.querySelector("input");
input.setAttribute("value", "some other default text"); // 첫번쨰 인수 : 바꾸고자 하는 속성, 두번째 : 내용
// 속성값이 변한다!
// const listItemElements = document.querySelectorAll("li"); // 실시간 목록 제공 X
const listItemElements = document.getElementsByTagName("li"); // 요소의 변경사항을 반영하는 실시간 목록을 제공한다.
for (const listItemEl of listItemElements) {
console.dir(listItemEl);
}
const ul = document.querySelector("ul");
ul.children;
ul.children[1];
ul.childNodes; // 텍스트 노드와 요소 노드도 볼 수 있음.
ul.firstChild;
ul.firstElementChild;
ul.lastChild;
ul.lastElementChild;
const liFirst = document.querySelector("li");
liFirst.parentElement; // 가장 가까운 부모 요소 노드에 접근
liFirst.parentNode; // 가장 가까운 부모 노드에 접근
document.documentElement.parentElement; // null
document.documentElement.parentNode; // document -> 전체 문서 객체
const liFirst = document.querySelector("li");
liFirst.closest("body"); // body 요소가 선택된다.
closest()는 요소 트리에 있는 아무 조상을 선택하기 좋다. querySelector처럼 CSS 요소를 사용한다는 것이 특징.const ul = li.parentElements;
ul.previousElementSibling; // header가 나옴
ul.previousSibling; // text 노드
ul.nextSibling; // text 노드
ul.nextElementSibling; // input이 나옴
DOM을 이용해서 탐색하는 것은 정말 신중하게 써야한다. 정말 바뀌지 않을 것들만 쓰자!
const section = document.querySelector("section");
section.style.backgroundColor = "green";
section.classList.toggle("visible");
section.classList.toggle("invisible");
visible이 있으면 삭제하고 없으면 추가. invisible도 마찬가지임.HTML string
innerHTML → Add(render) HTML stringinserAdjacentHTML() : 기존의 콘텐츠 옆에 추가하고자 할 때 사용할 수 있는 메서드 → Add(render) HTML string in specific positioncreateElement()
appendChild() / append() : 새로운 DOM 요소나 노드를 다른 요소의 내부에 추가. 새로운 부모 요소나 부모 노드를 추가할 수 있다. → Append new DOM element/nodeprepend(), before(), after(), insertBefore() : 기존 자식 노드 리스트 끝에 추가하는 대신에 원하는 특정 위치에 추가. → Insert new DOM element/node in specific positionreplceChild(), replaceWith() → Replace existing DOM element/node with new one.section.innerHTML = '<h2> A new title </h2>'
// 혹은
const list = document.querySelector('ul');
list.innerHTML = list.innerHTML + '<li>New Item</li>';
div.insertAdjacentHTML('beforeend', '<p>Something went wrong</p>')
createElement()를 통해 요소 추가하기const list = document.querySelector('ul');
const newLi = document.createElement('li');
list.appendChild(newLi);
newLi.textContent = 'Item 4';
newLi.style.backgroundColor = 'blue';
const list = document.querySelector('ul');
const newLi = document.createElement('li');
newLi.textContent = 'Item 4';
list.prepend(newLi); // 첫번째 요소로 삽입이 됨.
list.lastElementChild.before(newLi) // 이렇게 하면 윗 줄에서 첫번째 요소로 삽입되었던 Item 4 가 삭제되고 세번째 요소가 됨.
// 기존 위치가 변경된다고 생각하면 된다.
list.lastElementChild.after(newLi)
list.firstElementChild.replaceWith(newLi) // Item 1이 삭제되고 Item 4로 교체가 됨.
const list = document.querySelector('ul')
const secondLi = list.children[1]
const newLi = document.createElement('li')
newLi.textContent = 'Item 4'
secondLi.insertAdjacentElement('afterend', newLi)
cloneNode() : 노드를 복제해서 새로운 노드를 반환. 참이나 거짓이 되는 불리언이라는 1개의 선택적 인수를 취한다. → 기본값은 거짓. 깊은 복제 여부만 결정한다. newLi.cloneNode(false) // 리스트 항목 자체만 복제됨
const newLi2 = newLi.cloneNode(true) // 자식 요소뿐만 아니라 해당 요소의 전체 자식 요소와 전체 자손 요소가 복제의 일부가 됨.
list.append(newLi, newLi2) // 끝 부분에 Item 4가 두 개 추가가 됨.
const list = document.querySelector('ul');
const listItems = list.querySelectorAll('li'); // NodeList(3)
const listItems2 = list.getElementsByTagName('li'); // HTMLCollection(3)
const newLi = document.createElement('li');
newLi.textContent = 'Item 4';
list.append(newLi);
listItems // NodeList(3) => 초기 결과 그대로! => Non-live Array / Non-live List
listItems2 // HTMLCollection(4) => Item 4 추가한 결과 반영.
querySelector / querySelectorAll을 사용하는 이유 : 더 유연하고 다양한 쿼리를 지원const list = document.querySelector('ul');
list.remove(); // 방법1 : DOM에서 리스트를 삭제. 렌더링 페이지에서 삭제가 된다.
list.parentElement.removeChild(list); // 방법2