이 예제에서는 버튼을 클릭했을 때 페이지에 새로운 테이블을 추가해 볼 거예요.
<input type="button" value="Generate a table" />
function generateTable() {
// <table> 요소와 <tbody> 요소를 만듭니다.
const tbl = document.createElement("table");
const tblBody = document.createElement("tbody");
// 모든 셀을 만듭니다.
for (let i = 0; i < 2; i++) {
// 테이블의 행(row)을 만듭니다.
const row = document.createElement("tr");
for (let j = 0; j < 2; j++) {
// <td> 요소와 텍스트 노드를 만들고, 그 텍스트 노드를
// <td>의 내용으로 넣은 다음, 만들어진 <td>를
// 테이블 행의 끝에 추가합니다.
const cell = document.createElement("td");
const cellText = document.createTextNode(`cell in row ${i}, column ${j}`);
cell.appendChild(cellText);
row.appendChild(cell);
}
// 완성된 행을 테이블 본문(tbody)의 끝에 추가합니다.
tblBody.appendChild(row);
}
// <tbody>를 <table> 안에 넣습니다.
tbl.appendChild(tblBody);
// 완성된 <table>을 <body> 안에 추가합니다.
document.body.appendChild(tbl);
// tbl의 border(테두리) 속성을 '2'로 설정합니다.
tbl.setAttribute("border", "2");
}
document
.querySelector("input[type='button']")
.addEventListener("click", generateTable);
table {
margin: 1rem auto;
}
td {
padding: 0.5rem;
}
우리가 요소들과 텍스트 노드를 만들어낸 순서를 한번 눈여겨보세요.
<table> 요소를 만들었어요.<table> 요소의 자식인 <tbody> 요소를 만들었죠.<tbody> 요소의 자식들이 될 <tr> 요소들을 만들었어요.<tr> 요소 안에서는 또 다른 반복문을 사용해 <tr>의 자식인 <td> 요소들을 만들어냈고요.<td> 요소에 들어갈 테이블 셀의 텍스트 노드를 생성했답니다.<table>, <tbody>, <tr>, <td> 요소들, 그리고 텍스트 노드까지 전부 다 만들고 나면, 이번엔 아까와 반대 순서로 각 객체를 자기 부모에게 덧붙여줘요(append).
<td> 요소에 붙여줍니다.cell.appendChild(cellText);<td> 요소를 부모인 <tr> 요소에 붙여주고요.row.appendChild(cell);<tr> 요소들을 부모인 <tbody> 요소에 붙입니다.tblBody.appendChild(row);<tbody> 요소를 부모인 <table> 요소에 붙여주죠.tbl.appendChild(tblBody);<table> 요소를 부모인 <body> 요소에 붙여줍니다.document.body.appendChild(tbl);이 방식은 꼭 기억해 두세요. W3C DOM을 활용해 프로그래밍할 때 정말 자주 쓰이는 패턴이거든요. 위에서 아래로 내려가며 요소를 만들고(top-down), 아래에서 위로 올라가며 자식을 부모에게 붙여주는(bottom-up) 방식이에요.
자바스크립트 코드가 만들어낸 HTML 마크업은 이렇게 생겼어요.
<table border="2">
<tbody>
<tr>
<td>cell is row 0 column 0</td>
<td>cell is row 0 column 1</td>
</tr>
<tr>
<td>cell is row 1 column 0</td>
<td>cell is row 1 column 1</td>
</tr>
</tbody>
</table>
그리고 이 코드가 만들어낸 <table> 요소와 그 자식 요소들의 DOM 객체 트리는 다음 이미지와 같아요.

