Recursive DOM manipulation & Tree UIs

devfish·2023년 2월 15일
0

Javascript

목록 보기
28/30

DOM Traversal Methods & Patterns

Recursion can enable easy access to deeper HTML elements to find and manipulate elements inside the DOM. We can easily use callback functions to manipulate select elements depending on the situation (what we can to do.)

Walking the DOM

Traversing DOM (sub)trees with a recursive walk the DOM function (JSCookBook)

//It's this part that's key: 
function walkTheDOM(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
        walkTheDOM(node, func);
        node = node.nextSibling;
    }
}

// Example usage: Process all Text nodes on the page
walkTheDOM(document.body, function (node) {
    if (node.nodeType === 3) { // Is it a Text node?
        var text = node.data.trim();
        if (text.length > 0) { // Does it have non white-space text content?
            // process text
        }
    }
});

DOM manipulation via callback

const callback = (element) => {
    if (element.innerText && element.className === 'change') {
        element.innerText = `${element.innerText} changed`
    }
}
domTraversal(document.querySelector('body'), callback);

//--------------------------------------------------------

function domTraversal(parentElement, callback) {
    parentElement.childNodes.forEach((element) => {
        callback(element);
        if (element.hasChildNodes()) {
            domTraversal(element, callback);
        }
        return;
    });
}

Invoked via anonymous function:

    (function domTraversal(parentElement){
      parentElement.childNodes.forEach((element) => {
        if(element.innerText && element.className === 'change'){
            element.innerText = `${element.innerText} changed`
        }
        if(element.hasChildNodes()){
            domTraversal(element);
        }
        return;
      });
    })(document.querySelector('body'))

TreeUI

github reference code

Data: Menu

const menu = [
  {
    type: 'group',
    name: '음료',
    children: [
      {
        type: 'group',
        name: '콜드 브루',
        children: [
          { type: 'item', name: '나이트로 콜드 브루' },
          { type: 'item', name: '돌체 콜드 브루' },
          { type: 'item', name: '제주 비자림 콜드 브루' },
          { type: 'item', name: '콜드 브루' },
        ],
      },
      {
        type: 'group',
        name: '프라푸치노',
        children: [
          { type: 'item', name: '애플 쿠키 크림 프라푸치노' },
          { type: 'item', name: '더블 에스프레소 칩 프라푸치노' },
          { type: 'item', name: '모카 프라푸치노' },
          { type: 'item', name: '피스타치오 크림 프라푸치노' },
        ],
      },
      {
        type: 'group',
        name: '블렌디드',
        children: [
          { type: 'item', name: '망고 바나나 블렌디드' },
          { type: 'item', name: '딸기 요거트 블렌디드' },
          { type: 'item', name: '자몽 셔벗 블렌디드' },
          { type: 'item', name: '피치 & 레몬 블렌디드' },
        ],
      },
      {
        type: 'group',
        name: '티',
        children: [
          { type: 'item', name: '라임 패션 티' },
          { type: 'item', name: '민트 블렌드 티' },
          { type: 'item', name: '아이스 유스베리 티' },
          { type: 'item', name: '아이스 캐모마일 블렌드 티' },
        ],
      },
      {
        type: 'group',
        name: '주스',
        children: [
          { type: 'item', name: '한방에 쭉 감당' },
          { type: 'item', name: '파이팅 청귤' },
          { type: 'item', name: '딸기주스' },
          { type: 'item', name: '도와주 흑흑' },
        ],
      },
    ],
  },
  {
    type: 'group',
    name: '음식',
    children: [
      {
        type: 'group',
        name: '빵',
        children: [
          { type: 'item', name: '트러플 미니 스콘' },
          { type: 'item', name: '보늬밤 몽블랑 데니쉬' },
          { type: 'item', name: '고소한 치즈 베이글' },
          { type: 'item', name: '미니 클래식 스콘' },
        ],
      },
      {
        type: 'group',
        name: '케이크',
        children: [
          { type: 'item', name: '밀당 에그 타르트' },
          { type: 'item', name: '마스카포네 티라미수 케이크' },
          { type: 'item', name: '블루베리 쿠키 치즈 케이크' },
          { type: 'item', name: '부드러운 생크림 카스텔라' },
        ],
      },
      {
        type: 'group',
        name: '샌드위치',
        children: [
          { type: 'item', name: '애플 까망베르 샌드위치' },
          { type: 'item', name: '트리플 머쉬룸 치즈 샌드위치' },
          { type: 'item', name: '로스트 치킨 샐러드 밀 박스' },
          { type: 'item', name: 'B.E.L.T 샌드위치' },
        ],
      },
      {
        type: 'group',
        name: '과일',
        children: [
          { type: 'item', name: '하루 한 컵 RED' },
          { type: 'item', name: '한라봉 가득 핸디 젤리' },
        ],
      },
      {
        type: 'group',
        name: '스낵',
        children: [
          { type: 'item', name: '리저브 초콜릿 세트' },
          { type: 'item', name: '로스티드 아몬드 앤 초콜릿' },
          { type: 'item', name: '마카롱' },
          { type: 'item', name: '자일리톨 캔디 크리스탈 민트' },
        ],
      },
      {
        type: 'group',
        name: '아이스크림',
        children: [
          { type: 'item', name: '자바 칩 유기농 바닐라 아이스크림' },
          { type: 'item', name: '넛츠 초콜릿 아포가토' },
          { type: 'item', name: '바닐라 아포가토' },
        ],
      },
    ],
  },
  {
    type: 'group',
    name: '굿즈',
    children: [
      {
        type: 'group',
        name: '머그',
        children: [
          { type: 'item', name: '우리 한글 블랙 머그 473ml' },
          { type: 'item', name: '서울 투어 머그 355ml' },
          { type: 'item', name: '스타벅스 1호점 머그 400ml' },
          { type: 'item', name: '서울 제주 데이머그 세트' },
        ],
      },
      {
        type: 'group',
        name: '텀블러',
        children: [
          { type: 'item', name: 'SS 부산 투어 텀블러 355ml' },
          { type: 'item', name: 'SS 블랙 헤리티지 오드리 텀블러 355ml' },
          { type: 'item', name: 'SS 에치드 실버 텀블러 473ml' },
        ],
      },
      {
        type: 'group',
        name: '악세사리',
        children: [
          { type: 'item', name: '리저브 오렌지 카드 홀더' },
          { type: 'item', name: '스타벅스 1호점 에코백' },
          { type: 'item', name: '스타벅스 1호점 랩탑 파우치' },
        ],
      },
    ],
  },
  {
    type: 'group',
    name: '카드',
    children: [
      { type: 'item', name: '10000원권' },
      { type: 'item', name: '30000원권' },
      { type: 'item', name: '50000원권' },
      { type: 'item', name: '100000원권' },
    ],
  },
];

