본문으로 바로가기
반응형

[JavaScript (13)] Javascript Dom(Document Object Model, 문서 객체 모델)

안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 자바스크립트 돔(DOM) 입니다. : ) 

 

 

▶ DOM(Document Object Model) 이란?

 - HTML, XML 문서에 접근하기 위한 인터페이스
 - 문서 내의 모든 요소를 정의하고, 각각의 요소에 접근하는 방법을 제공 한다.
 - W3C의 표준 객체 모델

 

브라우저의 렌더링 엔진은 웹 문서를 로드한 후, 파싱하여 웹 문서를 브라우저가 이해할 수 있는 구조로 구성하여 메모리에 적재하는데 이를 DOM이라 한다.
즉 모든 요소와 요소의 어트리뷰트, 텍스트를 각각의 객체로 만들고 이들 객체를 부자 관계를 표현할 수 있는 트리 구조로 구성한 것이 DOM이다.
이 DOM은 자바스크립트를 통해 동적으로 변경할 수 있으며 변경된 DOM은 렌더링에 반영된다.

 

위와 같은 내용으로도 이해가 잘 안될 수도 있다. 근본적으로 이해하기 위해 웹 문서가 어떻게 브라우저에 렌더링 되는지 부터 한번 살펴 보자.
( 참고 : https://varvy.com/performance/cssom.html )

 

1. The web browser examines your HTML and builds the DOM (Document Object Model). 
 - HTML을 읽어서 DOM을 빌드 한다.
 - 웹 페이지는 일종의 문서(document)이다. 텍스트 파일(ex .html)로 만들어져 있으며 이 웹 문서를 브라우저에 렌더링하려면 브라우저가 이해할 수 있는 구조로 메모리에 올려야 한다.
브라우저의 렌더링 엔진은 웹 문서를 읽어 들인 후 파싱하여 브라우저가 이해할 수 있는 구조로 구성하여 메모리에 적재하는데 이를 DOM이라 한다.

2. The web browser examines your CSS and builds the CSSOM (CSS Object Model).
 - CSS를 읽어서 CSSOM을 빌드 한다.
 - CSSOM(Cascading Style Sheets Object Model) – 요소들과 연관된 스타일 정보의 구조화된 표현

3. The web browser combines the DOM and the CSSOM to create a render tree.
4. The web browser displays your webpage.
 - DOM과 CSSOM 의 구조를 결합하여 Render Tree 구조를 생성한 후 웹페이지에 보여준다.

오늘은 웹 문서(ex .html)를 브라우저(ex 크롬, 익스)에 렌더링하는 과정 중 1번 과정과 관련된 DOM에 관하여, 그리고 관련 API들을 알아보려 한다.

웹 문서를 브라우저에 렌더링 하는 순서

※ HTML 문서에 대한 모델 구성
1) 브라우저는 HTML 문서를 로드하고 데이터를 파싱한다.
2) HTML을 파싱한 결과로 DOM Tree를 생성한다.
3) 파싱하는 중 CSS 파일 링크를 만나면 CSS 파일을 요청해서 받아온다.
4) CSS 파일을 읽어서 CSSOM(CSS Object Model)을 만든다.
5) DOM Tree와 CSSOM이 모두 만들어지면 이 둘을 사용하여 Render Tree를 만든다.
6) Render Tree에 있는 각각의 노드들이 화면의 어디에 어떻게 위치할 지를 계산하는 Layout과정을 거쳐서, 화면에 실제 픽셀을 Paint한다.

 - 이 과정중 DOM Tree에 대해 먼저 알아 보려고 한다.

Dom tree

 - 브라우저가 HTML 문서를 로드한 후 파싱하여 생성하는 모델을 의미한다. 트리로 구조화되어 있기 때문에 DOM tree라 부른다.
 - 모든 엘리먼트는 HTMLElement의 자식이고, HTMLElement는 Element의 자식이고 Element는 Node의 자식이다. Node는 Object의 자식이다.
   이러한 구조를 tree 구조로 보여 준다.
 - DOM tree는 네 종류의 노드로 구성된다.

1) 문서 노드(Document Node)
 - 프로퍼티 값 : #document
 - 최상위에 존재하는 노드
 - 각각 요소, 어트리뷰트, 텍스트 노드에 접근하려면 문서 노드를 통해야 한다.

