DOM은 Document Object Model의 약자로, HTML 요소를 Object(JavaScript Object)처럼 조작(Manipulation)할 수 있는 Model이다. 쉽게 말하면, 프로그래밍 언어인 자바스크립트와 DOM을 이용해 HTML 문서의 엘리먼트에 접근하거나, 새로운 엘리먼트를 생성/삭제 할 수 있다. 결과적으로, 웹 페이지를 동적으로 움직이게 만들 수 있다.
DOM은 HTML 문서의 아주 작은 부분까지 접근할 수 있는 구조다. DOM은 부모가 자식을 여러 개 가진 패턴이 반복되는 트리 구조다. 회사의 조직도를 연상하면 쉽다.
console.dir이 유용하다.parsing) 자바 스크립트를 만나면 렌더링을 멈추고 파일을 다운로드 하고(fetching) 그걸 실행한다(executing) 실행을 마치고 다시 한 줄씩 읽어 내려간다(parsing)이 부분 이전 챕터에서 개발자 면접 질문에 단골로 나온다고 했었던 기억이 있다.
HTML문서를 읽기 전에 스크립트 요소를 미리 읽어와야 하는 경우도 있을 것이다. 이런 이야기를 하면서 나온 용어들. 면접 질문으로 대비하면 좋다.
console.dir(document.body)console.dir(document.body.children)
let newsContents = document.body.children[1];
// 변수에 담기
console.log(newsContents.parentElement)
// parentElement로 부모관계의 요소 찾기
JavaScript에서 DOM 구조를 순회하기 위해서 부모가 가진 하나 혹은 여러 개의 자식들을 반복해서 조회해 나가는 로직을 수도코드로 작성했다.
document 객체를 통해서 HTML 엘리먼트를 만들고(CREATE), 조회하고(READ), 갱신하고(UPDATE), 삭제할(DELETE) 수 있다. DOM에는 HTML에 적용(APPEND)하는 메소드가 따로 있다.
======createElement - <CREATE>======
const oneDiv = document.createElement('div') // 새로운 div 태그 생성 + 변수를 선언해 작업의 결과를 할당
======append, appendChild - <APPEND>======
document.body.append(oneDiv) // 새로 생성한 div 태그를 트리 구조와 연결
======querySelector, querySelectorAll - <READ>======
// DOM으로 HTML 엘리먼트 정보를 조회하기 위해서는 querySelector의 첫 번째 인자에 셀렉터를 전달한다. query는 질문하다라는 의미다.
const target = document.querySelector('.classname'); // classname 이라는 클래스명을 가진 엘리먼트(아마 최상단)를 가져오는데 하나만 가져오게 된다. 여러 개를 가져오고 싶다면?
const targets = document.querySelectorAll('.classname');
// 이 엘리먼트들은 배열처럼 반복문을 사용할 수 있다. 배열은 아니다. '배열 아닌 배열', '유사 배열' '배열형 객체' 'Array-like Object'이다.
// getElementsByTagName, getElementsByClassName
const target1 = document.getElementByID('id') // 비슷한 역할을 하는 옛날 방식
const target2 = document.querySelector('#id);
target1 === target2 //true
const container = document.querySelector('#container');
const oneDiv = document.createElement('div');
container.append(onediv);
======textContent, id, classList, setAttribute - <UPDATE>======
oneDiv.textContent = 'hello suri'; // textContent로 엘리먼트에 문자열을 입력한다.
oneDiv.classList.add('hide'); // 요소에 style속성을 담은 'hide'라는 클래스 이름을 추가한다.
oneDiv.id = 'username'; // 요소에 아이디 부여하기
oneDiv.classname = 'col'; // 이렇게 클래스 이름을 부여하면 다 사라지고 리셋이 되는 것 같다.
oneBtn.setAttribute('name', 'fruit') // 버튼 요소에 name 속성을 추가할 수 있다. setAttribute를 이용하면 태그 안에 어떤 속성이든 추가할 수 있는 마법이라고 생각하면 될까.
=====remove, removeChild, innerHTML = "", textContent = ""- <DELETE>=====
oneDiv.remove() // 엘리먼트 삭제하기
const container = document.querySelector('#container');
container.innerHTML = ''; // container 안에 모든 엘리먼트 삭제하기. innerHTML은 보안상의 문제가 있을 수 있다.
while (container.firstchild) {
container.removeChild(container.firstchild); // 자식 엘리먼트 지정해서 삭제하기
} // 첫 번째 자식 엘리먼트가 존재하면, 첫 번째 자식 엘리먼트를 삭제한다.
while (container.children.length > 1) {
container.removeChild(container.lastChild);
} // 자식 엘리먼트가 1개만 남을 때까지, 마지막 자식 엘리먼트를 삭제한다.
const tweets = document.querySelectorAll('.tweet');
for (let tweet of tweets) {
tweet.remove();
}
<!DOCTYPE html>
<html>
<head>
<title>DOM Review</title>
<meta charset="UTF-8" />
<style>
html,
body {
margin: 0;
padding: 0;
}
li {
list-style: none;
margin: 20px 0px;
}
.country {
font-weight: bold;
}
div span {
display: block;
}
</style>
</head>
<body>
<div id="app">
<ul class="city-list"></ul>
</div>
<script>
const places = [
{
id: 1,
city: "Seoul"
country: "Republic of Korea"
address: "jung-gu"
},
{
id: 2,
city: "New York"
country: "Unites States"
address: "-"
},
{
id: 3,
city: "Ilsan",
country: "Republic of Korea",
address: "Sik-sa"
}
];
const cityList = document.querySelector(".city-list");
for (let el of places) {
const li = document.createElement("li");
const div = document.createElement("div");
const span1 = document.createElement("span");
const span2 = document.createElement("span");
const span3 = document.createElement("span");
div.append(span2, span3);
li.append(span1, div);
cityList.append(li);
span1.classList.add("country");
span1.textContent = el.country;
span2.classList.add("city");
span2.textContent = "City : " + el.city;
span3.classList.add("address");
span3.textContent = "Address : " + el.address;
}
</script>
</body>
</html>
그렇지 않다. 자식 요소를 가진 부모 요소를 조회해서 변수에 담고 거기에서 querySelector를 사용할 수도 있었다.
Node는 Element의 상위 개념입니다. DOM 관련 객체는 대부분 Node에서 파생되었다고 봐도 과언이 아닙니다. Node의 기능을 적용(implements)한 객체는 여러 타입이 있는데, 그 중 가장 많이 사용되는 타입이 Element입니다.
노드리스트는 배열이 아니라, 노드의 콜렉션입니다. 그래서 배열 메소드들을 사용할 수 없다.
현재 노드에서 자식 노드에 접근하기 위해서 children 혹은 childNodes 메소드를 사용한다. children은 태그 노드를 리턴하고, childNodes는 태그 노드에 텍스트도 함께 리턴한다.
remove는 메모리 단위에서 삭제하는 것이라 반환값이 없다. undefined가 뜬다. removeChild는 삭제한 값을 반환한다.
append는 한 번에 여러개를 추가할 수 있다.
이벤트 객체는 사용자의 입력으로 발생한 이벤트 정보를 담은 객체이다.
이벤트 : 웹을 탐색하면서 사용자가 클릭이나 드래그 등을 하는 일
onclick onsubmit onchange onmousedown onmouseup onkeyup onkeydown event.preventDefault
엘리먼트에 특정 이벤트가 발생했을 때 이벤트 핸들러가 동작하도록 한다.
===1===
element.onclick = () => {
console.log('hello');
}
===2===
element.onclick = handler;
function handler() {
console.log('hello');
}
===3===
// 이게 더 모던한 방식이다.
element.addEventListener('click', () => {
console.log('hello')
});
addEventListener : 이벤트 핸들러를 할당했다가 해지하는 게 더 쉽고, 한꺼번에 2개 이상의 이벤트를 걸 수도 있다. element.onclick : 뒤에 똑같은 이벤트를 걸면 앞에 걸었던 이벤트를 지워버린다. 해지하는 것도 까다롭다. <button>Organic Tea</button>
<button>Hot Coffee</button>
<script>
let beverages = document.querySelectorAll("button");
let btnOrganicTea = beverages[0];
let btnHotCoffee = beverages[1];
function handler(event) {
console.log(event);
console.log(event.target);
console.log(event.target.textContent);
}
btnOrganicTea.onclick = handler;
btnHotCoffee.onclick = handler;

createDocumentFragment를 활용하여, 더 효율적으로 DOM을 제어
HTML5 template tag 사용법
같은 엘리먼트를 appendChild 하면, 기존 엘리먼트를 복사할까?
좌표 정보 조회 - offsetTop...
크기 정보 조회 - offsetWidth...