[구현테스트]_정렬과 필터링

hanseungjune·2023년 7월 11일
0

구현테스트

목록 보기
1/2
post-thumbnail

목표

  • 컬럼에 따른 정렬 기능 구현하기
  • Verified 필터링에 따른 컬럼 정렬 기능 구현하기
  • Verified 여부에 따른 필터링 기능 구현하기
  • Top 버튼 기능 구현하기

코드 및 로직

기본 DOM

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>정렬 및 필터링</title>
  </head>
  <body>
    <h1>1. 편리한 기능이 필요해</h1>
    <hr />

    <input type="checkbox" id="filter-verified" />

    <table>
      <thead>
        <tr>
          <th id="index-header" class="sortable">Index</th>
          <th id="nickname-header" class="sortable">Nickname</th>
          <th id="date-of-hire-header" class="sortable">Date of Hire</th>
          <th>Verified</th>
        </tr>
      </thead>
      <tbody id="table-body"></tbody>
    </table>
    <a href="../index.html" class="back-link">&#8592; 뒤로가기</a>
    <button class="scroll-up" id="topBtn">TOP</button>
    <script>
      let datas = [];
      const fetchData = async () => {
        try {
          const response = await fetch("index.json");
          const data = await response.json();
          datas.push(...data);
          console.log(datas);
          renderTable();
        } catch (error) {
          console.error(error);
        }
      };

      const renderTable = () => {
        const tableBody = document.getElementById("table-body");

        // 정렬할 때, 지우기
        while (tableBody.firstChild) {
          tableBody.removeChild(tableBody.firstChild);
        }

        datas.forEach((item) => {
          const row = document.createElement("tr");
          const indexCell = document.createElement("td");
          indexCell.textContent = item.id + 1;
          row.appendChild(indexCell);

          const nicknameCell = document.createElement("td");
          nicknameCell.textContent = item.name;
          row.appendChild(nicknameCell);

          const dateOfHireCell = document.createElement("td");
          dateOfHireCell.textContent = item.dateOfHire;
          row.appendChild(dateOfHireCell);

          const verifiedCell = document.createElement("td");
          verifiedCell.textContent = item.verified ? "Yes" : "No";
          row.appendChild(verifiedCell);

          tableBody.appendChild(row);
        });
      };

      fetchData();

      window.onload = function () {
        const currentUrl = window.location.href;

        if (currentUrl.includes("web")) {
          const link = document.querySelector(".back-link");
          link.href = `../`;
        }
      };
    </script>
    <script>
      // 여기에 코드 구현
    </script>
  </body>
</html>

데이터 가져오기(fetchData)

let datas = [];
const fetchData = async () => {
  try {
    const response = await fetch("index.json");
    const data = await response.json();
    datas.push(...data);
    console.log(datas);
    renderTable();
  } catch (error) {
    console.error(error);
  }
};
  1. 데이터 가져오기(fetchData):
    • fetchData 함수는 fetch를 사용하여 "index.json" 파일에서 데이터를 가져오는 비동기 함수입니다.
    • await 키워드를 사용하여 비동기 작업이 완료될 때까지 기다립니다.
    • 데이터를 JSON 형식으로 변환하고 datas 배열에 추가합니다.
    • renderTable 함수를 호출하여 데이터를 테이블로 렌더링합니다.

테이블 렌더링(renderTable)

let datas = [];
const fetchData = async () => {
  try {
    const response = await fetch("index.json");
    const data = await response.json();
    datas.push(...data);
    console.log(datas);
    renderTable();
  } catch (error) {
    console.error(error);
  }
};

const renderTable = () => {
  const tableBody = document.getElementById("table-body");

  // 정렬할 때, 지우기
  while (tableBody.firstChild) {
    tableBody.removeChild(tableBody.firstChild);
  }

  datas.forEach((item) => {
    const row = document.createElement("tr");
    const indexCell = document.createElement("td");
    indexCell.textContent = item.id + 1;
    row.appendChild(indexCell);

    const nicknameCell = document.createElement("td");
    nicknameCell.textContent = item.name;
    row.appendChild(nicknameCell);

    const dateOfHireCell = document.createElement("td");
    dateOfHireCell.textContent = item.dateOfHire;
    row.appendChild(dateOfHireCell);

    const verifiedCell = document.createElement("td");
    verifiedCell.textContent = item.verified ? "Yes" : "No";
    row.appendChild(verifiedCell);

    tableBody.appendChild(row);
  });
};

fetchData();

window.onload = function () {
  const currentUrl = window.location.href;

  if (currentUrl.includes("web")) {
    const link = document.querySelector(".back-link");
    link.href = `../`;
  }
};
  1. 테이블 렌더링(renderTable):
    • renderTable 함수는 datas 배열의 각 요소를 반복하면서 테이블 행을 생성합니다.
    • while 루프를 사용하여 테이블의 기존 행을 모두 제거합니다.
    • 각 데이터 항목에 대해 <tr> 요소와 <td> 요소를 생성하여 테이블 행을 구성합니다.
    • 생성한 요소에 데이터를 할당하고, 테이블에 추가합니다.

