• JavaScript의 cloneNode와 이벤트 버블링을 활용한 HTML template tag 활용법
    Frontend/JavaScript 2023. 4. 11. 03:16
    반응형

    HTML의 template 태그는 웹 개발에서 재사용성을 높이는 방법 중 하나입니다.

    template 태그를 사용하면 웹 페이지에서 일부분을 독립적으로 만들어 놓고, 필요할 때마다 JavaScript를 통해 동적으로 삽입할 수 있습니다. 이를 통해 코드의 가독성과 유지보수성을 높일 수 있습니다.

    예를 들면, 어떤 컴포넌트에 필요한 HTML 코드를 문자열로만 관리하는 것은 굉장히 불편합니다. ES6에 와서 백틱으로 템플릿 리터럴을 관리하는 기능이 추가되었지만, IDE 차원에서 코드가 인식이 잘 되지 않는 등 여러 문제가 있습니다.

    template 태그의 공식문서를 확인해보면 템플릿 태그는 렌더링 되지 않습니다.

    DOM 상에 존재하지 않지만, 해당 코드를 JavaScript가 동적으로 읽어들여 복제(.cloneNode())할 수 있습니다.

    template 태그와 그 자식들을 복제 후, DocumentFragment가 된 상태에서 해당 엘리먼트(와 자식들)를 특정 엘리먼트의 하위 요소로 붙여주면(.appendChild()) 뒤늦게 생성하여 붙일 수 있게 됩니다.

    즉, 해당 template tag를 마치 컴포넌트처럼 동적으로 찍어내는 행위가 가능합니다. 물론 이 방법 말고도 템플릿 리터럴이나 웹 컴포넌트와 같은 방법으로도 구현이 가능하지만, 이 방법은 실제 엘리먼트를 복제할 수 있다는 특징이 있습니다.

     

    이 글에서는 template tag를 그저 단순한 컴포넌트가 아닌, 이벤트 버블링과 함께 사용을 하는 방법을 보여드리려고 합니다. (클로저는 덤입니다.)

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    
    <body>
      <button id="add-button">추가하기</button>
      <div id="list"></div>
      <template id="template">
        <div class="component">
          <span class="text">아이템 </span>
          <button class="delete-button">삭제</button>
        </div>
      </template>
      <script type="module" src="./index.js"></script>
    </body>
    
    </html>
    console.log('import 성공!');
    const template = document.getElementById('template');
    const container = document.getElementById('list');
    
    function addItem() {
      let count = 0;
      return () => {
        const newComponent = template.content.cloneNode(true);
        newComponent.querySelector('.component').id = `item${count++}`;
        newComponent.querySelector('.text').innerText = `아이템 ${count}`;
        container.appendChild(newComponent);
      }
    }
    
    function deleteItem(button) {
      container.removeChild(button.parentNode);
    }
    
    const addButton = document.getElementById('add-button');
    addButton.addEventListener('click', addItem());
    
    // 이벤트 버블링으로 리스너 수를 줄임
    container.addEventListener('click', event => {
      // 클릭 이벤트가 버튼일 때에만 삭제 요청
      if (event.target.classList.contains('delete-button')) {
        deleteItem(event.target);
      }
    });



    JavaScript의 cloneNode 메서드는 template 태그를 복사하는 데에 유용하게 사용될 수 있습니다. cloneNode 메서드를 사용하면 template 의 내용을 복사한 새로운 노드를 생성할 수 있으며, 이를 다른 위치에 삽입할 수 있습니다.

    addItem() 에서는 이를 적극 활용하고 있는데,

    1. count 변수는 클로저에 의해 addItem()가 호출 당하는 것 만으로도 증가하게 됩니다.

    2. addItem()에서 실질적으로 동작하는 반환된 함수(IIFE)에서 기존 템플릿을 복제하여 id와 텍스트를 정해주고, list의 자식에 붙여줍니다.

     

    또한, 이벤트 버블링을 활용하면 template 태그를 동적으로 추가하는 경우에도 각 항목마다 이벤트 리스너를 추가하지 않아도 됩니다. (이벤트 버블링은 이벤트가 발생한 요소에서 시작하여 계속 상위 요소로 전파되는 동작을 말합니다.)

    따라서 template 내부에 있는 삭제 버튼을 클릭하면 버튼에서 이벤트가 발생하여 template 태그가 속한 상위 요소(container인 #list)까지 이벤트가 전파되고, 상위 요소에 등록된 이벤트 리스너에서 핸들링할 수 있습니다.

    다만 container가 어떤 엘리먼트가 눌렸는지를 확인할 필요가 있어 해당 버튼이 'delete-button'라는 클래스를 가진 경우에만 반응하도록 설계하여 하나의 이벤트 리스너가 해당 컨테이너(즉, 리스트)를 관리하게 될 수 있게 됩니다.

    반응형

    댓글