동적테이블 기능 목표

테이블은 이름, 번호 , 직급 컬럼으로 테이블을 생성

정렬 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

+ Recent posts