문서 제어

자바스크립트로 문서를 제어하기.

DOM Tree

DOM(Document Object Model)은 문서를 제어하는 API이다. 웹 브라우저가 웹 페이지를 읽으면 렌더링 엔진은 웹 페이지의 HTML 구문을 해석하고 Document 객체에서 문서 내용을 관리하는 DOM 트리 구조를 만들어낸다.

간단한 웹페이지가 만들어졌을 때 DOM 트리를 구성하는 객체 하나를 노드(Node)라고 한다. 기본적으로 문서 노드(Document 객체), HTML 요소 노드, 텍스트 노드로 구성이 된다.

DOM 트리

주의할 점은 HTML과 달리 DOM 트리는 요소 앞뒤에 연속적인 공백 문자를 발견하면 텍스트로 취급한다. (그래서 특히 제이쿼리 사용할 때 주의!)
html 요소 안에 있는 첫 번째와 마지막 공백 문자에 대해서는 공백 노드를 생성하지 않는다. 공백 노드를 주의해야 하는 것은 크로스 브라우징 때문인데, IE8이하 버전에서는 공백 노드가 없기 때문이다. (이놈의 IE…)

  • Document 객체

Document 객체는 모든 노드의 조상 노드이며 DOM 트리의 루트이다. 그래서 DOM 트리는 많은 요소들의 포함 관계를 표현한다.

노드 객체의 프로퍼티

parentNode : 이 노드의 부모 노드를 참조한다. Document객체의 부모 노드는 null이다.
childNodes : 이 노드의 자식 노드의 참조를 저장한 유사 배열 객체를 나타낸다.(NodeList).
firstChild : 이 노드의 첫 번째 자식 노드를 나타낸다.
lastChild : 이 노드의 마지막 자식 노드를 나타낸다.
nextSibling : 이 노드와 같은 부모를 가진 노드의 다음 형제 노드를 가리킨다.
previousSibling : 이 노드와 같은 부모를 가진 노드의 이전 형제 노드를 가리킨다.
nodeType : 노드 유형을 뜻하는 숫자를 나타낸다.
nodeValue : 텍스트 노드의 텍스트 콘텐츠를 나타낸다.
nodeName : 요소 노드는 대문자로 바뀐 요소 이름이 들어가고 텍스트 노드는 ‘#text’가 들어간다.

이를 참조하기 위해선 공백 노드의 영향을 받기 때문에 주의가 필요하다. 그래서 이러한 불편함 때문에 자주 사용하진 않는다.

HTML 요소의 트리

이러한 불편함을 제거하고자 (HTML 트리에선 공백 노드가 없기 때문에) HTML 문서에서 요소의 계층 구조만 가져오기 위한 프로퍼티도 존재한다.

childNodes : 이 요소의 자식 요소를 참조한 유사 배열 객체(NodeList)
parentElement : 이 요소의 부모 요소 객체를 참조.
firstElementChild : 이 요소의 첫 번째 자식 요소 객체를 참조.
lastElementChild : 이 요소의 마지막 자식 요소 객체를 참조.
nextElementSibling : 이 요소와 같은 부모를 가진 요소 다음의 형제 요소 객체를 참조.
previousElementSibling : 이 요소와 같은 부모를 가진 요소 이전의 형제 요소 객체를 참조.
childElementCount : 자식 요소의 개수를 나타낸다. === children.length

자바스크립트로 웹 페이지 제어하기

자바스크립트를 사용하면 DOM 트리의 노드 객체를 가져와서 제어할 수 있다. 또한 스타일 규칙 제어도 가능하다. 렌더링 엔진은 DOM 트리와 스타일 규칙이 바뀔떄마다 렌더 트리를 다시 구성하게 된다.

노드 객체 가져오기

자바스크립트로 HTML 요소를 제어하려면 그 전에 제어하고자 하는 요소 객체를 먼저 가져와야 한다.

id 속성으로 노드 가져오기

HTML 문서에 id 속성을 지정해서 가져올 수 있다. id는 같은 문서에서 유일한 값이다. id값으로 요소 객체를 가져올 때는 getElementById 메서드를 사용한다.

요소의 이름으로 노드 가져오기

getElementsByTagName이라는 메서드를 이용해서 인수로 넘긴 문자열과 같은 이름의 태그 목록을 가져올 수 있다. 중요한 건 이 메서드는 NodeList 객체를 반환한다. (같은 이름의 태그가 많기 때문이다.)