2) 요소 노드(Element Node)
 - 프로퍼티 값 : 태그 이름
 - HTML 요소를 표현한다.
 - HTML 요소는 중첩에 의해 부자 관계를 가지며 이 부자 관계를 통해 정보를 구조화 하여 문서의 구조를 서술한다.
 - 어트리뷰트, 텍스트 노드에 접근하려면 먼저 요소 노드를 찾아 접근해야 한다.

3) 어트리뷰트 노드(Attribute Node)
 - 프로퍼티 값 : 속성 이름
 - HTML 요소의 어트리뷰트를 표현한다.
 - 어트리뷰트 노드는 해당 어트리뷰트가 지정된 요소의 자식이 아니라 해당 요소의 일부로 표현된다. 따라서 해당 요소 노드를 찾아 접근하면 어트리뷰트를 참조, 수정할 수 있다.

4) 텍스트 노드(Text Node)
 - 프로퍼티 값 : #text
 - 텍스트 노드는 HTML 요소의 텍스트를 표현한다.
 - 실제 예제 소스를 통해 Dom tree를 살펴보자. (Dom tree를 시각적으로 보여주는 site)
   https://software.hixie.ch/utilities/js/live-dom-viewer/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1 class="class-h1">Hello World!</h1>
    <div class="class-div" id="contents">
        <p class="class-p" id="p1" name="p_name">My Name is GodDaeHee.</p>
        <p class="class-p" id="p2" name="p_name">My Name is GodDaeHee.2</p>
        <a class="class-a" href="https://goddaehee.tistory.com" target="_blank">클릭시 My Blog 이동~</a>
    </div>
</body>
</html>

 Dom 메서드

 - 이제부터 Dom에 접근하고, 조작하는 방법을 알아보자.

 - 예제 소스는 모두 다음 코드를 기반으로 테스트 해 보았다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1 class="class-h1">Hello World!</h1>
    <div class="class-div" id="contents">
        <p class="class-p" id="p1" name="p_name">My Name is GodDaeHee.</p>
        <p class="class-p" id="p2" name="p_name">My Name is GodDaeHee.2</p>
        <a class="class-a" href="https://goddaehee.tistory.com" target="_blank">클릭시 My Blog 이동~</a>
    </div>
</body>
</html>


1. 요소 선택 메서드
1) 한개의 요소 노드 선택
 - 선택된 요소가 없는 경우 null 리턴

1.1) document.getElementById(id)
 - id 속성 값으로 한 개의 노드를 선택한다.
 - 복수개의 노드가 선택된 경우, 첫번째 요소만 반환한다.
ex)

let el = document.getElementById('p1');
console.log(el); // <p class="class-p" id="p1" name="p_name">My Name is GodDaeHee.</p>

1.2) document.querySelector(cssSelector)
 - css 셀렉터를 사용하여 한 개의 노드를 선택한다.
 - 복수개의 노드가 선택된 경우, 첫번째 요소만 반환한다.
ex)

let el = document.getElementById('p'); // 첫번째 p 노드만 리턴한다.
console.log(el); // <p id="p1">My Name is GodDaeHee.</p>

 

2) 여러개의 요소 노드 선택
2.1) document.getElementsByClassName(class)
 - 해당 클래스에 속한 요소를 모두 선택 한다.
 - 공백으로 구분하여 여러 개의 class를 지정할 수 있다.
 - Return: HTMLCollection (live)
ex)

let el = document.getElementsByClassName('class-p');
console.log(el); // HTMLCollection(2) [p#p1.class-p, p#p2.class-p, p1: p#p1.class-p, p2: p#p2.class-p]
[...el].forEach(el => console.log(el));
// <p class="class-p" id="p1">My Name is GodDaeHee.</p>
// <p class="class-p" id="p2">My Name is GodDaeHee.2</p>

2.2) document.getElementsByName(name)
 - 해당 name 속성값을 가지는 요소를 모두 선택한다.
 - Return: NodeList (non-live)
ex)

let el = document.getElementsByName('p_name');
console.log(el); // NodeList(2) [p#p1.class-p, p#p2.class-p]
[...el].forEach(el => console.log(el));
// <p class="class-p" id="p1">My Name is GodDaeHee.</p>
// <p class="class-p" id="p2">My Name is GodDaeHee.2</p>

2.3) document.getElementsByTagName(태그명)
 - 해당 태그 이름의 요소를 모두 선택한다.
 - Return: NodeList (non-live)
ex)

