JSFiddle - React, Tailwind, and code Playground

by zachpainter77

HTML

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://cdn.datatables.net/1.10.10/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.10/css/jquery.dataTables.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/3.2.0/css/fixedColumns.dataTables.min.css">
<table class="table table-striped table-bordered">
	<thead>
		<tr>
			<th>Select</th>
			<th>Full Name</th>
			<th>Position</th>
			<th>Office</th>
			<th>Start Date</th>
			<th>Salary</th>
		</tr>
	</thead>
	<tbody data-bind="dataTablesForEach : {
			data: employees,
			options: { 
				deferRender: true,
				paging: true,
				fixedColumns: {
					leftColumns: 2
				},
				scrollX: true
			}
		}">
		<tr>
			<td>
				<input data-bind="checked: selectedEmployees, checkedValue: name" type="checkbox" />
			</td>
			<td data-bind="text: name"></td>
			<td data-bind="text: position"></td>
			<td data-bind="text: office"></td>
			<td data-bind="text: startDate"></td>
			<td data-bind="text: salary"></td>
		</tr>
	</tbody>
</table>

CSS

/* Ensure that the demo table scrolls */

th,
td {
	white-space: nowrap;
}

div.dataTables_wrapper {
	width: 600px;
	margin: 0 auto;
}

JavaScript

ko.bindingHandlers.dataTablesForEach = {
	page: 0,
	init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
		var binding = ko.utils.unwrapObservable(valueAccessor());

		if (binding.options.paging) {
			binding.data.subscribe(function(changes) {
				var table = $(element).closest('table').DataTable();
				ko.bindingHandlers.dataTablesForEach.page = table.page();
				table.destroy();
			}, null, 'arrayChange');
		}

		var nodes = Array.prototype.slice.call(element.childNodes, 0);
		ko.utils.arrayForEach(nodes, function(node) {
			if (node && node.nodeType !== 1) {
				node.parentNode.removeChild(node);
			}
		});

		return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
	},
	update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
		var binding = ko.utils.unwrapObservable(valueAccessor()),
			key = 'DataTablesForEach_Initialized';

		var table;
		if (!binding.options.paging) {
			table = $(element).closest('table').DataTable();
			table.destroy();
		}

		ko.bindingHandlers.foreach.update(element, valueAccessor, allBindings, viewModel, bindingContext);

		table = $(element).closest('table').DataTable(binding.options);

		if (binding.options.paging) {
			if (table.page.info().pages - ko.bindingHandlers.dataTablesForEach.page === 0) {
				table.page(--ko.bindingHandlers.dataTablesForEach.page).draw(false);
			} else {
				table.page(ko.bindingHandlers.dataTablesForEach.page).draw(false);
			}
		}

		if (!ko.utils.domData.get(element, key) && (binding.data || binding.length)) {
			ko.utils.domData.set(element, key, true);
		}

		return {
			controlsDescendantBindings: true
		};
	}
};

var vm = function() {
	this.employees = ko.observableArray([{
		"name": "Airi Satou",
		"position": "Accountant",
		"office": "Tokyo",
		"startDate": "28th Nov 08",
		"salary": "$162,700 "
	}, {
		"name": "Angelica Ramos",
		"position": "Chief Executive Officer...