function sortableFilterable(tableElement) { if (tableElement.tHead && tableElement.tHead.rows.length > 0) { const headers = tableElement.tHead.rows.item(0).cells; for (const header of headers) { if (typeof header.dataset.sortType !== 'undefined' && ['auto', 'string', 'integer'].indexOf(header.dataset.sortType) >= 0) { header.addEventListener('click', () => { // Classes const reverseSort = header.classList.contains('sorted') && !header.classList.contains('sorted-reverse'); for (const h of headers) { h.classList.remove('sorted', 'sorted-reverse'); } header.classList.add('sorted'); if (reverseSort) { header.classList.add('sorted-reverse'); } // Find offset let cellIndex = 0; for (const h of headers) { if (h === header) break; cellIndex += h.colspan || 1; } // Sort each tbody for (const tbody of tableElement.tBodies) { const rows = Array.prototype.slice.call(tbody.rows); rows.sort((a, b) => { if (reverseSort) { const c = a; a = b; b = c; } if (a.cells.length <= cellIndex || b.cells.length <= cellIndex) { return 0; } const aCell = a.cells.item(cellIndex); const bCell = b.cells.item(cellIndex); const aValue = typeof aCell.dataset.sortValue !== 'undefined' ? aCell.dataset.sortValue : aCell.innerText; const bValue = typeof bCell.dataset.sortValue !== 'undefined' ? bCell.dataset.sortValue : bCell.innerText; let type = typeof header.dataset.sortType !== 'undefined' ? header.dataset.sortType : 'auto'; if (type === 'auto') { if(!isNaN(aValue) && !isNaN(bValue)) { type = 'integer'; } else { type = 'string'; } } switch (type) { case 'string': return aValue.localeCompare(bValue, undefined, {sensitivity: 'base', numeric: true}); case 'integer': return ((+aValue) - (+bValue)) || 0; default: return 0; } }); // Reorder the DOM elements for (const row of rows) { tbody.appendChild(row); } } }); header.style.cursor = 'pointer'; } } } const filterInput = document.createElement('input'); filterInput.type = 'search'; filterInput.addEventListener('input', () => { const terms = filterInput.value.toLowerCase().split(' '); for (const tbody of tableElement.tBodies) { for (const row of tbody.rows) { const rowContent = row.innerHTML.replace(/<[^>]+>/g, ' ').toLowerCase(); let shouldHide = true; for (const term of terms) { if (rowContent.indexOf(term) >= 0) { shouldHide = false; break; } } row.style.display = shouldHide ? 'none' : null; } } }); tableElement.parentElement.insertBefore(filterInput, tableElement); return filterInput; }