let el = document.querySelectorAll('p');
console.log(el); // NodeList(2) [p#p1.class-p, p#p2.class-p]
[...el].forEach(el => console.log(el));
// <p class="class-p" id="p1">My Name is GodDaeHee.</p>
// <p class="class-p" id="p2">My Name is GodDaeHee.2</p>

 

3) 요소 생성 메서드

3.1) document.createElement(tagName)
 - tagName에 맞는 HTML 요소를 생성 한다.
ex)

let el = document.createElement('li');
console.log(el); // <li></li>

3.2) document.createTextNode(text)
 - 해당 text의 text노드 요소를 생성 한다.
ex)

let el = document.createTextNode("god")
console.log(el); // "god"

3.3) document.write(text)
 - HTML 출력 스트림을 통해 텍스트를 출력 한다.
ex)

document.write("god!");

 

4) 기본 내장 함수
 - 주로 다음 3개의 내장 함수 이외에는 거의 사용해보지 못했다.

ㆍdocument.cookie : HTML 문서의 쿠키(cookie)를 반환 한다.
ㆍdocument.referrer : 링크(linking)되어 있는 문서의 URI를 반환 한다.
ㆍdocument.URL : HTML 문서의 완전한 URL 주소를 반환 한다.

 - etc
ㆍdocument.anchors : name 속성을 가진 요소들을 반환한다.

ㆍdocument.baseURI : 절대주소 기본 URI를 반한한다.
ㆍdocument.documentURI : 문서의 URI를 반환한다.
ㆍdocument.body : 요소를 설정하거나 반환한다.
ㆍdocument.domain : HTML 문서를 로드한 도메인 서버의 네임(domain name)을 반환한다.
ㆍdocument.doctype : HTML 문서의 타입(doctype)을 반환한다.
ㆍdocument.documentElement : 도큐먼트 요소를 반환한다.
ㆍdocument.documentMode : 도큐먼트 모드를 반환한다.
ㆍdocument.forms : form요소를 모두 반환한다.
ㆍdocument.images : img요소를 모두 반환한다.
ㆍdocument.links : href 속성을 가지는 area요소와 a요소를 모두 반환한다.
ㆍdocument.title : title 요소를 반환한다.
ㆍdocument.embeds : embed요소를 모두 반환한다.
ㆍdocument.head : head요소를 반환한다.
ㆍdocument.implementation : DOMImplementation 객체를 반환한다.
ㆍdocument.inputEncoding : 도큐먼트 인코딩(character set) 형식을 반환한다.
ㆍdocument.lastModified : 도큐먼트 마지막 수정 날짜 및 시간을 반환한다.
ㆍdocument.readyState : 도큐먼트의 로딩 상태(loading status)를 반환한다.
ㆍdocument.scripts : script 요소를 모두 반환한다.
ㆍdocument.strictErrorChecking : 에러 검사가 실행되었는지 설정하거나 반환한다.

 

5) 노드 접근 활용 방법
 - getElementById, getElementsByTagName 등의 메서드를 통해 특정 노드, 또는 복수의 모드에 접근하는 내용을 살펴 보았다.
 - 이를 이용하여 형제 노드, 부모 노드, 자식 노드로도 접근이 가능하다.

5.1) DOM에서 노드 간의 관계는 다음 프로퍼티로 접근 가능 하다.
1) parentNode : 부모 노드
2) childNodes : 자식 노드 리스트(non-live, 텍스트 요소 포함)
   children : 자식 요소 중 Element type 요소만 반환 한다.(live)
3) firstChild : 첫 번째 자식 노드를 반환한다.(텍스트 요소 포함)
   firstElementChild : 첫 번째 자식 엘리먼트를 반환한다.(Element type 요소만)
4) lastChild : 마지막 자식 노드를 반환한다.(텍스트 요소 포함)
   lastElementChild : 마지막 자식 엘리먼트를 반환한다.(Element type 요소만)
5) nextSibling : 다음 형제 노드를 반환한다.(텍스트 요소 포함)
   nextElementSibling : 다음 형제 요소 중 Element type 요소만 반환 한다.
6) previousSibling : 이전 형제 노드를 반환한다.(텍스트 요소 포함)
   previousElementSibling : 이전 형제 요소 중 Element type 요소만 반환 한다.

ex)

