웹 개발에서 데이터를 표 형태로 사용자에게 보여주는 것은 가장 흔하면서도 중요한 작업 중 하나입니다. 단순한 정보 나열을 넘어 정렬, 검색, 페이지네이션 등 다채로운 상호작용을 제공해야 할 때, 많은 개발자들은 jQuery DataTables라는 강력한 라이브러리를 선택합니다. DataTables는 표준 HTML <table>
태그를 동적이고 기능이 풍부한 인터랙티브 테이블로 손쉽게 변환해주는 자바스크립트 플러그인입니다.
그러나 DataTables의 기본 기능만으로는 해결하기 어려운 실무적인 요구사항들이 존재합니다. 그 중 대표적인 것이 바로 '체크박스를 이용한 행 선택' 기능, 특히 '특정 조건을 만족하는 행만 선택할 수 있도록 제한'하는 기능입니다. 예를 들어, 사용자 목록 테이블에서 '활성(Active)' 상태인 사용자만 선택하여 일괄 작업을 처리하거나, 상품 목록에서 '재고 있음(In Stock)' 상태인 상품만 장바구니에 추가할 수 있도록 하는 시나리오가 이에 해당합니다.
이 글에서는 jQuery DataTables를 사용하여 조건부 체크박스 선택 기능을 구현하는 모든 과정을 깊이 있게 다룰 것입니다. 단순히 코드 조각을 나열하는 것을 넘어, 기본 개념부터 시작해 다양한 구현 방법의 장단점을 비교하고, 가장 까다로운 '전체 선택' 기능과의 연동, 그리고 실무에서 마주할 수 있는 여러 예외 상황에 대한 해결책까지 상세하게 제시합니다. 이 글을 끝까지 읽으시면, 어떤 복잡한 요구사항의 테이블이라도 자신 있게 구현할 수 있는 실력을 갖추게 될 것입니다.
1. jQuery DataTables의 기본: 시작이 반이다
본격적인 내용에 앞서, jQuery DataTables의 가장 기본적인 구조와 초기화 방법을 간단히 짚고 넘어가겠습니다. 이미 익숙하신 분들은 이 섹션을 건너뛰셔도 좋습니다. DataTables를 사용하기 위해서는 jQuery 라이브러리와 DataTables 라이브러리(CSS 및 JS 파일)를 먼저 HTML 문서에 포함해야 합니다.
필수 라이브러리 포함
기본 HTML 테이블 구조
DataTables는 표준 jQuery를 사용하여 위에서 정의한 테이블을 DataTables 객체로 초기화합니다. 가장 간단한 방법은 이제 기본적인 준비는 끝났습니다. 이 구조 위에 체크박스 기능을 층층이 쌓아 올리며 기능을 완성해 나가겠습니다.
물론 HTML과 jQuery만으로 체크박스 선택 기능을 직접 구현할 수도 있습니다. 하지만 DataTables는 이를 위해 매우 편리한 공식 확장 기능인 'Select'를 제공합니다. Select 확장을 사용하면 단 몇 줄의 코드로 세련된 행 선택 기능을 추가할 수 있으며, 키보드 탐색(Shift + 클릭, Ctrl/Cmd + 클릭) 등 복잡한 UX까지 자동으로 처리해 줍니다.
Select 확장을 사용하려면 관련 CSS 및 JS 파일을 추가로 로드해야 합니다. 이제 테이블 구조를 수정하여 첫 번째 열에 체크박스를 추가하고, DataTables 초기화 시 Select 관련 옵션을 설정해 보겠습니다.
첫 번째 열은 데이터 소스에 존재하지 않는 체크박스 열이므로,
위 코드의 핵심은
이 단계까지 완료하면, 테이블의 각 행 앞에 체크박스가 나타나고, 이를 클릭하면 해당 행이 파란색으로 하이라이트 되며 선택되는 것을 확인할 수 있습니다. 하지만 아직은 모든 행이 조건 없이 선택되는 상태입니다.
이제 이 글의 핵심 주제인 '조건부 선택'을 구현할 차례입니다. 목표는 '상태(status)' 값이 'Active'인 행만 선택 가능하도록 하고, 'Inactive'인 행은 선택할 수 없도록 비활성화하는 것입니다. 이를 구현하는 방법은 크게 두 가지 접근법이 있으며, 각각의 장단점을 살펴보겠습니다.
DataTables의
동작 원리: 각 행이 렌더링될 때, `rowCallback` 함수 내에서 해당 행의 'status' 데이터를 확인합니다. 만약 'status'가 'Inactive'라면, 해당 행의
기존 DataTables 초기화 코드에
`rowCallback` 방법의 장점:
`rowCallback` 방법의 단점:
이 방법은 Select 확장이 제공하는 기능을 조금 더 적극적으로 활용하는 방식입니다. `rowCallback`처럼 DOM을 직접 비활성화하는 대신, 선택 가능한 행과 불가능한 행을 클래스로 구분하고, Select 확장이 '선택 가능한 클래스를 가진 행'의 체크박스만 선택 대상으로 인지하도록 설정하는 것입니다.
동작 원리:
1. `rowCallback` 또는 `createdRow`를 사용해 조건(
이 방법은 `rowCallback`을 사용한다는 점은 동일하지만, DOM의 `disabled` 속성을 직접 건드리지 않고 CSS 클래스를 통해 선택 가능 여부를 '표시'만 한다는 점에서 차이가 있습니다. 하지만 실제로는 `disabled` 속성을 직접 부여하는 것이 Select 확장과의 충돌을 막고 사용자 경험(클릭 불가)을 명확하게 하는 데 더 효과적이므로, **대부분의 실무 상황에서는 방법 1이 더 선호되고 안정적입니다.** 따라서 이 글의 나머지 부분에서는 방법 1을 기준으로 심화 기능을 구현해 나가겠습니다.
체크박스 테이블 기능의 꽃은 바로 헤더에 위치한 '전체 선택' 체크박스입니다. 사용자는 이 체크박스 하나로 현재 페이지의 모든 항목을 선택하거나 해제할 수 있기를 기대합니다. 하지만 우리의 시나리오에는 '선택 불가능한 행'이 존재하기 때문에, 이 기능을 구현할 때 몇 가지 함정을 피해야 합니다.
'전체 선택' 기능의 요구사항:
1. '전체 선택' 체크박스를 클릭하면, 현재 페이지에서 '선택 가능한'(즉, 'Active' 상태인) 모든 행만 선택되어야 한다. 'Inactive' 행은 무시되어야 한다.
2. 모든 '선택 가능한' 행이 개별적으로 선택되면, '전체 선택' 체크박스는 자동으로 체크 상태가 되어야 한다.
3. 선택된 행 중 하나라도 선택 해제되면, '전체 선택' 체크박스는 자동으로 체크 해제 상태가 되어야 한다.
이 로직을 구현하기 위해 jQuery 이벤트 핸들러와 DataTables가 제공하는 API 및 이벤트를 함께 사용해야 합니다.
헤더의
코드 분석:
사용자가 개별 행들을 하나씩 클릭할 때 '전체 선택' 체크박스의 상태도 그에 맞게 업데이트되어야 합니다. 이를 위해 DataTables의
코드 분석:
지금까지 논의된 모든 개념(기본 초기화, Select 확장, 조건부 비활성화, 전체 선택 연동)을 하나로 합친 완전한 코드는 다음과 같습니다. 이 코드를 그대로 복사하여 실행해 보면 모든 기능이 정상적으로 동작하는 것을 확인할 수 있습니다.
지금까지 jQuery DataTables에서 특정 조건에 따라 행의 선택 가능 여부를 제어하는 방법을 심층적으로 알아보았습니다.
특히, 실무에서 반드시 필요한 '전체 선택' 체크박스 기능은 비활성화된 행을 고려하여 예외 처리를 해야 하므로 구현이 다소 복잡하지만, DataTables가 제공하는 풍부한 API(
jQuery DataTables는 이 외에도 서버사이드 프로세싱, 버튼(내보내기, 인쇄) 기능, 응답형 디자인 등 무수히 많은 기능을 제공하는 방대한 생태계를 가지고 있습니다. 오늘 배운 조건부 로직 처리 방식을 응용한다면, 앞으로 마주하게 될 그 어떤 복잡한 테이블 관련 요구사항도 해결할 수 있는 튼튼한 기반을 다진 것이라 확신합니다. DataTables 공식 문서를 항상 가까이 두고 다양한 예제와 API를 탐색해 보시길 권장합니다.
<table>
, <thead>
, 구조를 기반으로 동작합니다. 명확한 구조는 DataTables가 테이블을 올바르게 해석하는 데 도움을 줍니다.
<table id="userTable" class="display" style="width:100%">
<thead>
<tr>
<th>이름</th>
<th>직책</th>
<th>사무실</th>
<th>나이</th>
<th>상태</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
DataTables 초기화
$(selector).DataTable()
을 호출하는 것입니다.
$(document).ready(function() {
$('#userTable').DataTable({
// 여기에 다양한 옵션들을 추가할 수 있습니다.
});
});
2. 체크박스 행 선택의 구현: Select 확장 기능 활용
Select 확장 라이브러리 포함
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/select/1.3.1/css/select.dataTables.min.css">
<script type="text/javascript" src="https://cdn.datatables.net/select/1.3.1/js/dataTables.select.min.js"></script>
체크박스 열 추가 및 Select 초기화
HTML 구조 변경
<table id="userTable" class="display" style="width:100%">
<thead>
<tr>
<th><input type="checkbox" id="selectAll"></th>
<th>이름</th>
<th>직책</th>
<th>사무실</th>
<th>나이</th>
<th>상태</th>
</tr>
</thead>
</table>
DataTables 초기화 (JavaScript)
columns
와 columnDefs
옵션을 사용하여 동적으로 생성해 주어야 합니다.
$(document).ready(function() {
var userData = [
{ "name": "Tiger Nixon", "position": "System Architect", "office": "Edinburgh", "age": 61, "status": "Active" },
{ "name": "Garrett Winters", "position": "Accountant", "office": "Tokyo", "age": 63, "status": "Inactive" },
{ "name": "Ashton Cox", "position": "Junior Technical Author", "office": "San Francisco", "age": 66, "status": "Active" },
{ "name": "Cedric Kelly", "position": "Senior Javascript Developer", "office": "Edinburgh", "age": 22, "status": "Active" },
{ "name": "Airi Satou", "position": "Accountant", "office": "Tokyo", "age": 33, "status": "Inactive" }
// ... more data
];
var table = $('#userTable').DataTable({
data: userData,
columns: [
{ data: null, defaultContent: '' }, // 체크박스 열
{ data: 'name' },
{ data: 'position' },
{ data: 'office' },
{ data: 'age' },
{ data: 'status' }
],
// columnDefs: 첫 번째 열을 체크박스로 정의
columnDefs: [ {
orderable: false, // 이 열은 정렬 기능을 사용하지 않음
className: 'select-checkbox', // Select 확장이 인지할 수 있는 클래스
targets: 0, // 0번째 열
render: function (data, type, full, meta){
return '';
}
} ],
// select: Select 확장 기능 활성화
select: {
style: 'multi', // 'os', 'single', 'multi' 중 선택. 다중 선택 활성화
selector: 'td:first-child' // 클릭으로 선택을 트리거할 요소 (첫 번째 td)
},
order: [[1, 'asc']] // 기본 정렬 (이름 기준 오름차순)
});
});
columnDefs
와 select
옵션입니다.
columnDefs
: 특정 열(targets: 0
)에 대한 정의를 설정합니다.
orderable: false
: 체크박스 열은 정렬의 의미가 없으므로 정렬 기능을 비활성화합니다.className: 'select-checkbox'
: DataTables Select 확장이 이 클래스를 통해 해당 열을 체크박스 선택 열로 인지하고, 클릭 시 선택/해제 UI(파란색 배경 등)를 자동으로 처리해 줍니다. (render 함수로 input을 직접 생성하는 경우 필수 클래스는 아닐 수 있지만, 스타일링을 위해 유용합니다)render
: 셀 내부에 실제로 렌더링 될 HTML을 정의합니다. 여기서는 단순한 를 반환합니다.
select
: Select 확장 기능을 활성화하고 세부 동작을 정의합니다.
style: 'multi'
: 다중 행 선택을 허용합니다. 'single'
로 설정하면 한 번에 하나의 행만 선택됩니다.selector: 'td:first-child'
: 행 선택을 트리거하는 클릭 이벤트가 발생할 요소를 지정합니다. 여기서는 각 행의 첫 번째 (우리가 체크박스를 넣은 곳)를 지정했습니다. 이 덕분에 체크박스 자체뿐만 아니라 그 주변 영역을 클릭해도 행이 선택됩니다.
3. 조건부 선택 구현: 비활성화 로직 추가
방법 1: `rowCallback`을 이용한 DOM 직접 제어 (가장 직관적인 방법)
rowCallback
옵션은 테이블의 각 행()이 그려질 때마다 실행되는 콜백 함수를 정의할 수 있게 해줍니다. 이 콜백 함수는 생성된 행의 DOM 요소와 해당 행의 데이터를 인자로 받기 때문에, 데이터의 내용에 따라 DOM을 동적으로 조작하기에 매우 유용합니다.
요소에 특정 클래스(예: disabled-row
)를 추가하고, 내부에 있는 체크박스 요소를 찾아 disabled
속성을 부여합니다.
JavaScript 코드 수정
rowCallback
옵션을 추가합니다.
var table = $('#userTable').DataTable({
// ... 이전 옵션들 ...
// rowCallback 추가
rowCallback: function(row, data, index){
// 'data' 객체는 현재 행의 원본 데이터를 담고 있습니다.
// 예: { "name": "Garrett Winters", "status": "Inactive", ... }
if (data.status === 'Inactive') {
// 1. 행(tr)에 비활성화 클래스 추가
// - 시각적으로 비활성화되었음을 나타내기 위함 (CSS 필요)
$(row).addClass('disabled-row');
// 2. 행 내부의 체크박스를 찾아 비활성화(disabled) 처리
// - Select 확장이 이 체크박스를 선택 대상으로 삼지 않도록 함
// - 사용자도 직접 클릭할 수 없게 됨
$('input[type="checkbox"]', row).prop('disabled', true);
// 3. (선택적) Select 확장이 적용하는 선택 클래스를 강제로 제거
// - 다른 동작에 의해 의도치 않게 선택되는 것을 방지
$(row).removeClass('selected');
}
},
// ... select, order 등 나머지 옵션들 ...
});
CSS 스타일 추가
disabled-row
클래스가 적용된 행이 시각적으로 구분되도록 CSS를 추가합니다. 사용자가 비활성화된 상태임을 명확히 인지할 수 있도록 하는 것이 중요합니다.
/* 선택된 행의 기본 스타일 (DataTables Select 제공) */
tr.selected {
background-color: #B0BED9; /* 예시 색상 */
}
/* 비활성화된 행에 대한 스타일 */
tr.disabled-row {
color: #999; /* 텍스트 색상을 흐리게 */
background-color: #f5f5f5 !important; /* 배경색을 약간 회색으로, selected 스타일 덮어쓰기 */
}
/* 비활성화된 행에 속한 체크박스의 커서 모양 변경 */
tr.disabled-row td.select-checkbox {
cursor: not-allowed;
}
방법 2: Select 확장의 `select.selector`와 `className` 조합 (더 세련된 방법)
status === 'Active'
)을 만족하는 행의 체크박스(또는 )에만 특별한 클래스(예: 'selectable'
)를 부여합니다.
2. DataTables 초기화 시, `select.selector` 옵션에 바로 이 클래스를 지정합니다. (예: `td.selectable input[type="checkbox"]`).
3. 이렇게 하면 Select 확장은 `selectable` 클래스를 가진 `` 내부의 체크박스에서 발생하는 클릭 이벤트에만 반응하게 됩니다.
4. 심화 과정: '전체 선택' 체크박스와의 완벽한 연동
'전체 선택' 체크박스 클릭 이벤트 처리
#selectAll
체크박스에 대한 클릭 이벤트를 바인딩합니다.
// '전체 선택' 체크박스 클릭 이벤트
$('#selectAll').on('click', function(){
// 체크박스의 체크 상태 확인
var isChecked = this.checked;
// DataTables API를 사용하여 행들을 제어
// '.disabled-row' 클래스가 없는 행, 즉 선택 가능한 행만 대상으로 함
var rows = table.rows({ 'page': 'current' }).nodes().to$().not('.disabled-row');
if (isChecked) {
// 모든 선택 가능한 행을 선택
rows.find('input[type="checkbox"]').prop('checked', true);
table.rows(rows).select();
} else {
// 모든 선택 가능한 행을 해제
rows.find('input[type="checkbox"]').prop('checked', false);
table.rows(rows).deselect();
}
});
table.rows({ 'page': 'current' }).nodes()
: 현재 보이는 페이지의 모든 행() DOM 노드를 가져옵니다.
.to$()
: 가져온 DOM 노드들을 jQuery 객체로 변환하여 jQuery 메서드(like .not()
, .find()
)를 사용할 수 있게 합니다..not('.disabled-row')
: jQuery 셀렉터를 사용하여, disabled-row
클래스를 가지지 않은 행들만 필터링합니다. 이것이 바로 조건부 로직의 핵심입니다.table.rows(rows).select() / deselect()
: 필터링된 행들을 DataTables의 `select` 또는 `deselect` API를 통해 일괄적으로 선택/해제합니다. 단순히 체크박스의 `checked` 속성만 바꾸는 것이 아니라, DataTables가 선택 상태를 내부적으로 인지하도록 API를 호출하는 것이 중요합니다.개별 행 선택/해제 시 '전체 선택' 체크박스 상태 동기화
select
, deselect
이벤트를 활용하는 것이 가장 좋습니다. 이 이벤트들은 행이 선택되거나 해제될 때마다 트리거됩니다.
// 테이블의 select, deselect 이벤트 리스너
table.on('select deselect', function() {
// '전체 선택' 체크박스의 상태를 업데이트하는 함수 호출
updateSelectAllCheckbox();
});
function updateSelectAllCheckbox() {
// 1. 전체 선택 가능한 행의 수
var selectableRowsCount = table.rows({ 'page': 'current' }).nodes().to$().not('.disabled-row').length;
// 2. 현재 선택된 행의 수
var selectedRowsCount = table.rows({ selected: true, 'page': 'current' }).nodes().to$().not('.disabled-row').length;
var selectAllCheckbox = $('#selectAll');
// 선택 가능한 행이 하나도 없다면 체크박스 비활성화 및 체크 해제
if (selectableRowsCount === 0) {
selectAllCheckbox.prop('checked', false);
selectAllCheckbox.prop('disabled', true);
return; // 함수 종료
} else {
selectAllCheckbox.prop('disabled', false);
}
// 선택된 행의 수와 전체 선택 가능한 행의 수가 같다면 '전체 선택'을 체크
if (selectedRowsCount === selectableRowsCount) {
selectAllCheckbox.prop('checked', true);
} else {
// 그렇지 않다면 '전체 선택'을 체크 해제
selectAllCheckbox.prop('checked', false);
}
}
// DataTables 그리기가 완료될 때마다 (페이지 이동, 정렬, 검색 시)
// '전체 선택' 체크박스 상태를 재확인해야 함
table.on('draw.dt', function() {
updateSelectAllCheckbox();
});
table.on('select deselect', ...)
: 행이 선택되거나 해제될 때마다 `updateSelectAllCheckbox` 함수를 실행합니다.updateSelectAllCheckbox
함수 로직:
table.on('draw.dt', ...)
: 이 부분이 매우 중요합니다. 사용자가 페이지를 넘기거나, 정렬 순서를 바꾸거나, 검색을 수행하면 테이블 내용이 새로 그려집니다. 이때마다 '전체 선택' 체크박스의 상태도 현재 보이는 데이터를 기준으로 다시 계산되어야 하므로, draw.dt
이벤트 발생 시에도 상태 업데이트 함수를 호출해 주어야 합니다.5. 최종 완성: 통합 실전 예제 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>DataTables 조건부 체크박스 선택 예제</title>
<style>
body { font-family: sans-serif; }
.container { margin: 20px; }
tr.selected { background-color: #B0BED9 !important; /* !important to override other styles */ }
tr.disabled-row { color: #999; background-color: #f5f5f5 !important; }
tr.disabled-row td.select-checkbox,
tr.disabled-row input[type="checkbox"] {
cursor: not-allowed;
}
</style>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/select/1.3.1/css/select.dataTables.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/select/1.3.1/js/dataTables.select.min.js"></script>
</head>
<body>
<div class="container">
<h1>사용자 목록</h1>
<p>'Active' 상태인 사용자만 선택 가능합니다.</p>
<table id="userTable" class="display" style="width:100%">
<thead>
<tr>
<th></th>
<th>이름</th>
<th>직책</th>
<th>사무실</th>
<th>나이</th>
<th>상태</th>
</tr>
</thead>
</table>
<br>
<button id="showSelected">선택된 사용자 확인</button>
<div id="selectedData" style="margin-top: 10px; border: 1px solid #ccc; padding: 10px;"></div>
</div>
<script>
$(document).ready(function() {
var userData = [
{"name": "Tiger Nixon", "position": "System Architect", "office": "Edinburgh", "age": 61, "status": "Active"},
{"name": "Garrett Winters", "position": "Accountant", "office": "Tokyo", "age": 63, "status": "Inactive"},
{"name": "Ashton Cox", "position": "Junior Technical Author", "office": "San Francisco", "age": 66, "status": "Active"},
{"name": "Cedric Kelly", "position": "Senior Javascript Developer", "office": "Edinburgh", "age": 22, "status": "Active"},
{"name": "Airi Satou", "position": "Accountant", "office": "Tokyo", "age": 33, "status": "Inactive"},
{"name": "Brielle Williamson", "position": "Integration Specialist", "office": "New York", "age": 61, "status": "Active"},
{"name": "Herrod Chandler", "position": "Sales Assistant", "office": "San Francisco", "age": 59, "status": "Active"},
{"name": "Rhona Davidson", "position": "Integration Specialist", "office": "Tokyo", "age": 55, "status": "Inactive"},
{"name": "Colleen Hurst", "position": "Javascript Developer", "office": "San Francisco", "age": 39, "status": "Active"},
{"name": "Sonya Frost", "position": "Software Engineer", "office": "Edinburgh", "age": 23, "status": "Active"}
];
var table = $('#userTable').DataTable({
data: userData,
columns: [
{ data: null, defaultContent: '', orderable: false, className: 'select-checkbox' },
{ data: 'name' },
{ data: 'position' },
{ data: 'office' },
{ data: 'age' },
{ data: 'status' }
],
columnDefs: [ {
targets: 0,
render: function (data, type, full, meta){
// 체크박스를 렌더링. 비활성화 로직은 rowCallback에서 처리
return '<input type="checkbox">';
}
} ],
select: {
style: 'multi',
selector: 'td:first-child'
},
order: [[1, 'asc']],
rowCallback: function(row, data, index){
// 상태가 'Inactive'인 행을 비활성화
if (data.status === 'Inactive') {
$(row).addClass('disabled-row');
$('td:first-child input[type="checkbox"]', row).prop('disabled', true);
}
// 행이 선택되었는지 여부를 확인하고 체크박스 상태를 동기화
// (페이징, 정렬 후 상태 유지를 위해 필요)
var isSelected = $.inArray(data.DT_RowId, table.rows({ selected: true }).ids().toArray()) !== -1;
$('input[type="checkbox"]', row).prop('checked', isSelected);
},
// rowId를 지정해주어야 페이징/정렬 시에도 선택 상태가 올바르게 유지됩니다.
rowId: function(a) {
return 'row-' + a.name.replace(/\s/g, '-');
}
});
// '전체 선택' 체크박스 클릭 이벤트 핸들러
$('#selectAll').on('click', function(){
var isChecked = this.checked;
var rows = table.rows({ 'page': 'current' }).nodes().to$().not('.disabled-row');
if (isChecked) {
rows.find('input[type="checkbox"]').prop('checked', true);
table.rows(rows).select();
} else {
rows.find('input[type="checkbox"]').prop('checked', false);
table.rows(rows).deselect();
}
});
// 개별 행 선택/해제 시 체크박스 상태 동기화
table.on('select.dt deselect.dt', function(e, dt, type, indexes) {
if (type === 'row') {
// 이벤트가 발생한 행의 체크박스 상태만 업데이트
var rows = table.rows(indexes).nodes().to$();
rows.find('input[type="checkbox"]').prop('checked', table.rows(indexes).is$());
updateSelectAllCheckbox();
}
});
// '전체 선택' 체크박스 상태 업데이트 함수
function updateSelectAllCheckbox() {
var selectableRows = table.rows({ 'page': 'current' }).nodes().to$().not('.disabled-row');
var selectedRows = table.rows({ selected: true, 'page': 'current' }).nodes().to$().not('.disabled-row');
var selectAllCheckbox = $('#selectAll');
if (selectableRows.length === 0) {
selectAllCheckbox.prop('checked', false).prop('disabled', true);
} else {
selectAllCheckbox.prop('disabled', false);
selectAllCheckbox.prop('checked', selectableRows.length === selectedRows.length);
}
}
// 테이블이 다시 그려질 때(페이징, 검색 등) '전체 선택' 상태 업데이트
table.on('draw.dt', function() {
updateSelectAllCheckbox();
// 각 행의 체크박스 상태도 다시 동기화
table.rows({ 'page': 'current' }).every(function() {
var rowNode = this.node();
var isSelected = this.is$();
$(rowNode).find('input[type="checkbox"]').prop('checked', isSelected);
});
});
// 선택된 데이터 확인 버튼 클릭 이벤트
$('#showSelected').on('click', function() {
var selectedData = table.rows({ selected: true }).data().toArray();
var dataHtml = "선택된 사용자: " + selectedData.length + "명
";
if (selectedData.length > 0) {
dataHtml += "";
selectedData.forEach(function(user){
dataHtml += "
";
} else {
dataHtml += "선택된 사용자가 없습니다.";
}
$('#selectedData').html(dataHtml);
});
});
</script>
</body>
</html>
마무리하며
rowCallback
을 통해 데이터에 기반하여 DOM을 동적으로 제어하는 것이 핵심이었으며, 이를 공식 'Select' 확장 기능과 유기적으로 결합하여 강력하고 사용자 친화적인 인터페이스를 구축할 수 있었습니다.
rows()
, select()
, deselect()
)와 이벤트 시스템(select.dt
, deselect.dt
, draw.dt
)을 잘 활용하면 견고하고 논리적인 코드를 작성할 수 있습니다.