NodeList를 반환하는 메서드

getElementsByTagName, getElementsByName, getElementsByClassName 메서드는 NodeList 객체를 반환한다.
NodeList 객체는 HTML 문서의 변화에 따라 동적으로 바뀐다.

class 속성값으로 노드 가져오기

HTML 요소에 class 속성을 지정하여 가져오는 방법을 말한다. getElementByClassName 메서드를 사용하면 특정 class 속성을 가지고 있는 요소 객체 목록을 가져온다.

이 메서드는 공백 문자로 연결한 문자열을 인수로 넘기면 그 식별자들을 class 속성 값으로 갖는 요소의 목록을 가져올 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="cat black">봄베이</div>
<div class="cat white">페르시안</div>
<div class="dog white">스피츠</div>
<script>
var cats = document.getElementsByClassName('cat');
for(var i = 0; i < cats.length; i++) {
console.log(i + " 번 째 고양이 : " + cats[i].innerHTML);
}
</script>
</body>
</html>

css 선택자로 노드 가져오기.

querySelectorAll 메서드를 사용하면 인수로 넘긴 선택자와 일치하는 요소 객체가 담긴 NodeList를 가져올 수 있다. 단 이 방법의 NodeList는 메서드를 호출한 시점에 일치하는 요소를 반환한다.(동적으로 NodeList가 변하진 않는다.)

querySelector는 지정한 선택자와 일치한 요소 객체 중 첫 번째 요소를 반환한다.

속성 값의 읽기와 쓰기.

HTML 요소에는 속성을 설정할 수가 있다. 이를 자바스크립트로도 읽거나 쓸 수 있다.

요소 객체의 프로퍼티로 요소의 속성을 읽고 쓰기

HTML의 일반적인 속성이나 이벤트 처리기 프로퍼티 등이 정의되어 있기 때문에 요소 객체.속성 이름 방식으로 작성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<a id="school" href="http://school.gilbut.co.kr">길벗 스쿨</a>
<script>
var anchor = document.getElementById('school');
console.log(anchor.href);
</script>
</body>
</html>

속성 값 가져오기

요소 객체.getAttribute(속성 이름)의 방식으로 사용한다. 속성이 없으면 null 혹은 빈 문자열을 반환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form id="favorite">
<input type="checkbox" name="dog" value="pome">포메라니안<br>
<input type="checkbox" name="dog" value="dalma">달마시안<br>
<input type="checkbox" name="dog" value="bool">불독
</form>
<script>
var result = [];
var fm = document.getElementById('favorite');
var list = fm.children;
for (var i = 0; i < list.length; i++) {
if(list[i].nodeName == 'INPUT' && list[i].type == "checkbox") {
result.push(list[i].getAttribute("value"));
}
}
console.log(result.join(','));
</script>
</body>
</html>

속성 값 설정하기

setAttribute 메서드를 이용하면 요소의 속성을 설정할 수 있다. 해당 속성이 없으면 그 속성을 새롭게 추가한다.

위 두 메서드의 장점은 HTML의 일반적인 속성 외에도 모든 속성을 설정할 수 있다는 점과 속성 이름을 프로그램 실행 중에 동적으로 설정할 수 있다는 것이다.

그 외에도 속성을 확인하는 hasAttribute, 제거하는 removeAttribute 메서드가 있다.

HTML 요소의 내용을 읽고 쓰기

innerHTML

innerHTML 프로퍼티는 요소 안의 HTML 코드를 읽거나 쓸 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p id="cards">&hearts;하트는 <em>승려</em>라는 뜻입니다.</p>
<script>
var para = document.getElementById('cards');
para.innerHTML = "&diams;다이아는 <strong>상인</strong>이라는 뜻입니다."
console.log(para.innerHTML);
</script>
</body>
</html>

textContent와 innerText 프로퍼티

textContent는 웹 페이지에 표시했을 때의 텍스트 정보를 표시한다. 다만 IE9 이하에서는 사용이 안되니 크로스 브라우징을 신경써야 한다.
대신 사용할 수 있는 프로퍼티가 innerText이다. 둘의 차이점은

  • textContent는 script 요소 안의 텍스트를 반환하지만 innerText는 반환하지 않는다.
  • textContent는 공백 문자를 그대로 반환하지만 innerText는 남는 공백 문자를 제거한다.
  • innerText는 table, tbody, tr 요소 등의 테이블 요소를 수정할 수 없다.