createTreeView implementation

Flow

  • document.getElementById('root') - find DOM element to hold other tags
  • document.createElement('li') - create DOM element (tags)
  • adding text to the element(tags)
    • Element.textContent = 'text' - fill it with text
    • document.createTextNode('text') - create a text node (child) that you can append to any element
  • Element.setAttribute('type', 'checkbox'):
    equivalent to Element.type="checkbox'
  • invoke recursive function and pass the data & node's children
  • Element.append(li) - insert the argument(li) into the DOM element
    • Element.append() vs. Element.appendChild(): append lets you
      append multiple items (nodes & strings) to the element, that's the only difference!

//my implementation
const root = document.getElementById('root');
function createTreeView(menu, currentNode) {
  for (let i = 0; i < menu.length; i++){
    const li = document.createElement("li");
    const textLi = document.createTextNode(menu[i].name);
    if(menu[i].children){
      const checkbox = document.createElement("input");
      checkbox.type="checkbox";
      checkbox.classList.add("checked");
      const span = document.createElement("span");
      const ul = document.createElement("ul");
      span.appendChild(textLi);
      li.append(checkbox, span, ul); //append는 여러개 달 수 있음! appendchild랑 똑같음 이것만 다름
      createTreeView(menu[i].children,ul);
   }else{
      li.appendChild(textLi);
      // li.textContent = menu[i].name;
   }
   currentNode.appendChild(li);
  }

}

createTreeView(menu, root);
// 호승님 REFERENCE 
const root = document.getElementById('root');
function createTreeView(menu, currentNode) {

  menu.map(item => {
    const listElements = document.createElement("li")
    currentNode.append(listElements)
    //* 더미데이터에서 type key의 값이 item이면, 더 이상 자식요소가 없음

    if (item.type === 'item') { //& 자식 요소가 없으니까 이름만 출력해주기
      listElements.textContent = item['name'];
      // currentNode.appendChild(listElements)
    } else { //*그럼 이제 자식 요소가 있을 때 처리를...
      const box = document.createElement('input')
      box.setAttribute('type', 'checkbox');
      listElements.append(box);
      const name = document.createElement('span');
      name.textContent = item['name'];
      listElements.append(name);
      const ul = document.createElement('ul')
      listElements.append(ul);
      createTreeView(item.children, ul)
    }
  })
}
createTreeView(menu, root);
//

//REFERENCE CODE
// TODO: implement createTreeView function via recursion

const root = document.getElementById('root');
// imperative solution
function createTreeView(menu, currentNode) {
  for (let i = 0; i < menu.length; i++) {
    const li = document.createElement('li');
    if (menu[i].children) {
      const input = document.createElement('input');
      input.type = 'checkbox';
      const span = document.createElement('span');
      span.textContent = menu[i].name;

      const ul = document.createElement('ul');
      li.append(input, span, ul);
      currentNode.append(li);

      createTreeView(menu[i].children, ul);
    } else {
      li.textContent = menu[i].name;
      currentNode.append(li);
    }
  }
}
// declarative solution
// function createTreeView(menu, currentNode) {
//   const appendCollectionsInfo = function (currentNode, collections) {
//     const li = document.createElement('li');
//     const checkbox = document.createElement('input');
//     checkbox.type = 'checkbox';
//     const isItem = collections.type === 'item' ? true : false;
//     if (isItem) {
//       li.append(collections.name);
//     } else {
//       const groupName = document.createElement('span');
//       groupName.textContent = collections.name;
//       li.append(checkbox, groupName);
//     }
//     const haveChildren = Boolean(collections.children);
//     const children = collections.children;
//     if (haveChildren) {
//       const ul = document.createElement('ul');
//       createTreeView(children, ul);
//       li.append(ul);
//     }
//     currentNode.append(li);
//     return currentNode;
//   };
//   menu.reduce(appendCollectionsInfo, currentNode);
// }

createTreeView(menu, root);

References

profile
la, di, lah

0개의 댓글