let el = document.getElementById("contents");
console.log(el); // <div class="class-div" id="contents">...</div>
console.log(el.parentNode); // <body>...</body>
console.log(el.childNodes); // NodeList(7) [text, p#p1.class-p, text, p#p2.class-p, text, a.class-a, text]length: 70: text1: p#p1.class-p2: text3: p#p2.class-p4: text5: a.class-a6: text__proto__: NodeList
console.log(el.firstChild); // #text
console.log(el.firstElementChild) // <p class="class-p" id="p1" name="p_name">My Name is GodDaeHee.</p>
console.log(el.lastChild); // #text
console.log(el.lastElementChild) // <a class="class-a" href="https://goddaehee.tistory.com" target="_blank">클릭시 My Blog 이동~</a>
console.log(el.nextSibling); // #text
console.log(el.nextElementSibling); // null
console.log(el.previousSibling); // #text
console.log(el.previousElementSibling); // // <h1 class="class-h1">Hello World!</h1>
console.log(el.previousSibling.previousSibling) // <h1 class="class-h1">Hello World!</h1>

 

※ IE를 제외한 대부분의 브라우저들은 요소 사이의 공백 또는 줄바꿈 문자를 텍스트 노드로 취급하기 때문에 생각과 다른 결과를 얻을 수 있다. 

5.2) 노드에 대한 접근 및 수정
 - 다음 프로퍼티를 통해 접근 및 수정이 가능하다.
1) nodeName
 - 노드의 이름을 반환한다.
2) nodeValue
 - 노드의 값을 반환한다.
 - 텍스트 노드의 경우는 문자열, 요소 노드의 경우 null을 반환 한다.
3) nodeType
 - 노드의 타입을 반환한다.
ex)

let el = document.getElementById("contents");
console.log(el); // <div class="class-div" id="contents">...</div>
console.log(el.nodeName); // DIV
console.log(el.nodeValue); // null
console.log(el.nodeType); // 1

 

5.3) 어트리뷰트에 대한 접근 및 수정
 - 다음 프로퍼티를 통해 접근 및 수정이 가능하다.
1) className
 - class 어트리뷰트의 값을 리턴하거나 변경할 수 있다.
2) classList
 - add, remove, item, toggle, contains, replace 메소드를 제공한다.
3) id
 - id 어트리뷰트의 값을 리턴하거나 변경할 수 있다.
4) 이외
 - hasAttribute(attribute), getAttribute(attribute), setAttribute(attribute, value), removeAttribute(attribute)
ex) 

let el = document.querySelectorAll('p');
console.log(el); // NodeList(2) [p#p1.class-p, p#p2.class-p]
[...el].forEach(el => {
	console.log(el.className); // class-p, class-p
});
[...el].forEach(el => {
	el.className = 'class-p2';
	console.log(el.className); // class-p2, class-p2
});
[...el].forEach(el => {
	el.classList.replace('class-p2', 'class-p');
	console.log(el.className); // class-p, class-p
});
[...el].forEach(el => {
	console.log(el.id); // p1, p2
});
[...el].forEach(el => {
	el.id = el.id + '_id';
	console.log(el.id); // p1_id, p2_id
});

 

5.4) 이외 HTML 조작
1) textContent
 - 노드의 내부 콘텐츠를 text/plain 으로 파싱(Parsing)한 결과를 리턴하거나 변경한다. (마크업은 문자열로 인식되어 그대로 출력된다.)
 - textContent를 통해 요소에 새로운 텍스트를 할당하면 텍스트를 변경할 수 있다.

2) innerText
 - innerText 프로퍼티를 사용하여도 요소의 텍스트 콘텐츠에만 접근할 수 있다. 하지만 아래의 이유로 사용하지 않는 것이 좋다.
 - CSS에 순종적이고, 이 때문에 textContent 프로퍼티보다 느리다.

3) innerHTML
 - innerHTML 프로퍼티의 값은 text/html으로 파싱한 결과를 리턴하거나 변경한다. (마크업 포함)
 - 대표적인 XSS(Cross-Site Scripting) 공격에 취약한 사례로 언급된다는 점을 유의 해야 한다.

※ 가급적 HTML Parsing이 필요 없는경우 textContent를 쓰면 보안적으로도, 속도적으로도 유리하다.

 

ex)

textContent를 사용하여 카운트 다운 함수 만들기

2020/03/20 - [3. 웹개발/3_2_2 Javascript Function 모음] - [JavaScript ] 카운트 다운 함수(Count Down Function)

 

반응형

댓글을 달아 주세요