동적테이블 기능 목표
테이블은 이름, 번호 , 직급 컬럼으로 테이블을 생성
정렬 sort는 테이블 헤더를 누르면 내림차순 오름차순 순으로 toggle이 되도록 설정
search는 항목별로 fiter를 하여 검색 및 조회
체크박스를 통해 filter를 하여 조회
html 파일
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>직원 인적사항</title>
<style>
#tablebody, .theader{text-align: center;}
#search {text-align: center; margin: 30px 0px;}
th {background-color: aliceblue;}
#Title{text-align: center; margin-top : 40px;}
#positionselect{
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 3%;
}
.levelselect{margin-right: 10%;}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1 id="Title">직원 인적사항</h1>
<table class= "table table-striped table-bordered table-hover" id="peopletable">
<tr>
<th class="theader" onclick=sortContent(0)>이름</th>
<th class="theader" onclick=sortContent(1)>전화번호</th>
<th class="theader" onclick=sortContent(2)>직급</th>
</tr>
<div class="row" id="search">
<div class="col-md-3">
<select class="form-control" id="searchSelect">
<option value="name">이름</option>
<option value="phone">전화번호</option>
<option value="position">직급</option>
</select>
</div>
<div class = "col-md-7">
<input class="form-control" id="inputbody" type="text" placeholder="검색어를 입력해주세요">
</div>
<div class = "col-md-2">
<button class="btn btn-outline-primary" type="button" id="searchBtn">검색</button>
</div>
</div>
<div class="row" id="positionselect">
<span class="levelselect">직급 선택</span>
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="대표">대표
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="부장">부장
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="이사">이사
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="차장">차장
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="과장">과장
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="대리">대리
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" name="checkbox" type="checkbox" value="사원">사원
</div>
</div>
</div>
</div>
<tbody id="tablebody"></tbody>
</table>
</div>
<script src="Doc3.js"></script>
</body>
</html>
Doc.js 파일
js 파일에는 JSDoc을 주석으로 달아 만들어 보았다.
//테이블 데이터
/**@type {object} peopleArr 배열 요소 object */
var peopleArr = [
{name : "홍길동", phone : "010-2222-2141", position : "이사"},
{name : "김남일", phone : "010-3433-7232", position : "차장"},
{name : "박지성", phone : "010-6234-4564", position : "사원"},
{name : "히딩크", phone : "010-5453-7554", position : "대리"},
{name : "박주영", phone : "010-9234-0981", position : "사원"},
{name : "손흥민", phone : "010-9165-3074", position : "대리"},
{name : "에시앙", phone : "010-4934-7545", position : "사원"},
{name : "드록바", phone : "010-5763-8282", position : "과장"},
{name : "라이언", phone : "010-5631-0248", position : "대리"},
{name : "조규성", phone : "010-6233-3504", position : "팀장"},
{name : "박지수", phone : "010-9045-5781", position : "부장"},
{name : "고종욱", phone : "010-3334-2401", position : "사원"},
{name : "사이먼", phone : "010-5129-7358", position : "사원"},
{name : "오란씨", phone : "010-3042-7663", position : "사원"},
{name : "도미노", phone : "010-4077-6256", position : "차장"},
{name : "베지타", phone : "010-4325-7458", position : "대표"}
]
//테이블 생성 함수 호출
buildTable(peopleArr);
/**
* for문으로 요소를 반복하여 테이블을 생성한다.
* @param {object} data peopleArr
* @returns string
* @example <tr><td>정문면</td><td>010-3002-7063</td><td>사원</td>
* @function buildTable 테이블을 생성하는 함수를 호출한다.
*/
function buildTable(data) {
/**@type {object} table html 객체 */
var table = document.getElementById('tablebody');
for (var i=0; i < data.length; i++){
/**@type {string} row 대체변수${변수명}으로 표시 */
var row = `<tr>
<td>${data[i].name}</td>
<td>${data[i].phone}</td>
<td>${data[i].position}</td>
</tr>`;
table.innerHTML += row;
}
}
// 정렬을 위해 sortType은 처음엔 asc 할당한다.
var sortType = 'asc';
/**
* @function sortContent(index) 테이블 헤더를 클릭하여 첫번째클릭은 내림차순 두번째클릭은 오름차순 순으로 정렬을 한다.
* @param {number} index html 파일 클릭이벤트에 인덱스 번호를 주었다.
* @returns boolean 또는 정렬을 한다.
* @method insertBefore() 참조된 노드 앞에 특정 부모 노드의 자식 노드를 삽입
*/
function sortContent(index) {
/**@type {object} table html 객체 */
var table = document.getElementsByTagName('table');
/**@type {boolean} sortType 삼항연산자 true면 'desc' false면 'asc'*/
sortType = (sortType =='asc') ? 'desc' : 'asc';
/**@type {boolean} checkSort*/
var checkSort = true;
/**
* @example 테이블 1번째 줄의 17개면 17 도출
* @type {object} rows html 객체
*/
var rows = table[0].rows;
while (checkSort) {
/**@type {boolean} checkSort에 미리 false 할당 */
checkSort = false;
/**
th태그는 빼고 정렬을 해야되기 때문에 (rows.length - 1);를 먼저 계산한다.
1,2번 한세트 2,3번 한세트 이런식으로 쭉 계산을 할 예정이다.
비교를 위해 대문자로 변경을 한다.
*/
for (var i = 1; i < (rows.length - 1); i++) {
var fCell = rows[i].cells[index].innerHTML.toUpperCase();
var sCell = rows[i + 1].cells[index].innerHTML.toUpperCase();
var row = rows[i];
// 오름차순 OR 내림차순 정렬하는 부분
if ( (sortType === 'asc' && fCell > sCell) || (sortType === 'desc' && fCell < sCell) ) {
//if문이 성립되면 해당 row을 기존 row 앞쪽으로 삽입한다.
row.parentNode.insertBefore(row.nextSibling, row);
//if문 성립이 안되면 true 반환한다.
checkSort = true;
}
}
}
}
/**
* 검색하려는 조건, 검색어를 매개변수로 받아서 사원 정보를 객체 배열로 반환
* @param {string} dropdownkey 검색 조건
* @param {string} keyword 검색어
* @returns Array[]
* @example memberSearch('name','길동'); = [ { name:"홍길동", position:"이사", phone:"010-2222-2141"} ]
* @method filter(필터객체,조건) 선택한 요소 중에서 전달받은 선택자에 해당하거나, 함수 호출의 결과가 참(true)인 요소를 모두 선택합니다.
* @method index(keyword) keyword와 맞는 인덱스를 반환한다.
*/
const memberSearch = (dropdownkey, keyword) => {
// 검색어가 존재하지 않을시에 빈 배열을 돌려준다.
if (!keyword) return [];
/**@type {object} member 배열 하나하나의 요소 */
return [...peopleArr].filter((member)=>{
// 멤버가 가진 정보가 number 타입일 경우 문자열로 변경
/**@type {string} memberInfo 속성에 대한 value 값 추출 */
let memberInfo = (typeof member[dropdownkey] === 'number') ? member[dropdownkey] + '' : member[dropdownkey];
// 소문자로 치환한 뒤 String.prototype.indexOf()로 존재하는지 검사 / 존재한다면 새 배열에 담는다.
return memberInfo.indexOf(keyword) > -1;
});
}
/**
* 테이블이 로우를 생성해주는 함수
* @type {function} updateRow
* @param {Array} members 사원정보 배열
* @returns 파라미터가 존재하지 않을 경우 빈 화면을 그려준다. / 반대로 존재할 경우 서치한 배열 정보를 테이블 로우로 생성
* @event table body 에 <td> node 생성
* @example updateRow(Members);
* @method appendChild() 접근한 요소 객체 뒤로 html 요소를 추가
*
*/
const updateRow = (members) => {
// 파라미터가 존재하지 않을시
if(members.length === 0) return document.getElementById('tablebody').innerHTML = '검색 결과가 없습니다.';
// 파라미터가 존재 할 시
// 테이블 row를 빈화면으로 초기화
document.getElementById('tablebody').innerHTML = '';
//forEach 메소드로 배열을 반복한다.
/**@type {object} item 서치한 사원 정보 배열 */
members.forEach((item)=>{
let table = document.getElementById('tablebody');
let tr = document.createElement('tr');
table.appendChild(tr);
// for ~ in 반복문을 통해 item 안의 property 즉 key를 반복한다.
/**@type {string} property item 배열 요소안의 object key값 */
for (const property in item) {
let td = document.createElement('td');
td.innerText = item[property];
tr.appendChild(td);
}
})
}
/**
* 선택한 직급이 담긴 배열을 매개변수로 받아서 배열 아이템에 일치하는 사원만 배열에 담아 반환
* @type {function} memberCheck
* @param {Array} positions 직급이 담긴 배열
* @returns Array[]
* @example 리턴값예시 [ { name:"지운학", position:"대표", phone:"010-4326-7658"} ]
*/
const memberCheck = (positions) => {
// Array.prototype.filter() 로 사원 정보를 순회하면서 맞는 직급만 담아서 반환하기
return [...peopleArr].filter((member)=>{
/**@type {boolean} result */
var result = false;
// 직급 배열 안에 각 사원에 직급이 존재하는지 검사
// 직급배열안에 직급이 존재하면 result=true 반환되어
// 필터메소드에 충족되고 필터된 배열을 반환한다.
positions.forEach((position)=>{
if (member.position === position) result = true;
});
return result;
})
}
/*
search eventlistener
검색 click을 통해 key, keyword 할당 후 updateRow(memberSearch(key,keyword)) 호출한다.
*/
document.getElementById('searchBtn').addEventListener('click',()=>{
/**@type {string} key 검색조건 */
let key = document.getElementById('searchSelect').value; // name, phone, position
/**@type {string} keyword 검색어 */
let keyword = document.getElementById('inputbody').value; // 검색어
updateRow(memberSearch(key,keyword));
})
/* filter eventlistener
체크박스 click을 통해 이벤트 발생하며, updateRow(memberCheck(array)) 호출한다.*/
document.querySelectorAll('input[name="checkbox"]').forEach((node)=>{
node.addEventListener('click',()=>{
/*querySelectorAll('input[name="checkbox"]') 선택기를 인자로 두어 NodeList를 반환한다.*/
/**
* @type {object} checkedList nodelist 반환
*/
let checkdList = document.querySelectorAll('input[name="checkbox"]:checked');
/** @type {object} array posotion(직급)을 담은 배열객체*/
let array = [];
checkdList.forEach((node)=>{
/* push() node의 value값을 하나씩 추가한다. */
array.push(node.value);
})
// array 길이가 0 이면 기존 테이블 정보가 나온다.
if (array.length === 0) updateRow(peopleArr);
// 길이가 0 이 아니면 체크된 배열 정보가 나온다.
else updateRow(memberCheck(array));
});
})
동적테이블 만들고 느낀점
1. 평소에 html 파일에 onclick 같은 이벤트를 많이 넣었는데 코드의 재활용이 힘들어 addEventListener() 메소드를 많이 사용하는것이 재활용 측면에서도 효율적으로 코딩을 할 수 있었다.
2. js코드를 src로 링크를 하여 따로 파일을 관리하는것이 유지 보수 측면에서 관리하기 좋았다.
정렬 addEventListener()메소드를 사용하여 만들어보기
정렬부분이 뭔가 아쉬워서 다시 만들어 보았다.
위에 html 파일을 적용하여 js파일을 바꿔주면 된다.
/**
*
* @param {string} key 검색 조건
* @param {string} order 정렬 조건 / asc(오름차순), desc(내림차순) / defalut parameter = asc
* @returns Array
* @example memberSort('name') = 이름, 오름차순 정렬
* memberSost('ph','desc') = 전화번호, 내림차순 정렬
*/
const memberSort = (key, order = 'asc') => {
// 원본 객체 배열의 복사본 생성
let copyMembers = [...peopleArr]
// 복사한 객체 배열 Array.prototype.sort() 를 사용해서 검색 조건에 맞는 값을 비교하여 정렬한다.
copyMembers.sort((a,b)=>{
// 검색 조건이 사원 정보에 존재하지 않을 경우 정렬을 하지 않는다
if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0;
// 검색 조건이 문자열인 경우 소문자로 변경
let keyA = (typeof a[key] === 'string') ? a[key].toLowerCase() : a[key];
let keyB = (typeof b[key] === 'string') ? b[key].toLowerCase() : b[key];
// 비교가능한 두 값을 비교해서 순서를 바꾼다.
let x = 0;
if(keyA > keyB) x = 1;
else if(keyA < keyB) x = -1;
// 내림차순일 경우 역순으로 진행
return (order === 'desc') ? (x * -1) : x
});
// 모든 값을 순회하여 정렬이 종료되면 객체 리턴
return copyMembers;
}
// 선택한 컬럼의 이름, 정렬정보를 가져온다
// 가져온 컬럼의 이름(key), 정렬정보(order)로 새 객체 배열을 만든다
// 새로 만든 객체 배열로 테이블의 row를 새로 생성한다.
document.getElementsByClassName('theader').addEventListener('click',(event)=>{
let key = event.target.innerText.toLowerCase();
let order = event.target.dataset.state;
if (order === 'asc'){
updateRow(memberSort(key));
event.target.dataset.state = 'desc'
}else{
updateRow(memberSort(key,'desc'));
event.target.dataset.state = 'asc'
}
});
'Web > javascript' 카테고리의 다른 글
함수(function)과 메서드(method) (0) | 2023.01.30 |
---|---|
filter 메소드 배열 필터하기 (0) | 2023.01.26 |
이벤트 등록 addEventListener() (0) | 2023.01.25 |
JSDoc 문법 정리 (0) | 2023.01.18 |
JSDoc 개념 (0) | 2023.01.18 |