목표
- 컬럼에 따른 정렬 기능 구현하기
- 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">← 뒤로가기</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);
}
};
- 데이터 가져오기(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 = `../`;
}
};
- 테이블 렌더링(renderTable):
renderTable
함수는 datas
배열의 각 요소를 반복하면서 테이블 행을 생성합니다.
while
루프를 사용하여 테이블의 기존 행을 모두 제거합니다.
- 각 데이터 항목에 대해
<tr>
요소와 <td>
요소를 생성하여 테이블 행을 구성합니다.
- 생성한 요소에 데이터를 할당하고, 테이블에 추가합니다.
정렬 기능(sortTable)
let sortState = {
index: null,
nickname: null,
dateOfHire: null,
reverse: false,
};
let filterState = {
verified: false,
};
const sortTable = (column) => {
const sameColumnClicked = sortState[column] !== null;
if (sameColumnClicked) {
sortState.reverse = !sortState.reverse;
} else {
sortState.reverse = false;
}
sortState[column] = !sortState[column]
? "asc"
: sortState[column] === "asc"
? "desc "
: null;
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);
};
- 정렬 기능(
sortTable
)
sortTable
함수는 클릭된 열을 기준으로 데이터를 정렬합니다.
sortState
객체는 현재 정렬 상태를 추적합니다.
- 동일한 열을 클릭하면 정렬 순서를 변경합니다.
filteredData
배열은 필터링된 데이터를 저장합니다. 체크박스가 선택된 경우, "Verified"
가 true
인 데이터만 가져옵니다.
filteredData
배열을 선택된 열과 현재 정렬 순서에 따라 정렬합니다.
- 정렬된 데이터를
filteredRender
함수에 전달하여 테이블을 다시 렌더링합니다.
필터링 기능(applyFilter)
filterCheckBox.addEventListener("change", () => {
filterState.verified = filterCheckBox.checked;
applyFilter();
});
const applyFilter = () => {
const filteredData = filterState.verified
? datas.filter((item) => item.verified)
: datas;
filteredRender(filteredData);
};
- 필터링 기능(
applyFilter
)
filterCheckBox
체크박스의 변경 이벤트를 감지하여 필터링 상태를 변경합니다.
filterState
객체는 현재 필터링 상태를 추적합니다.
- 체크박스가 선택된 경우,
filterState.verified
를 true
로 설정하고, 필터링을 적용합니다.
datas
배열에서 "Verified"
가 true
인 데이터만 가져와 filteredData
배열에 저장합니다.
filteredData
배열을 filteredRender
함수에 전달하여 테이블을 다시 렌더링합니다.
최상단 이동 기능(topBtn)
topBtn.addEventListener("click", () => {
window.scrollTo(0, 0);
});
- 최상단 이동 기능(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">← 뒤로가기</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>
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?.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);
});
};
let sortState = {
index: null,
nickname: null,
dateOfHire: null,
reverse: false,
};
let filterState = {
verified: false,
};
const sortTable = (column) => {
const sameColumnClicked = sortState[column] !== null;
if (sameColumnClicked) {
sortState.reverse = !sortState.reverse;
} else {
sortState.reverse = false;
}
sortState[column] = !sortState[column]
? "asc"
: sortState[column] === "asc"
? "desc "
: null;
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");
});
filterCheckBox.addEventListener("change", () => {
filterState.verified = filterCheckBox.checked;
applyFilter();
});
const applyFilter = () => {
const filteredData = filterState.verified
? datas.filter((item) => item.verified)
: datas;
filteredRender(filteredData);
};
topBtn.addEventListener("click", () => {
window.scrollTo(0, 0);
});
});
</script>
</body>
</html>
구현 화면