정렬 기능(sortTable)

// null : 일반, asc : 오름차순, desc : 내림차순
let sortState = {
  index: null,
  nickname: null,
  dateOfHire: null,
  reverse: false,
};

// false : 전부, true: 'Yes'
let filterState = {
  verified: false,
};

const sortTable = (column) => {
  // 오름차순이나 내림차순이면 true
  // 일반이면 false
  const sameColumnClicked = sortState[column] !== null;

  // reverse가 false면 오름차순, true 내림차순
  if (sameColumnClicked) {
    sortState.reverse = !sortState.reverse;
  } else {
    sortState.reverse = false;
  }

  // null이면 asc, asc면 desc, desc면 null
  sortState[column] = !sortState[column]
    ? "asc"
  : sortState[column] === "asc"
    ? "desc "
  : null;

  // checkbox 클릭하면, 필터링한 데이터. 아니면 그대로
  const filteredData = filterState.verified
  ? datas.filter((item) => item.verified)
  : datas;

  // 정렬하기
  filteredData.sort((a, b) => {
    // 배열안의 객체의 프로퍼티가 다 다름
    let valueA = a[column];
    let valueB = b[column];

    // 날짜는 날짜 객체로 비교해야 비교 가능
    if (column === "dataOfHire") {
      valueA = new Date(valueA);
      valueB = new Date(valueB);
    }

    // 뒤집기(자리만 바꾸는거)
    if (sortState.reverse) {
      [valueA, valueB] = [valueB, valueA];
    }

    if (valueA < valueB) {
      // 내림차순
      return -1;
    } else if (valueA > valueB) {
      // 오름차순
      return 1;
    } else {
      // 기본
      return 0;
    }
  });

  filteredRender(filteredData);
};
  1. 정렬 기능(sortTable)
    • sortTable 함수는 클릭된 열을 기준으로 데이터를 정렬합니다.
    • sortState 객체는 현재 정렬 상태를 추적합니다.
    • 동일한 열을 클릭하면 정렬 순서를 변경합니다.
    • filteredData 배열은 필터링된 데이터를 저장합니다. 체크박스가 선택된 경우, "Verified"true인 데이터만 가져옵니다.
    • filteredData 배열을 선택된 열과 현재 정렬 순서에 따라 정렬합니다.
    • 정렬된 데이터를 filteredRender 함수에 전달하여 테이블을 다시 렌더링합니다.

필터링 기능(applyFilter)

// check = true, nocheck = false
filterCheckBox.addEventListener("change", () => {
  filterState.verified = filterCheckBox.checked;
  applyFilter();
});

// verified가 true면 'Yes'만 가져오기
const applyFilter = () => {
  const filteredData = filterState.verified
  ? datas.filter((item) => item.verified)
  : datas;
  filteredRender(filteredData);
};
  1. 필터링 기능(applyFilter)
    • filterCheckBox 체크박스의 변경 이벤트를 감지하여 필터링 상태를 변경합니다.
    • filterState 객체는 현재 필터링 상태를 추적합니다.
    • 체크박스가 선택된 경우, filterState.verifiedtrue로 설정하고, 필터링을 적용합니다.
    • datas 배열에서 "Verified"true인 데이터만 가져와 filteredData 배열에 저장합니다.
    • filteredData 배열을 filteredRender 함수에 전달하여 테이블을 다시 렌더링합니다.

최상단 이동 기능(topBtn)

// 최상단으로 올리기
topBtn.addEventListener("click", () => {
  window.scrollTo(0, 0);
});
  1. 최상단 이동 기능(topBtn)
    • "TOP" 버튼을 클릭하면 페이지를 최상단으로 스크롤합니다.
    • window.scrollTo(0, 0)를 사용하여 페이지의 스크롤 위치를 (0, 0) 좌표로 이동시킵니다.

