Javascript Node
HTML 문서의 정보는 노드 트리(node tree)라고 불리는 계층적 구조에 저장됩니다.
이러한 노드 트리는 노드들의 집합이며, 노드 간의 관계를 보여줍니다.
노드 트리는 최상위 레벨인 루트 노드(root node)로부터 시작하여, 가장 낮은 레벨인 텍스트 노드까지 뻗어 내려갑니다.
자바스크립트에서는 HTML DOM을 이용하여 노드 트리에 포함된 모든 노드에 접근할 수 있습니다.
노드 간의 관계
노드 트리의 모든 노드는 서로 계층적 관계를 맺고 있습니다.
노드 트리의 가장 상위에는 단 하나의 루트 노드(root node)가 존재합니다.
루트 노드를 제외한 모든 노드는 단 하나의 부모 노드(parent node)만을 가집니다.
모든 요소 노드는 자식 노드(child node)를 가질 수 있습니다.
형제 노드(sibling node)란 같은 부모 노드를 가지는 모든 노드를 가리킵니다.
조상 노드(ancestor node)란 부모 노드를 포함해 계층적으로 현재 노드보다 상위에 존재하는 모든 노드를 가리킵니다.
자손 노드(descendant node)란 자식 노드를 포함해 계층적으로 현재 노드보다 하위에 존재하는 모든 노드를 가리킵니다.
노드 간의 관계를 이용하여 접근하는 방법
HTML DOM에서 노드 간의 관계는 다음과 같은 프로퍼티로 정의됩니다.
1. parentNode : 부모 노드
2. childNodes : 자식 노드 리스트
3. firstChild : 첫 번째 자식 노드
4. lastChild : 마지막 자식 노드
5. nextSibling : 다음 형제 노드
6. previousSibling : 이전 형제 노드
위와 같은 프로퍼티를 이용하여 원하는 노드에 손쉽게 접근할 수 있습니다.
노드에 대한 정보
노드에 대한 정보는 다음과 같은 프로퍼티를 통해 접근할 수 있습니다.
1. nodeName
2. nodeValue
3. nodeType
이 프로퍼티들은 특별히 다른 인터페이스를 이용하지 않고도, 해당 노드의 정보에 직접 접근할 수 있는 방법을 제공합니다.
// innerHTML == firstChild.nodeValue 이다.
console.log(divElement[0].innerHTML);
console.log(divElement[0].childNodes[0].nodeValue);
빈 텍스트 노드의 처리
현재 대부분의 주요 웹 브라우저는 모두 W3C DOM 모델을 지원하지만, 그 처리 방식에 있어 약간씩의 차이가 있습니다.
그중에서도 가장 큰 차이점은 띄어쓰기와 줄 바꿈을 처리하는 방식입니다.
파이어폭스나 기타 브라우저들은 띄어쓰기나 줄 바꿈을 텍스트 노드(text node)로 취급합니다.
하지만 익스플로러는 띄어쓰기나 줄 바꿈을 텍스트 노드로 취급하지 않습니다.
따라서 자식 노드나 형제 노드를 이용하여 원하는 노드에 접근하려고 하면 브라우저 간에 차이가 발생하게 됩니다.
이 차이를 없애는 가장 손쉬운 방법은 nodeType 프로퍼티를 사용하여 선택된 요소의 타입을 검사하는 것입니다.
// html 노드의 모든 자식 노드 중에서 첫 번째 노드의 이름을 선택함.
console.log(document.childNodes[1].childNodes[0].nodeName); // HEAD
console.log(document.childNodes[1].childNodes[1].nodeName); // 크롬에선 줄 띄움 때문에 텍스트 노드로 인식
console.log(document.childNodes[1].childNodes[2].nodeName); // BODY
노드 리스트(node list)
노드 리스트는 getElementsByTagName() 메소드나 childNodes 프로퍼티의 값으로 반환되는 객체입니다.
이 객체는 HTML 문서와 같은 순서로 문서 내의 모든 노드를 리스트 형태로 저장하고 있습니다.
리스트의 각 노드는 0부터 시작하는 인덱스를 이용하여 접근할 수 있습니다.
// node list
const divElement = document.getElementsByTagName('div');
console.log(divElement);
노드의 추가
다음 메소드를 사용하면 특정 위치에 새로운 노드를 추가할 수 있습니다.
1. appendChild()
2. insertBefore()
3. insertData()
// node add
var appendChildCount = 1;
document.getElementById("appendChild").onclick = function(){
var text = document.createElement("p");
text.innerHTML=`안녕하세요 ${appendChildCount}번째 저는 p 에요`;
appendChildCount++;
divElement[0].appendChild(text);
};
노드의 생성
생성할 노드의 종류에 따라 다음과 같은 메소드를 사용할 수 있습니다.
1. createElement()
2. createAttribute()
3. createTextNode()
document.getElementById("createAttribute").onclick = function(){
var div2 = document.getElementById("div2"); // 아이디가 "text"인 요소를 선택함.
var newAttribute = document.createAttribute("style"); // 새로운 style 속성 노드를 생성함.
newAttribute.value = "color:red";
div2.setAttributeNode(newAttribute); // 해당 요소의 속성 노드로 추가함.
};
노드의 제거
다음 메소드를 사용하면 특정 노드를 제거할 수 있습니다.
1. removeChild()
2. removeAttribute()
document.getElementById("removeChild").onclick = function(){
var child = divElement[0].childNodes[0];
divElement[0].removeChild(child);
};
노드의 복제
cloneNode() 메소드를 사용하면 특정 노드를 복제할 수 있습니다.
복제할노드.cloneNode(자식노드복제여부);
- 자식 노드 복제 여부 : 전달된 값이 true이면 복제되는 노드의 모든 속성 노드와 자식 노드도 같이 복제하며, false이면 속성 노드만 복제하고 자식 노드는 복제하지 않습니다.
이벤트 리스너(event listener)
이벤트 리스너란 이벤트가 발생했을 때 그 처리를 담당하는 함수를 가리키며, 이벤트 핸들러(event handler)라고도 합니다.
지정된 타입의 이벤트가 특정 요소에서 발생하면, 웹 브라우저는 그 요소에 등록된 이벤트 리스너를 실행시킵니다.
객체나 요소에 프로퍼티로 등록하는 방법
객체나 요소에 프로퍼티로 이벤트 리스너를 등록할 때는 다음과 같은 방법을 사용할 수 있습니다.
1. 자바스크립트 코드에서 프로퍼티로 등록
2. HTML 태그에 속성으로 등록
자바스크립트 코드에서 프로퍼티로 등록하는 방법은 거의 모든 브라우저가 대부분의 이벤트 타입을 지원하고 있습니다.
이 방법의 단점은 이벤트 타입별로 오직 하나의 이벤트 리스너만을 등록할 수 있다는 점입니다.
window.onload = function() { // 이 함수는 HTML 문서가 로드될 때 실행됨.
var text = document.getElementById("text"); // 아이디가 "text"인 요소를 선택함.
text.innerHTML = "HTML 문서가 로드되었습니다.";
}
<p onclick="alert('문자열을 클릭했어요!')">이 문자열을 클릭해 보세요!</p>
또한, 다음과 같이 HTML 태그에 속성으로 이벤트 리스너를 등록할 수도 있습니다.
이 방법의 단점으로는 HTML 코드에 자바스크립트 코드가 추가됨으로써 가독성이 안 좋아지며, 유지보수도 힘들어집니다.
객체나 요소의 메소드에 이벤트 리스너를 전달하는 방법
객체나 요소의 메소드에 이벤트 리스너를 전달할 때는 다음 메소드를 사용할 수 있습니다.
1. addEventListener()
2. attachEvent()
addEventListener() 메소드는 거의 모든 브라우저에서 지원하는 이벤트 리스너 등록을 위한 메소드입니다.
이 메소드의 원형은 다음과 같습니다.
대상객체.addEventListener(이벤트명, 실행할이벤트리스너, 이벤트전파방식)
1. 이벤트 명 : 이벤트 리스너를 등록할 이벤트 타입을 문자열로 전달합니다.
2. 실행할 이벤트 리스너 : 지정된 이벤트가 발생했을 때 실행할 이벤트 리스너를 전달합니다.
3. 이벤트 전파 방식 : false면 버블링(bubbling) 방식으로, true면 캡처링(capturing) 방식으로 이벤트를 전파합니다.
버블링 전파 방식은 이벤트가 발생한 요소부터 시작해서, DOM 트리를 따라 위쪽으로 올라가며 전파되는 방식입니다.
캡쳐링 전파 방식은 이벤트가 발생한 요소까지 DOM 트리의 최상위부터 아래쪽으로 내려가면 전파되는 방식입니다.
var btn = document.getElementById("btn"); // 아이디가 "btn"인 요소를 선택함.
btn.addEventListener("click", clickBtn); // 선택한 요소에 click 이벤트 리스너를 등록함.
btn.addEventListener("mouseover", mouseoverBtn); // 선택한 요소에 mouseover 이벤트 리스너를 등록함.
btn.addEventListener("mouseout", mouseoutBtn); // 선택한 요소에 mouseout 이벤트 리스너를 등록함.
function clickBtn() {
document.getElementById("text").innerHTML = "버튼이 클릭됐어요!";
}
function mouseoverBtn() {
document.getElementById("text").innerHTML = "버튼 위에 마우스가 있네요!";
}
function mouseoutBtn() {
document.getElementById("text").innerHTML = "버튼 밖으로 마우스가 나갔어요!";
}
이벤트 리스너 삭제
removeEventListener() 메소드를 사용하면, 등록된 이벤트 리스너를 손쉽게 삭제할 수 있습니다.
var removeBtn = document.getElementById("removeBtn");
removeBtn.addEventListener("click", clickRemoveBtn);
function clickRemoveBtn() {
btn.removeEventListener("mouseover", mouseoverBtn);
btn.removeEventListener("mouseout", mouseoutBtn);
document.getElementById("text").innerHTML = "이벤트 리스너가 삭제되었어요!";
}
이벤트 객체(event object)
이벤트 객체(event object)란 특정 타입의 이벤트와 관련이 있는 객체입니다.
이벤트 객체는 해당 타입의 이벤트에 대한 상세 정보를 저장하고 있습니다.
모든 이벤트 객체는 이벤트의 타입을 나타내는 type 프로퍼티와 이벤트의 대상을 나타내는 target 프로퍼티를 가집니다.
이러한 이벤트 객체는 이벤트 리스너가 호출될 때 인수로 전달됩니다.
var btn2 = document.getElementById("btn2"); // 아이디가 "btn"인 요소를 선택함.
btn2.addEventListener("click", clickBtn); // 선택한 요소에 click 이벤트 리스너를 등록함.
function clickBtn(event) {
document.getElementById("text2").innerHTML =
"이 이벤트의 타입은 " + event.type + "이며, 이벤트의 대상은 " + event.target + "입니다.";
}
전체 코드
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Home Page</title>
<link rel="stylesheet" type="text/css" href="myhomepage.css">
</head>
<body>
<div>
div 1
</div>
<div>
div 2
</div>
<div>
div 3
</div>
<div>
div 4
</div>
<button id="appendChild">appendChild</button>
<button id="createAttribute">createAttribute</button>
<button id="removeChild">removeChild</button>
<button id="cloneNode">cloneNode</button>
<button id="btn">addEventListener</button>
<div>
<p id="text"></p>
</div>
<button id="removeBtn">removeEventListener</button>
<div>
<p id="text2"></p>
</div>
<button id="btn2">event object</button>
<script src="myhomepage2.js"></script>
</body>
</html>
@import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&display=swap');
div {
/* 테두리 굵기, 선 모양, 색상 */
border: 1px solid rgb(189, 84, 84);
/* div안의 text들은 가운데 정렬을 해라! */
text-align: center;
/* margin을 위에서 부터 시계방향으로 10, 20, 30, 40px 씩 준다. */
/* margin: 10px 20px 30px 40px; */
margin: 15px;
padding : 20px;
font-family: 'Black Han Sans', sans-serif;
}
button:hover {
background-color: black;
color: white;
}
button {
margin: 10px;
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
/* display: block; */
font-size: 16px;
}
#add {
text-align: center;
}
input {
margin: 10px auto;
background-color: #f6ff4b;
border: none;
color: rgb(0, 0, 0);
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: block;
font-size: 16px;
}
// HTML 문서의 모든 자식 노드 중에서 두 번째 노드의 이름을 선택함.
console.log(document.childNodes[1].nodeName); // HTML
// html 노드의 모든 자식 노드 중에서 첫 번째 노드의 이름을 선택함.
console.log(document.childNodes[1].childNodes[0].nodeName); // HEAD
console.log(document.childNodes[1].childNodes[1].nodeName); // 크롬에선 줄 띄움 때문에 텍스트 노드로 인식
console.log(document.childNodes[1].childNodes[2].nodeName); // BODY
// node list
const divElement = document.getElementsByTagName('div');
console.log(divElement);
// innerHTML == firstChild.nodeValue 이다.
console.log(divElement[0].innerHTML);
console.log(divElement[0].childNodes[0].nodeValue);
// node add
var appendChildCount = 1;
document.getElementById("appendChild").onclick = function(){
var text = document.createElement("p");
text.innerHTML=`안녕하세요 ${appendChildCount}번째 저는 p 에요`;
appendChildCount++;
divElement[0].appendChild(text);
};
//createAttribute
document.getElementById("createAttribute").onclick = function(){
var newAttribute = document.createAttribute("style"); // 새로운 style 속성 노드를 생성함.
newAttribute.value = "color:red";
divElement[1].setAttributeNode(newAttribute); // 해당 요소의 속성 노드로 추가함.
};
//removeChild
document.getElementById("removeChild").onclick = function(){
var child = divElement[0].childNodes[0];
divElement[0].removeChild(child);
};
//cloneNode
document.getElementById("cloneNode").onclick = function(){
var body = document.childNodes[1].childNodes[2];
body.appendChild(divElement[0].cloneNode(true));
};
var btn = document.getElementById("btn"); // 아이디가 "btn"인 요소를 선택함.
btn.addEventListener("click", clickBtn); // 선택한 요소에 click 이벤트 리스너를 등록함.
btn.addEventListener("mouseover", mouseoverBtn); // 선택한 요소에 mouseover 이벤트 리스너를 등록함.
btn.addEventListener("mouseout", mouseoutBtn); // 선택한 요소에 mouseout 이벤트 리스너를 등록함.
function clickBtn() {
document.getElementById("text").innerHTML = "버튼이 클릭됐어요!";
}
function mouseoverBtn() {
document.getElementById("text").innerHTML = "버튼 위에 마우스가 있네요!";
}
function mouseoutBtn() {
document.getElementById("text").innerHTML = "버튼 밖으로 마우스가 나갔어요!";
}
var removeBtn = document.getElementById("removeBtn");
removeBtn.addEventListener("click", clickRemoveBtn);
function clickRemoveBtn() {
btn.removeEventListener("mouseover", mouseoverBtn);
btn.removeEventListener("mouseout", mouseoutBtn);
document.getElementById("text").innerHTML = "이벤트 리스너가 삭제되었어요!"
}
var btn2 = document.getElementById("btn2"); // 아이디가 "btn"인 요소를 선택함.
btn2.addEventListener("click", clickBtn); // 선택한 요소에 click 이벤트 리스너를 등록함.
function clickBtn(event) {
document.getElementById("text2").innerHTML =
"이 이벤트의 타입은 " + event.type + "이며, 이벤트의 대상은 " + event.target + "입니다.";
}
참고
tcpschool.com/javascript/js_dom_node
'대내 활동 > 웹프로그래밍 상상튜터링' 카테고리의 다른 글
웹 프로그래밍 멘토멘티 프로그램 5회차 (0) | 2020.09.20 |
---|---|
웹 프로그래밍 멘토멘티 프로그램 3회차 (2) | 2020.09.05 |
웹 프로그래밍 멘토멘티 프로그램 2회차 (2) | 2020.08.30 |
웹 프로그래밍 상상튜터링 프로그램 1회차 (0) | 2020.08.25 |