단 몇 가지 DOM 메서드만 사용해서 이 테이블과 그 안의 자식 요소들을 전부 만들어낼 수 있어요. 여러분이 만들고자 하는 구조의 트리 모델을 머릿속에 잘 그려두는 걸 잊지 마세요. 그러면 필요한 코드를 작성하기가 훨씬 수월해질 거예요. 위 그림 1의 <table> 트리를 보면, <table> 요소는 <tbody>라는 단 하나의 자식을 가집니다. <tbody>는 두 개의 자식을 갖고요. 각 <tbody>의 자식(<tr>) 역시 두 개의 자식(<td>)을 가집니다. 그리고 마지막으로 각각의 <td>는 단 하나의 자식, 바로 텍스트 노드를 갖게 되는 구조랍니다.
이 예제에서는 버튼을 클릭했을 때 특정 단락(paragraph)의 배경색을 바꿔볼 거예요.
<body>
<input type="button" value="Set paragraph background color" />
<p>hi</p>
<p>hello</p>
</body>
function setBackground() {
// 먼저 문서 안의 모든 p 요소를 가져옵니다.
const paragraphs = document.getElementsByTagName("p");
// 목록에서 두 번째 단락을 가져옵니다.
const secondParagraph = paragraphs[1];
// 인라인 스타일로 배경색을 설정합니다.
secondParagraph.style.background = "red";
}
document.querySelector("input").addEventListener("click", setBackground);
getElementsByTagName(tagNameValue)은 모든 DOM Element나](https://developer.mozilla.org/en-US/docs/Web/API/Element)나) 루트 Document 요소에서 사용할 수 있는 메서드예요. 이 메서드를 호출하면 해당 태그 이름과 일치하는 모든 자손 요소들을 배열 형태로 반환해 준답니다. 목록의 첫 번째 요소는 배열의 [0]번 자리에 위치하게 되죠.
우리는 이 예제에서 다음과 같은 단계들을 거쳤어요.
p 요소들을 가져옵니다.const paragraphs = document.getElementsByTagName("p");p 요소들의 목록에서 두 번째 단락 요소를 뽑아내죠.const secondParagraph = paragraphs[1];
paragraph 객체의 style 속성을 사용해서 배경색을 빨간색(red)으로 설정해 줍니다.secondParagraph.style.background = "red";텍스트 노드를 만들 때는 document 객체를 이용해서 createTextNode 메서드를 호출하면 돼요. 그냥 원하는 텍스트 내용을 전달해주기만 하면 끝이랍니다. 그러면 이 메서드는 텍스트 노드를 나타내는 객체를 반환해 줘요.
myTextNode = document.createTextNode("world");
이 말은 즉, 텍스트 데이터가 "world"인 TEXT_NODE 타입의 노드(글자 뭉치)가 하나 만들어졌고, myTextNode라는 변수가 이 새로운 노드 객체를 가리키고(참조하고) 있다는 뜻이에요. 이 텍스트를 실제 HTML 페이지 화면에 띄우려면, 이 텍스트 노드를 다른 어떤 노드 요소의 자식으로 쏙 넣어줘야(append) 한답니다.
자, 이제 secondParagraph.appendChild(node_element)를 호출해서, 아까 만든 그 텍스트 요소가 두 번째 <p> 요소의 새로운 자식이 되도록 만들어 볼까요.
secondParagraph.appendChild(myTextNode);
이 예제를 직접 실행해 보시면, hello라는 단어와 world라는 단어가 helloworld 처럼 딱 붙어서 나오는 걸 보실 수 있을 거예요. 눈으로 보이는 HTML 페이지 상에서는 hello와 world라는 두 개의 텍스트 노드가 마치 하나의 노드처럼 보이지만, 문서 모델(document model) 구조상으로는 여전히 두 개의 독립적인 노드라는 걸 꼭 기억하세요. 방금 추가한 두 번째 노드는 TEXT_NODE 타입의 새로운 노드이고, 두 번째 <p> 태그의 두 번째 자식으로 자리 잡고 있는 거랍니다. 문서 트리 안에서 방금 만든 이 텍스트 노드 객체가 어디에 위치하는지는 아래 그림을 보면 쉽게 이해되실 거예요.

참고:
createTextNode()와appendChild()를 조합하는 건 hello와 world라는 단어 사이에 공백(white space)을 넣을 수 있는 아주 간단한 방법이에요. 또 하나 기억해둘 중요한 점은,appendChild메서드는 방금 world가 hello 뒤에 붙은 것처럼 항상 마지막 자식의 바로 뒤에 새로운 자식을 덧붙인다는 거예요. 만약에 어떤 텍스트 노드를 hello와 world 사이에 끼워 넣고 싶다면,appendChild대신insertBefore라는 메서드를 사용하셔야 해요.
createElement를 사용하면 새로운 HTML 요소나 그 외에 여러분이 원하는 어떤 요소든 뚝딱 만들어낼 수 있어요. 예를 들어, 요소의 자식으로 들어갈 새로운 <p> 요소를 만들고 싶다면, 아까 예제에서 썼던 myBody 변수를 활용해서 새로운 요소 노드를 덧붙여주면(append) 되죠. 노드를 생성하려면 document.createElement("tagname")을 호출하면 됩니다. 바로 이렇게요.
myNewPTagNode = document.createElement("p");
myBody.appendChild(myNewPTagNode);

당연히 노드들을 지워버릴 수도 있어요. 다음 코드는 두 번째 <p> 요소인 secondParagraph에서 ("world"라는 단어를 품고 있는) myTextNode 텍스트 노드를 제거하는 역할을 해요.
secondParagraph.removeChild(myTextNode);
재미있는 건, 이렇게 지웠다고 해서 ("world"를 담고 있는) myTextNode 텍스트 노드가 세상에서 완전히 사라진 건 아니라는 거예요. 여전히 존재하고 있죠. 아래 코드를 보시면 방금 떼어낸 myTextNode를 우리가 새로 만들었던 <p> 요소인 myNewPTagNode에 다시 찰싹 붙여주는 걸 볼 수 있어요.
myNewPTagNode.appendChild(myTextNode);
이런 과정들을 거쳐서 최종적으로 변경된 객체 트리의 모습은 이렇게 생겼답니다.

아래 그림은 우리가 앞선 예제에서 만들었던 테이블의 객체 트리 구조를 보여주고 있어요.

테이블을 만드는 기본 과정은 다음과 같아요.
참고:
스크립트의 맨 마지막을 보시면 새로운 코드가 한 줄 추가되어 있어요. 테이블의border속성을 설정하는 데setAttribute()라는 또 다른 DOM 메서드를 사용했죠. 이setAttribute()메서드는 속성 이름과 속성값, 이렇게 두 개의 인자를 받아요. 이setAttribute메서드만 있으면 어떤 요소의 어떤 속성이든 마음대로 설정할 수 있답니다.
// body에 대한 참조를 가져옵니다.
const myBody = document.getElementsByTagName("body")[0];
// <table>과 <tbody> 요소를 생성합니다.
const myTable = document.createElement("table");
const myTableBody = document.createElement("tbody");
// 모든 셀을 만드는 과정입니다.
for (let j = 0; j < 3; j++) {
// <tr> 요소를 만듭니다.
const myCurrentRow = document.createElement("tr");
for (let i = 0; i < 4; i++) {
// <td> 요소를 만듭니다.
const myCurrentCell = document.createElement("td");
// 텍스트 노드를 만듭니다.
const currentText = document.createTextNode(
`cell is row ${j}, column ${i}`,
);
// 방금 만든 텍스트 노드를 <td> 셀 안에 넣습니다.
myCurrentCell.appendChild(currentText);
// <td> 셀을 <tr> 행 안에 넣습니다.
myCurrentRow.appendChild(myCurrentCell);
}
// 완성된 <tr> 행을 <tbody> 안에 넣습니다.
myTableBody.appendChild(myCurrentRow);
}
// <tbody>를 <table> 안에 넣습니다.
myTable.appendChild(myTableBody);
// 완성된 <table>을 <body> 안에 넣습니다.
myBody.appendChild(myTable);
// myTable의 border 속성을 2로 설정합니다.
myTable.setAttribute("border", "2");
이 예제에서는 두 가지 새로운 DOM 속성을 소개해 드릴게요. 첫 번째는 myCell의 자식 노드 목록을 통째로 가져올 때 사용하는 childNodes 속성이에요. 이 childNodes 목록에는 노드의 이름이나 종류에 상관없이 모든 자식 노드들이 전부 다 포함된답니다. getElementsByTagName()과 마찬가지로, 이것도 노드들의 목록을 반환해 줘요.
이 둘의 차이점은요, 첫째로 getElementsByTagName()은 딱 지정한 태그 이름을 가진 요소들만 골라서 반환하지만, childNodes는 자식뿐만 아니라 그 아래 단계의 모든 자손들까지 빠짐없이 긁어모아 온다는 거예요.
이렇게 목록을 반환받고 나면, [x] 방식을 사용해서 내가 원하는 특정 자식 항목만 쏙 뽑아올 수 있어요. 아래 예제에서는 테이블의 두 번째 행, 두 번째 셀에 있는 텍스트 노드를 찾아내서 myCellText라는 변수에 얌전히 저장해 두는 모습을 볼 수 있습니다.
그런 다음, 이 예제의 결과를 화면에 보여주기 위해서 새로운 텍스트 노드를 하나 만드는데요. 이 새로운 텍스트 노드의 내용은 방금 찾아낸 myCellText의 데이터로 채우고, 이걸 다시 <body> 요소의 자식으로 덧붙여(append) 주고 있어요.
참고:
다루고 있는 객체가 텍스트 노드라면, data 속성을 사용해서 그 노드 안에 들어있는 텍스트 내용만 깔끔하게 뽑아낼 수 있답니다.
const myBody = document.getElementsByTagName("body")[0];
const myTable = myBody.getElementsByTagName("table")[0];
const myTableBody = myTable.getElementsByTagName("tbody")[0];
const myRow = myTableBody.getElementsByTagName("tr")[1];
const myCell = myRow.getElementsByTagName("td")[1];
// myCell의 자식 노드 목록(childNodes) 중 첫 번째 요소 항목을 가져옵니다.
const myCellText = myCell.childNodes[0];
// currentText의 내용은 myCellText의 데이터 내용이 됩니다.
const currentText = document.createTextNode(myCellText.data);
myBody.appendChild(currentText);
아까 살펴본 예제 1의 끝부분을 보면 myTable 객체에 setAttribute를 호출하는 코드가 있었죠. 그땐 테이블의 border(테두리) 속성을 설정하려고 이 메서드를 불렀어요. 반대로, 이미 설정된 속성값이 뭔지 궁금해서 가져오고 싶을 땐 getAttribute 메서드를 쓰시면 됩니다. 이렇게요.
myTable.getAttribute("border");
JavaScript 변수에 객체를 잘 담아두셨다면, 이제 style 속성을 직접 만지작거릴 수 있어요. 아래 코드는 아까 봤던 코드를 살짝 고친 건데요, 두 번째 열(column)에 있는 셀들은 모두 화면에서 숨겨버리고(hide), 첫 번째 열에 있는 셀들은 전부 배경색을 빨갛게 바꿔버리는 기능을 해요. 여기서 style 속성을 직접 건드려서 값을 설정하고 있다는 점을 눈여겨보세요.
const myBody = document.getElementsByTagName("body")[0];
const myTable = document.createElement("table");
const myTableBody = document.createElement("tbody");
for (let row = 0; row < 2; row++) {
const myCurrentRow = document.createElement("tr");
for (let col = 0; col < 2; col++) {
const myCurrentCell = document.createElement("td");
const currentText = document.createTextNode(`cell is: ${row}${col}`);
myCurrentCell.appendChild(currentText);
myCurrentRow.appendChild(myCurrentCell);
// 만약 열(column) 번호가 0이라면 셀의 배경색을 빨간색으로 설정합니다.
// 열 번호가 1이라면 그 셀은 화면에서 숨겨버립니다.
if (col === 0) {
myCurrentCell.style.background = "red";
} else {
myCurrentCell.style.display = "none";
}
}
myTableBody.appendChild(myCurrentRow);
}
myTable.appendChild(myTableBody);
myBody.appendChild(myTable);