라는 차이가 있다.

노드 생성, 삽입, 삭제

노드 객체를 생성하는 주요 메서드

document.createElement : 요소 노드 객체 생성
document.createAttribute : 속성 노드 객체 생성
document.createTextNode : 텍스트 노드 객체 생성
document.createComment : 주석 노드 객체 생성
document.createDocumentFragment : 도큐먼트 프레그먼트
document.importNode : 다른 문서에 있는 노드 복사

노드 삽입하기

  1. appendChild

인수로 넘긴 노드 객체를 해당 요소에 마지막 자식 노드로 삽입한다. 그 객체가 DOM 트리에 추가되고 각 노드에 계층 구조를 정의하는 프로퍼티가 바뀐다.

  1. insertBefore

지정한 자식 노드 바로 앞에 노드 객체를 삽입할 때 사용하는 메서드이다.

이 두 메서드를 문서에 삽입하면 해당 노드를 현재 위치에서 삭제하고 새로운 위치에 삽입하게 된다. 그래서 결과적으론 그 노드가 이동하게 되는 셈이다.

  1. HTML 요소를 생성하는 편리한 함수.

실제 요소를 동적으로 생성하는 코드는 요소 이름, 속성 이름과 속성 값, 자식 노드 목록을 지정하는 코드가 대부분이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./elt.js"></script>
<script>
window.onload = function() {
var headline = elt('h1', null, "DOM에 관하여");
document.body.appendChild(headline);
var input = elt('input', { type: "button", value: "click"});
document.body.appendChild(input);
var bloodtypes = ["A","B","O","AB"];
var form = elt("form", {id: "menu"});
var select = elt("select", {name: "bloodtype", id: "bloodtype"});
bloodtypes.forEach(type => {
select.appendChild(elt("option", null, type));
});
form.appendChild(select);
document.body.appendChild(form);
};
</script>
</head>
<body>
<!-- <form id="menu">
<select name="bloodtype" id="bloodtype">
<option>A</option>
<option>B</option>
<option>O</option>
<option>AB</option>
</select>
</form> -->
</body>
</html>
  1. 노드 삭제하기

removeChild 메서드는 노드의 자식 노드를 삭제한다.

  1. 노드 치환하기

replaceChild는 인수로 받은 자식 노드를 제거하고 새로운 노드로 치환한다.

HTML 요소의 위치

좌표계

요소 위치를 표현하기 위한 좌표계는 뷰포트 좌표계와 문서 좌표계가 있다. 단위는 픽셀이고 x축 방향은 오른쪽, y축 방향은 아래쪽이다.

뷰포트 좌표계는 왼쪽 위 꼭짓점을 원점으로 하는 웹 브라우저에서 문서의 내용을 표시하는 영역이다. (그래서 윈도우 좌표계라고도 한다.)
문서좌표계는 왼쪽 위 꼭짓점을 원점으로 하며 웹 브라우저 표시 영역 안에 표시된다. 그래서 문서를 스크롤하면 문서 좌표계의 원점이 뷰표트를 따라 이동한다.

HTML 요소의 위치와 크기

getBoundingClientRect 메서드는 뷰포트 좌표계로 측정한 해당 요소의 border 박스 위치와 크기 정보를 담은 객체를 반환한다.

left, top, right, bottom, width, height 프로퍼티를 가지고 있다.

IE9 이후에는

window.innerWidth, window.innerHeight를 사용하여 뷰포트늬 너비나 높이를 구할 수 있다.

스크롤한 거리 구하기

각 브라우저 마다 제공하는 프로퍼티가 다르다.

  • IE, Firefox

document.documentElement.scrollLeft(Top) : x축 방향(y축 방향)으로 스크롤한 거리.

  • Chrome, Safari, Ofera, Edge

document.body.scrollLeft(Top) : x축 방향(y축 방향)으로 스크롤한 거리.

  • firefox, chrome, safari, ofera, edge, IE9 이상

window.pageX(Y)Offset : X축(Y축) 방향으로 스크롤한 거리.

특정 위치로 스크롤 하기

window 객체의 scrollTo 메서드는 문서 좌표를 인수로 받으며, 원점까지 스크롤 합니다.