본문으로 건너뛰기

요소 만들어 붙이기

자바스크립트 목차웹 페이지 움직이기레슨 68 / 76

목차

할 일 목록처럼 배열에 담긴 데이터 개수만큼 화면에 <li> 줄을 그려야 할 때는 자바스크립트로 새 요소를 만들어 부모 요소에 붙입니다. 이때 쓰는 목록 그리기 패턴은 언제나 1. 목록 비우기 → 2. 하나씩 꺼내 요소 만들기 → 3. 붙이기 세 단계로 이루어집니다.

5단계 중 1단계. render()를 시작할 때 list.textContent = ''로 기존 자식 <li>들을 먼저 비웁니다.

1요소 하나 만들어 붙이기: createElement와 append

새 HTML 요소를 만들 때는 **document.createElement('태그이름')**를 부르고, 만든 요소를 화면의 부모 요소 끝에 넣을 때는 **부모.append(자식)**를 부릅니다.

'use strict';

const list = document
  .querySelector('#todo-list');
const li = document
  .createElement('li');
li.textContent = '우유 사기';
list.append(li);
실행 결과
<li>우유 사기</li> 추가됨
  • document.createElement('li')⁠를 부른 직후에 li는 아직 메모리 안에만 떠 있어 화면에 보이지 않습니다.
  • list.append(li)로 화면에 있는 #todo-list에 붙여야 비로소 눈에 보입니다.
  • append에는 여러 자식 요소를 쉼표로 나란히 넘겨 li.append(box, span, btn)⁠처럼 한 번에 순서대로 붙일 수도 있습니다.

2패턴: 목록 그리기 (render 함수)

배열 todos를 화면의 #todo-list로 그리는 함수를 render()로 묶어 봅니다. 세 단계 주석을 눈여겨보세요.

'use strict';

const todos = [
  { id: 1, text: '우유 사기' },
  { id: 2, text: '복습하기' },
  { id: 3, text: '산책하기' },
];
const list = document
  .querySelector('#todo-list');

이어서 세 단계로 목록을 그리는 render 함수를 만들고 부릅니다.

function render() {
  // 1. 목록 비우기
  list.textContent = '';
  // 2. 하나씩 꺼내 요소 만들기
  for (const t of todos) {
    const li = document
      .createElement('li');
    li.textContent = t.text;
    // 3. 붙이기
    list.append(li);
  }
}
render();
실행 결과
- 우유 사기
- 복습하기
- 산책하기
  1. 1. 목록 비우기 (list.textContent = '';): 이전에 그려 둔 자식 <li>들을 먼저 모두 지웁니다.
  2. 2. 하나씩 꺼내 요소 만들기: for (const t of todos)⁠로 배열을 돌며 각 항목마다 새 <li>를 만들고 글자와 클래스를 채웁니다.
  3. 3. 붙이기 (list.append(li)): 완성한 <li>를 부모 list 끝에 붙입니다.

3왜 그리기 전에 목록을 먼저 비워야 할까?

만약 render() 첫 줄에서 list.textContent = '';⁠로 비우는 단계를 빼먹고 render()를 두 번 부르면 어떻게 될까요? 배열 동작으로 똑같이 확인해 봅니다.

const items = [
  '우유',
  '복습',
  '산책',
];
const screen = [];
function badRender() {
  for (const x of items) {
    screen.push(x);
  }
}
badRender();
badRender();
console.log(screen.length);
console.log(screen.join(', '));
실행 결과
6
우유, 복습, 산책, 우유, 복습, 산책

이전에 그려 둔 3줄 뒤에 새 3줄이 또 붙어서 3개가 6개로 두 배가 됩니다. 반면 첫 줄에서 기존 목록을 먼저 비우면 render()를 몇 번 다시 불러도 언제나 배열 items와 똑같은 3줄만 깔끔하게 그려집니다.

예제

결과를 먼저 생각한 뒤 펼쳐 보세요. 맞혀 보기는 점수에 들지 않습니다.

할 일 줄 <li> 안에 <span class="text">와 삭제 단추 <button class="delete"> 두 자식을 만들어 붙이는 코드입니다. 빈칸 없이 실행했을 때 만들어진 li 안의 자식 순서는 어떻게 될까요?

'use strict';

const li = document
  .createElement('li');
const span = document
  .createElement('span');
span.className = 'text';
span.textContent = '우유 사기';

const btn = document
  .createElement('button');
btn.className = 'delete';
btn.textContent = '×';

li.append(span, btn);
결과 보기
실행 결과
<li>
  <span>우유 사기</span>
  <button>×</button>
</li>

li.append(span, btn)은 넘긴 순서대로 span을 먼저 붙이고 그 뒤에 btn을 자식 요소로 붙입니다.

자주 틀리는 곳

  1. render() 첫 줄에서 list.textContent = ''⁠를 빼먹는다: 할 일을 하나 추가하고 render()를 다시 불렀을 때 기존 3줄이 그대로 남은 채 4줄이 더 붙어 7줄이 됩니다. 다시 그리는 함수는 반드시 비우기부터 시작합니다.
  2. document.createElement⁠만 부르고 append를 안 한다: 요소를 만들고 속성까지 다 채웠더라도 부모 요소에 .append()로 붙이지 않으면 화면에는 아무것도 나타나지 않습니다.
  3. 배열 todos.push(...)만 하고 render()를 다시 부르지 않는다: 배열에 새 객체를 넣었다고 해서 브라우저가 알아서 새 <li>를 만들어 주지 않습니다. 배열을 바꾼 뒤에는 반드시 render()를 다시 불러야 화면이 따라 바뀝니다.

확인 문제

  1. 목록 그리기 패턴의 세 단계를 순서대로 쓰세요.
정답

1. 목록 비우기 (list.textContent = '';) → 2. 하나씩 꺼내 요소 만들기 (document.createElement) → 3. 붙이기 (list.append)입니다.

  1. document.createElement('p')⁠로 문단 요소 p를 만들고 p.textContent = '할 일이 없습니다.';⁠까지만 실행했을 때 화면에 문단이 보이지 않는 까닭은 무엇이며, main 요소 끝에 보이게 하려면 어떤 코드를 더해야 하나요?
정답

createElement로 만든 요소는 아직 메모리 안에만 있고 문서 나무에 붙지 않았기 때문입니다. main.append(p);를 실행해 부모 요소에 붙여야 화면에 보입니다.

  1. todos 배열에 항목이 3개 들어 있습니다. list.textContent = '';⁠가 없는 render() 함수를 세 번 연달아 부르면 #todo-list 안의 <li>는 모두 몇 개가 되나요?
정답

비우지 않고 3개씩 세 번 덧붙이므로 9개가 됩니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로