전체 코드(실습용)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>정렬 및 필터링</title>
  </head>
  <body>
    <h1>1. 편리한 기능이 필요해</h1>
    <hr />

    <input type="checkbox" id="filter-verified" />

    <table>
      <thead>
        <tr>
          <th id="index-header" class="sortable">Index</th>
          <th id="nickname-header" class="sortable">Nickname</th>
          <th id="date-of-hire-header" class="sortable">Date of Hire</th>
          <th>Verified</th>
        </tr>
      </thead>
      <tbody id="table-body"></tbody>
    </table>
    <a href="../index.html" class="back-link">&#8592; 뒤로가기</a>
    <button class="scroll-up" id="topBtn">TOP</button>
    <script>
      let datas = [];
      const fetchData = async () => {
        try {
          const response = await fetch("index.json");
          const data = await response.json();
          datas.push(...data);
          console.log(datas);
          renderTable();
        } catch (error) {
          console.error(error);
        }
      };

      const renderTable = () => {
        const tableBody = document.getElementById("table-body");

        // 정렬할 때, 지우기
        while (tableBody.firstChild) {
          tableBody.removeChild(tableBody.firstChild);
        }

        datas.forEach((item) => {
          const row = document.createElement("tr");
          const indexCell = document.createElement("td");
          indexCell.textContent = item.id + 1;
          row.appendChild(indexCell);

          const nicknameCell = document.createElement("td");
          nicknameCell.textContent = item.name;
          row.appendChild(nicknameCell);

          const dateOfHireCell = document.createElement("td");
          dateOfHireCell.textContent = item.dateOfHire;
          row.appendChild(dateOfHireCell);

          const verifiedCell = document.createElement("td");
          verifiedCell.textContent = item.verified ? "Yes" : "No";
          row.appendChild(verifiedCell);

          tableBody.appendChild(row);
        });
      };

      fetchData();

      window.onload = function () {
        const currentUrl = window.location.href;

        if (currentUrl.includes("web")) {
          const link = document.querySelector(".back-link");
          link.href = `../`;
        }
      };
    </script>
    <script>
      // DOMContentLoaded DOM 객체에 내용을 로드하기 위해서
      window.addEventListener("DOMContentLoaded", () => {
        const indexHeader = document.getElementById("index-header");
        const nicknameHeader = document.getElementById("nickname-header");
        const dateOfHireHeader = document.getElementById("date-of-hire-header");
        const filterCheckBox = document.getElementById("filter-verified");
        const topBtn = document.getElementById("topBtn");

        // 정렬 기능 구현하기
        const filteredRender = (filteredDatas = datas) => {
          const tableBody = document.getElementById("table-body");

          // 정렬할 때, 지우기
          while (tableBody.firstChild) {
            tableBody.removeChild(tableBody.firstChild);
          }

          // 기본적으로는 페칭 데이터, 이미 정렬된 데이터일수 있으니까 filteredDatas
          filteredDatas?.forEach((item) => {
            const row = document.createElement("tr");
            const indexCell = document.createElement("td");
            indexCell.textContent = item.id + 1;
            row.appendChild(indexCell);

            const nicknameCell = document.createElement("td");
            nicknameCell.textContent = item.name;
            row.appendChild(nicknameCell);

            const dateOfHireCell = document.createElement("td");
            dateOfHireCell.textContent = item.dateOfHire;
            row.appendChild(dateOfHireCell);

            const verifiedCell = document.createElement("td");
            verifiedCell.textContent = item.verified ? "Yes" : "No";
            row.appendChild(verifiedCell);

            tableBody.appendChild(row);
          });
        };

        // null : 일반, asc : 오름차순, desc : 내림차순
        let sortState = {
          index: null,
          nickname: null,
          dateOfHire: null,
          reverse: false,
        };

        // false : 전부, true: 'Yes'
        let filterState = {
          verified: false,
        };

        const sortTable = (column) => {
          // 오름차순이나 내림차순이면 true
          // 일반이면 false
          const sameColumnClicked = sortState[column] !== null;

          // reverse가 false면 오름차순, true 내림차순
          if (sameColumnClicked) {
            sortState.reverse = !sortState.reverse;
          } else {
            sortState.reverse = false;
          }

          // null이면 asc, asc면 desc, desc면 null
          sortState[column] = !sortState[column]
            ? "asc"
            : sortState[column] === "asc"
            ? "desc "
            : null;

          // checkbox 클릭하면, 필터링한 데이터. 아니면 그대로
          const filteredData = filterState.verified
            ? datas.filter((item) => item.verified)
            : datas;

          // 정렬하기
          filteredData.sort((a, b) => {
            // 배열안의 객체의 프로퍼티가 다 다름
            let valueA = a[column];
            let valueB = b[column];

            // 날짜는 날짜 객체로 비교해야 비교 가능
            if (column === "dataOfHire") {
              valueA = new Date(valueA);
              valueB = new Date(valueB);
            }

            // 뒤집기(자리만 바꾸는거)
            if (sortState.reverse) {
              [valueA, valueB] = [valueB, valueA];
            }

            if (valueA < valueB) {
              // 내림차순
              return -1;
            } else if (valueA > valueB) {
              // 오름차순
              return 1;
            } else {
              // 기본
              return 0;
            }
          });

          filteredRender(filteredData);
        };

        // 클릭이벤트
        indexHeader.addEventListener("click", () => {
          sortTable("id");
        });

        // 클릭이벤트
        nicknameHeader.addEventListener("click", () => {
          sortTable("name");
        });

        // 클릭이벤트
        dateOfHireHeader.addEventListener("click", () => {
          sortTable("dateOfHire");
        });

        // check = true, nocheck = false
        filterCheckBox.addEventListener("change", () => {
          filterState.verified = filterCheckBox.checked;
          applyFilter();
        });

        // verified가 true면 'Yes'만 가져오기
        const applyFilter = () => {
          const filteredData = filterState.verified
            ? datas.filter((item) => item.verified)
            : datas;
          filteredRender(filteredData);
        };

        // 최상단으로 올리기
        topBtn.addEventListener("click", () => {
          window.scrollTo(0, 0);
        });
      });
    </script>
  </body>
</html>

구현 화면

profile
필요하다면 공부하는 개발자, 한승준

0개의 댓글