이번 시간에는 DOM에 대해 알아보겠습니다.
DOM
DOM이란 Document Object Model입니다. document를 객체로 구현했다고 생각하면 됩니다. F12에서 콘솔 대신에 Elements 탭을 보면 html 구조가 나와있습니다. html은 이미 알고 있다는 가정 하에 진행하겠습니다. html은 정말 쉬우니까요..
html
-head
--title
--meta
--link
-body
--header#header
---nav
---div
--main
--footer
---"hello"
--script
같은 구조로 되었습니다. 즉 계층적 구조로 되어있는데요. header, main, footer, script의 부모는 body이고, head와 body의 부모는 html이라고 할 수 있습니다. head와 body는 형제자매 관계이겠죠. header는 body의 자식이고요. footer의 안에는 태그 대신 "hello"라는 문자가 들어있습니다. 전 시간에 다룬 텍스트 노드죠.
이걸 잘 생각해보면 객체로 표현할 수 있습니다.
{
document: {
html: {
head: {
title: ...
},
body: {
header: ...
}
}
}
}
이렇게 들어갈 수 있습니다. 위와 같이 만든 게 DOM입니다. 정말 객체 모양으로 표현됐죠? 가장 위는 document고요. 자식으로 갈 때는 html 태그를 건너 뛰고, head와 body로 갑니다. head와 body는 각각 document.head
, document.body
로 접근할 수 있습니다.
Node와 Element
잠시 Node와 Element에 대해 알아보겠습니다! Node는 태그 노드와 텍스트 노드 전체를 가리키고, Element는 텍스트 노드를 제외하고, 흔히 생각하는 태그(<a>같은)만 가리킵니다. 따라서 태그만 검색하고 싶을 때는 Element가 붙은 메소드를 선택해야합니다. 아래는 자주 쓰이는 DOM의 속성입니다.
속성
태그.nodeType
일단 태그를 선택한 후 nodeType 속성을 검색하면 해당 태그의 종류를 알려주는 숫자가 나옵니다.
- 1(
Node.ELEMENT_NODE
) -> Element - 3(
Node.TEXT_NODE
) -> 텍스트 - 8(
Node.COMMENT_NODE
) -> 주석 - 9(
Node.DOCUMENT_NODE
) -> Document - 10(
Node.DOCUMENT_TYPE_NODE
) -> DOCTYPE - 11(
Node.DOCUMENT_FRAGMENT_NODE
) -> Document Fragment
중간에 빈 2, 4, 5, 6은 더 이상 쓰이지 않는 숫자입니다. 7은 거의 안 쓰기 때문에 뺐습니다. 뒤에 Node.[노드 상수]
해도 같은 값이 나옵니다. Node.ELEMENT_NODE == 1
입니다.
태그.children, 태그.childNodes
자식으로 갈 때는 children(텍스트 노드 제외)또는 childNodes(텍스트 노드 포함)를 사용합니다.
document.body.children; // [header, main, footer, script]
따라서 main을 선택하고 싶다면 document.body.children[1]
을 선택하면 되는 거죠.
이 DOM의 속성들은 모든 태그에 다 사용할 수 있습니다. 즉 document부터 head, body, script, div, span 등 모든 태그에 다 지원됩니다. document.getElementById('header')
로 가리킨 태그도document.getElementById('header').children
하면 #header의 자식들이 나옵니다.
한번에 document.getElementsByTagName('main')
하면 편할 것은 왜 굳이 children 이런 것을 쓰냐고요? 한 번에 선택할 때는 getElementsByTagName같은 메소드가 더 편하지만 main의 부모를 찾아라, 또는 main의 자식들을 찾아라 할 때는 이름을 모르기 때문에 children같은 속성을 사용하는 겁니다.
태그.firstChild, 태그.firstElementChild, 태그.lastChild, 태그.lastElementChild
모든 자식을 선택하는 대신, 첫 번째 자식만 선택하고 싶다면 firstChild 속성이 있습니다. 마지막을 선택하고 싶으면 lastChild가 있고요.
document.body.firstChild; // <header>...</header>
document.body.lastchild; // <script>...</script>
firstElementChild, lastElementChild는 같은 역할을 하지만, 텍스트 노드는 무시합니다.
태그.parentNode, 태그.parentElement
부모를 찾을 때는 parentNode 속성을 사용합니다.
document.body.parentNode; // <html>...</html>
자식은 여러 개일 수 있기 때문에 children이나 childNodes같은 복수형 단어를 썼다면 부모는 항상 한명이기 때문에 단수형 parentNode입니다.
태그.previousSibling, 태그.nextSibling, 태그.previousElementSibling, 태그.nextElementSibling
형제자매 태그를 찾을 때는 previousSibling, nextSibling을 씁니다. sibling이 형제자매라는 뜻이거든요. 각각 바로 전이나 바로 다음 형제자매를 찾아줍니다.
document.getElementsByTagName('main')[0].nextSibling; // <footer></footer>
previousElementSibling, nextElementSibling은 역시 텍스트 노드를 무시합니다.
태그.innerHTML, 태그.outerHTML
선택한 태그의 내용물을 얻어오거나 바꿀 수 있습니다.
var footer = document.getElementsByTagName('footer')[0];
footer.innerHTML; // 'hello'
footer.innerHTML = 'goodbye';
원래의 내용이던 hello를 goodbye로 바꿨습니다. 텍스트 외에도 태그를 집어 넣을수도 있습니다.
footer.innerHTML = '<b>bold</b>';
굵은 글씨의 bold 텍스트가 들어갑니다. outerHTML은 현재태그까지 포함한 문자열을 반환합니다.
footer.outerHTML = '<footer><b>bold</b></footer>';
태그.속성
var tag = document.getElementById('header');
tag.id; // 'header'
태그를 선택하고 그 속성을 조회할 수 있습니다. 바꿀 수도 있고요. id, className(class), name, value, placeholder, checked, disabled, readonly 같은 속성 값을 볼 수 있습니다.
태그.attributes
해당 태그가 가진 모든 속성을 보고싶다면 attributes 속성을 사용하면 됩니다.
태그.clientHeight, 태그.clientWidth
태그의 margin, border, scrollbar을 제외한 높이와 너비를 반환합니다.
태그.offsetHeight, 태그.offsetWidth
태그의 margin만 제외한 높이와 너비를 반환합니다.
태그.scrollHeight, 태그.scrollWidth
스크롤 가능한 범위까지 포함한 태그의 높이와 너비를 반환합니다.
아래는 자주 쓰이는 태그의 메소드들입니다. DOM을 조작하려면 꼭 알아두어야 합니다.
메소드
태그.appendChild
이전 시간에 createElement() 함수로 만들었던 태그를 넣을 때 이 메소드가 필요합니다. 마지막 순서의 자식 태그로 추가됩니다.
var newElement = document.createElement('div');
document.body.appendChild(newElement);
위의 코드처럼 하면 body의 마지막 자식 태그로 div 태그가 하나 추가됩니다.
태그.removeChild
선택한 자식 태그를 삭제합니다.
document.body.removeChild(document.body.childNodes[document.body.childNodes.length - 1]);
body의 마지막 자식 태그를 삭제하는 코드입니다. document.body.childNodes[document.body.childNodes.length - 1]
가 마지막 자식 태그를 선택하는 코드고요.
태그.insertBefore
appendChild가 자식 태그로 집어넣는 거라면 insertBefore 메소드는 자신의 형제 태그로 집어넣습니다. 자신 이전에요.
var newElement = document.createElement('div');
document.body.insertBefore(newElement, document.getElementById('header'));
위의 코드는 부모.insertBefore(넣을 태그, 기준 태그)
입니다. 위의 코드는 body의 자식으로, header 이전에 새로 만든 div태그를 넣으라는 뜻이죠.
태그.cloneNode
자신을 복사합니다. 복사한 것을 저장해서 appendChild나 insertBefore로 집어넣으면 되겠죠?
var clone = document.getElementsByTagName('nav')[0].cloneNode();
다음 시간에는 window 객체의 속성인 Date와 RegExp 객체에 대해 알아보겠습니다!
document: {
html: {
head: {
title: ...
},
body: {
header: ...
}
}
}
}
이런식으로 된다고 하셨는데 왜 객체형태가 아닌 html 형태로 나오게 되나요?