Interactive Markdown table filtering and sorting transforms static documentation into dynamic, user-friendly interfaces that enable real-time data exploration and content discovery. By implementing client-side filtering algorithms, sortable column headers, and advanced search functionality, technical teams can create engaging documentation experiences that scale efficiently with large datasets while maintaining the simplicity and portability of standard Markdown table syntax.

Why Implement Interactive Table Features?

Professional interactive table functionality provides essential benefits for modern documentation systems:

  • Enhanced User Experience: Enable users to quickly find relevant information in large datasets without manual scanning
  • Data Exploration: Provide tools for users to analyze and understand complex information through sorting and filtering
  • Improved Accessibility: Support keyboard navigation and screen reader compatibility for inclusive content access
  • Performance Optimization: Reduce cognitive load by showing only relevant information based on user-defined criteria
  • Scalable Documentation: Handle growing datasets without compromising usability or requiring manual content reorganization

Foundation Interactive Table Implementation

Basic Client-Side Filtering

Essential HTML and JavaScript setup for interactive Markdown tables:

<!-- Basic interactive table structure -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive Markdown Tables</title>
    <style>
        .table-container {
            max-width: 100%;
            overflow-x: auto;
            margin: 20px 0;
        }
        
        .interactive-table {
            width: 100%;
            border-collapse: collapse;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
        }
        
        .interactive-table th,
        .interactive-table td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #e1e5e9;
            vertical-align: top;
        }
        
        .interactive-table th {
            background-color: #f6f8fa;
            font-weight: 600;
            position: relative;
            cursor: pointer;
            user-select: none;
        }
        
        .interactive-table th:hover {
            background-color: #f1f3f4;
        }
        
        .sort-indicator {
            position: absolute;
            right: 8px;
            top: 50%;
            transform: translateY(-50%);
            opacity: 0.5;
            font-size: 12px;
        }
        
        .sort-indicator.active {
            opacity: 1;
        }
        
        .sort-asc::after {
            content: "▲";
        }
        
        .sort-desc::after {
            content: "▼";
        }
        
        .table-controls {
            margin-bottom: 15px;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            align-items: center;
        }
        
        .search-input {
            padding: 8px 12px;
            border: 1px solid #d0d7de;
            border-radius: 6px;
            font-size: 14px;
            min-width: 250px;
        }
        
        .filter-select {
            padding: 8px 12px;
            border: 1px solid #d0d7de;
            border-radius: 6px;
            font-size: 14px;
            background-color: white;
        }
        
        .clear-filters {
            padding: 8px 16px;
            background-color: #f6f8fa;
            border: 1px solid #d0d7de;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
        }
        
        .clear-filters:hover {
            background-color: #f1f3f4;
        }
        
        .row-hidden {
            display: none;
        }
        
        .no-results {
            text-align: center;
            padding: 40px;
            color: #656d76;
            font-style: italic;
        }
        
        .results-count {
            font-size: 14px;
            color: #656d76;
            margin-left: auto;
        }
    </style>
</head>
<body>
    <div class="table-container">
        <div class="table-controls">
            <input 
                type="text" 
                class="search-input" 
                placeholder="Search all columns..." 
                id="globalSearch"
            >
            <select class="filter-select" id="categoryFilter">
                <option value="">All Categories</option>
            </select>
            <select class="filter-select" id="statusFilter">
                <option value="">All Statuses</option>
            </select>
            <button class="clear-filters" id="clearFilters">Clear Filters</button>
            <span class="results-count" id="resultsCount"></span>
        </div>
        
        <table class="interactive-table" id="dataTable">
            <thead>
                <tr>
                    <th data-column="name" data-type="string">
                        Name
                        <span class="sort-indicator"></span>
                    </th>
                    <th data-column="category" data-type="string">
                        Category
                        <span class="sort-indicator"></span>
                    </th>
                    <th data-column="status" data-type="string">
                        Status
                        <span class="sort-indicator"></span>
                    </th>
                    <th data-column="priority" data-type="number">
                        Priority
                        <span class="sort-indicator"></span>
                    </th>
                    <th data-column="date" data-type="date">
                        Last Updated
                        <span class="sort-indicator"></span>
                    </th>
                    <th data-column="size" data-type="number">
                        Size (MB)
                        <span class="sort-indicator"></span>
                    </th>
                </tr>
            </thead>
            <tbody id="tableBody">
                <!-- Data rows will be populated by JavaScript -->
            </tbody>
        </table>
        
        <div class="no-results" id="noResults" style="display: none;">
            No results found matching your criteria.
        </div>
    </div>

    <script src="interactive-table.js"></script>
</body>
</html>

Comprehensive JavaScript Implementation

// interactive-table.js - Complete interactive table functionality
class InteractiveTable {
    constructor(tableId, options = {}) {
        this.table = document.getElementById(tableId);
        this.tbody = this.table.querySelector('tbody');
        this.thead = this.table.querySelector('thead');
        this.noResults = document.getElementById('noResults');
        this.resultsCount = document.getElementById('resultsCount');
        
        this.options = {
            enableSearch: true,
            enableSort: true,
            enableFilter: true,
            caseSensitive: false,
            highlightMatches: true,
            debounceDelay: 300,
            pageSize: 50,
            enablePagination: false,
            ...options
        };
        
        this.data = [];
        this.filteredData = [];
        this.currentSort = { column: null, direction: 'asc' };
        this.filters = new Map();
        this.searchTerm = '';
        this.currentPage = 1;
        
        this.init();
    }
    
    init() {
        this.setupEventListeners();
        this.populateFilterOptions();
        this.updateResultsCount();
    }
    
    setupEventListeners() {
        // Global search
        if (this.options.enableSearch) {
            const searchInput = document.getElementById('globalSearch');
            if (searchInput) {
                let searchTimeout;
                searchInput.addEventListener('input', (e) => {
                    clearTimeout(searchTimeout);
                    searchTimeout = setTimeout(() => {
                        this.handleGlobalSearch(e.target.value);
                    }, this.options.debounceDelay);
                });
            }
        }
        
        // Column sorting
        if (this.options.enableSort) {
            this.thead.addEventListener('click', (e) => {
                const header = e.target.closest('th[data-column]');
                if (header) {
                    this.handleSort(header.dataset.column, header.dataset.type);
                }
            });
        }
        
        // Filters
        if (this.options.enableFilter) {
            const categoryFilter = document.getElementById('categoryFilter');
            const statusFilter = document.getElementById('statusFilter');
            
            if (categoryFilter) {
                categoryFilter.addEventListener('change', (e) => {
                    this.setFilter('category', e.target.value);
                });
            }
            
            if (statusFilter) {
                statusFilter.addEventListener('change', (e) => {
                    this.setFilter('status', e.target.value);
                });
            }
        }
        
        // Clear filters
        const clearButton = document.getElementById('clearFilters');
        if (clearButton) {
            clearButton.addEventListener('click', () => {
                this.clearAllFilters();
            });
        }
        
        // Keyboard navigation
        this.table.addEventListener('keydown', (e) => {
            this.handleKeyboardNavigation(e);
        });
    }
    
    setData(data) {
        this.data = data;
        this.filteredData = [...data];
        this.renderTable();
        this.populateFilterOptions();
        this.updateResultsCount();
    }
    
    handleGlobalSearch(searchTerm) {
        this.searchTerm = searchTerm.toLowerCase().trim();
        this.applyFilters();
    }
    
    handleSort(column, type = 'string') {
        if (this.currentSort.column === column) {
            this.currentSort.direction = this.currentSort.direction === 'asc' ? 'desc' : 'asc';
        } else {
            this.currentSort.column = column;
            this.currentSort.direction = 'asc';
        }
        
        this.sortData(column, type, this.currentSort.direction);
        this.updateSortIndicators();
        this.renderTable();
    }
    
    sortData(column, type, direction) {
        this.filteredData.sort((a, b) => {
            let aVal = a[column];
            let bVal = b[column];
            
            // Handle different data types
            switch (type) {
                case 'number':
                    aVal = parseFloat(aVal) || 0;
                    bVal = parseFloat(bVal) || 0;
                    break;
                case 'date':
                    aVal = new Date(aVal);
                    bVal = new Date(bVal);
                    break;
                case 'string':
                default:
                    aVal = String(aVal).toLowerCase();
                    bVal = String(bVal).toLowerCase();
                    break;
            }
            
            let comparison = 0;
            if (aVal < bVal) comparison = -1;
            if (aVal > bVal) comparison = 1;
            
            return direction === 'desc' ? -comparison : comparison;
        });
    }
    
    setFilter(column, value) {
        if (value === '') {
            this.filters.delete(column);
        } else {
            this.filters.set(column, value);
        }
        this.applyFilters();
    }
    
    applyFilters() {
        this.filteredData = this.data.filter(row => {
            // Apply column filters
            for (const [column, filterValue] of this.filters) {
                const cellValue = String(row[column]).toLowerCase();
                const filter = filterValue.toLowerCase();
                
                if (!cellValue.includes(filter)) {
                    return false;
                }
            }
            
            // Apply global search
            if (this.searchTerm) {
                const searchableText = Object.values(row)
                    .join(' ')
                    .toLowerCase();
                
                if (!searchableText.includes(this.searchTerm)) {
                    return false;
                }
            }
            
            return true;
        });
        
        this.renderTable();
        this.updateResultsCount();
    }
    
    renderTable() {
        if (this.filteredData.length === 0) {
            this.tbody.innerHTML = '';
            this.table.style.display = 'none';
            this.noResults.style.display = 'block';
            return;
        }
        
        this.table.style.display = 'table';
        this.noResults.style.display = 'none';
        
        const startIndex = this.options.enablePagination 
            ? (this.currentPage - 1) * this.options.pageSize 
            : 0;
        const endIndex = this.options.enablePagination 
            ? startIndex + this.options.pageSize 
            : this.filteredData.length;
        
        const visibleData = this.filteredData.slice(startIndex, endIndex);
        
        this.tbody.innerHTML = visibleData.map(row => {
            const cells = Object.entries(row).map(([key, value]) => {
                let cellContent = this.formatCellValue(key, value);
                
                // Highlight search matches
                if (this.options.highlightMatches && this.searchTerm) {
                    cellContent = this.highlightSearchTerm(cellContent, this.searchTerm);
                }
                
                return `<td data-column="${key}">${cellContent}</td>`;
            }).join('');
            
            return `<tr>${cells}</tr>`;
        }).join('');
    }
    
    formatCellValue(column, value) {
        // Custom formatting based on column type
        switch (column) {
            case 'date':
                return new Date(value).toLocaleDateString();
            case 'size':
                return `${parseFloat(value).toFixed(1)} MB`;
            case 'priority':
                const priority = parseInt(value);
                const badges = {
                    1: '<span class="priority-badge high">High</span>',
                    2: '<span class="priority-badge medium">Medium</span>',
                    3: '<span class="priority-badge low">Low</span>'
                };
                return badges[priority] || value;
            case 'status':
                const statusClass = value.toLowerCase().replace(/\s+/g, '-');
                return `<span class="status-badge status-${statusClass}">${value}</span>`;
            default:
                return this.escapeHtml(String(value));
        }
    }
    
    highlightSearchTerm(text, searchTerm) {
        if (!searchTerm) return text;
        
        const regex = new RegExp(`(${this.escapeRegExp(searchTerm)})`, 'gi');
        return text.replace(regex, '<mark>$1</mark>');
    }
    
    updateSortIndicators() {
        // Clear all indicators
        this.thead.querySelectorAll('.sort-indicator').forEach(indicator => {
            indicator.className = 'sort-indicator';
        });
        
        // Set active indicator
        if (this.currentSort.column) {
            const header = this.thead.querySelector(`[data-column="${this.currentSort.column}"] .sort-indicator`);
            if (header) {
                header.className = `sort-indicator active sort-${this.currentSort.direction}`;
            }
        }
    }
    
    populateFilterOptions() {
        // Populate category filter
        const categoryFilter = document.getElementById('categoryFilter');
        if (categoryFilter) {
            const categories = [...new Set(this.data.map(row => row.category))].sort();
            categoryFilter.innerHTML = '<option value="">All Categories</option>' +
                categories.map(cat => `<option value="${cat}">${cat}</option>`).join('');
        }
        
        // Populate status filter
        const statusFilter = document.getElementById('statusFilter');
        if (statusFilter) {
            const statuses = [...new Set(this.data.map(row => row.status))].sort();
            statusFilter.innerHTML = '<option value="">All Statuses</option>' +
                statuses.map(status => `<option value="${status}">${status}</option>`).join('');
        }
    }
    
    updateResultsCount() {
        if (this.resultsCount) {
            const total = this.data.length;
            const filtered = this.filteredData.length;
            
            if (filtered === total) {
                this.resultsCount.textContent = `${total} items`;
            } else {
                this.resultsCount.textContent = `${filtered} of ${total} items`;
            }
        }
    }
    
    clearAllFilters() {
        this.filters.clear();
        this.searchTerm = '';
        
        // Reset UI controls
        document.getElementById('globalSearch').value = '';
        document.getElementById('categoryFilter').value = '';
        document.getElementById('statusFilter').value = '';
        
        this.filteredData = [...this.data];
        this.renderTable();
        this.updateResultsCount();
    }
    
    handleKeyboardNavigation(e) {
        const focusedRow = this.tbody.querySelector('tr:focus');
        if (!focusedRow) return;
        
        switch (e.key) {
            case 'ArrowDown':
                e.preventDefault();
                const nextRow = focusedRow.nextElementSibling;
                if (nextRow) nextRow.focus();
                break;
            case 'ArrowUp':
                e.preventDefault();
                const prevRow = focusedRow.previousElementSibling;
                if (prevRow) prevRow.focus();
                break;
            case 'Enter':
            case ' ':
                e.preventDefault();
                this.handleRowAction(focusedRow);
                break;
        }
    }
    
    handleRowAction(row) {
        // Custom action when row is selected
        row.classList.toggle('selected');
        console.log('Row action:', row);
    }
    
    // Utility functions
    escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
    
    escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }
    
    // Export functionality
    exportToCSV() {
        const headers = Object.keys(this.data[0] || {});
        const csvContent = [
            headers.join(','),
            ...this.filteredData.map(row => 
                headers.map(header => `"${String(row[header]).replace(/"/g, '""')}"`).join(',')
            )
        ].join('\n');
        
        const blob = new Blob([csvContent], { type: 'text/csv' });
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = 'table-data.csv';
        link.click();
        window.URL.revokeObjectURL(url);
    }
    
    // API for external control
    getFilteredData() {
        return this.filteredData;
    }
    
    getCurrentFilters() {
        return {
            search: this.searchTerm,
            filters: Object.fromEntries(this.filters),
            sort: this.currentSort
        };
    }
    
    restoreState(state) {
        if (state.search) {
            document.getElementById('globalSearch').value = state.search;
            this.searchTerm = state.search;
        }
        
        if (state.filters) {
            Object.entries(state.filters).forEach(([column, value]) => {
                this.setFilter(column, value);
                const select = document.getElementById(`${column}Filter`);
                if (select) select.value = value;
            });
        }
        
        if (state.sort && state.sort.column) {
            this.currentSort = state.sort;
            this.sortData(state.sort.column, 'string', state.sort.direction);
            this.updateSortIndicators();
        }
        
        this.renderTable();
        this.updateResultsCount();
    }
}

// Sample data structure
const sampleData = [
    {
        name: "User Authentication API",
        category: "Backend",
        status: "Active",
        priority: 1,
        date: "2025-10-01",
        size: 2.5
    },
    {
        name: "Frontend Dashboard",
        category: "Frontend",
        status: "In Development",
        priority: 2,
        date: "2025-09-28",
        size: 15.3
    },
    {
        name: "Database Migration Scripts",
        category: "Database",
        status: "Completed",
        priority: 3,
        date: "2025-09-25",
        size: 0.8
    },
    {
        name: "Payment Processing Service",
        category: "Backend",
        status: "Testing",
        priority: 1,
        date: "2025-10-03",
        size: 4.7
    },
    {
        name: "Mobile App Components",
        category: "Mobile",
        status: "Active",
        priority: 2,
        date: "2025-09-30",
        size: 8.2
    },
    {
        name: "Analytics Dashboard",
        category: "Frontend",
        status: "Planning",
        priority: 3,
        date: "2025-09-15",
        size: 12.1
    },
    {
        name: "Email Notification System",
        category: "Backend",
        status: "Active",
        priority: 2,
        date: "2025-09-22",
        size: 3.4
    },
    {
        name: "User Profile Management",
        category: "Frontend",
        status: "In Development",
        priority: 1,
        date: "2025-10-02",
        size: 6.9
    }
];

// Initialize the interactive table
document.addEventListener('DOMContentLoaded', function() {
    const interactiveTable = new InteractiveTable('dataTable', {
        enableSearch: true,
        enableSort: true,
        enableFilter: true,
        highlightMatches: true,
        debounceDelay: 250
    });
    
    interactiveTable.setData(sampleData);
    
    // Add export functionality
    const exportButton = document.createElement('button');
    exportButton.textContent = 'Export CSV';
    exportButton.className = 'clear-filters';
    exportButton.addEventListener('click', () => {
        interactiveTable.exportToCSV();
    });
    
    document.querySelector('.table-controls').appendChild(exportButton);
});

Advanced Filtering Techniques

Custom Filter Components

// advanced-filters.js - Sophisticated filtering system
class AdvancedFilterSystem {
    constructor(table, options = {}) {
        this.table = table;
        this.options = {
            enableRangeFilters: true,
            enableRegexSearch: false,
            enableMultiSelect: true,
            enableDateRanges: true,
            ...options
        };
        
        this.filterDefinitions = new Map();
        this.activeFilters = new Map();
        this.filterOperators = {
            'equals': (value, filter) => value === filter,
            'contains': (value, filter) => String(value).toLowerCase().includes(filter.toLowerCase()),
            'startsWith': (value, filter) => String(value).toLowerCase().startsWith(filter.toLowerCase()),
            'endsWith': (value, filter) => String(value).toLowerCase().endsWith(filter.toLowerCase()),
            'greaterThan': (value, filter) => parseFloat(value) > parseFloat(filter),
            'lessThan': (value, filter) => parseFloat(value) < parseFloat(filter),
            'between': (value, filter) => {
                const num = parseFloat(value);
                return num >= filter.min && num <= filter.max;
            },
            'dateAfter': (value, filter) => new Date(value) > new Date(filter),
            'dateBefore': (value, filter) => new Date(value) < new Date(filter),
            'dateRange': (value, filter) => {
                const date = new Date(value);
                return date >= new Date(filter.start) && date <= new Date(filter.end);
            },
            'regex': (value, filter) => {
                try {
                    return new RegExp(filter, 'i').test(String(value));
                } catch {
                    return false;
                }
            }
        };
        
        this.init();
    }
    
    init() {
        this.createAdvancedFilterUI();
        this.setupFilterEvents();
    }
    
    defineColumnFilter(column, filterConfig) {
        this.filterDefinitions.set(column, {
            type: filterConfig.type || 'text',
            operators: filterConfig.operators || ['contains'],
            options: filterConfig.options || [],
            label: filterConfig.label || column,
            ...filterConfig
        });
    }
    
    createAdvancedFilterUI() {
        const container = document.createElement('div');
        container.className = 'advanced-filters';
        container.innerHTML = `
            <div class="filter-header">
                <h3>Advanced Filters</h3>
                <button class="toggle-filters" id="toggleAdvanced">Show Filters</button>
            </div>
            <div class="filter-panel" id="filterPanel" style="display: none;">
                <div class="filter-controls" id="filterControls"></div>
                <div class="filter-actions">
                    <button class="apply-filters" id="applyFilters">Apply Filters</button>
                    <button class="clear-filters" id="clearAdvancedFilters">Clear All</button>
                    <button class="save-filter-set" id="saveFilterSet">Save Filter Set</button>
                </div>
            </div>
        `;
        
        // Insert before the table
        this.table.parentNode.insertBefore(container, this.table);
        
        this.setupAdvancedFilterEvents(container);
    }
    
    setupAdvancedFilterEvents(container) {
        const toggleButton = container.querySelector('#toggleAdvanced');
        const filterPanel = container.querySelector('#filterPanel');
        
        toggleButton.addEventListener('click', () => {
            const isVisible = filterPanel.style.display !== 'none';
            filterPanel.style.display = isVisible ? 'none' : 'block';
            toggleButton.textContent = isVisible ? 'Show Filters' : 'Hide Filters';
        });
        
        container.querySelector('#applyFilters').addEventListener('click', () => {
            this.applyAdvancedFilters();
        });
        
        container.querySelector('#clearAdvancedFilters').addEventListener('click', () => {
            this.clearAllAdvancedFilters();
        });
        
        container.querySelector('#saveFilterSet').addEventListener('click', () => {
            this.saveFilterSet();
        });
    }
    
    createFilterControl(column, config) {
        const controlContainer = document.createElement('div');
        controlContainer.className = 'filter-control';
        
        switch (config.type) {
            case 'text':
                controlContainer.innerHTML = this.createTextFilter(column, config);
                break;
            case 'select':
                controlContainer.innerHTML = this.createSelectFilter(column, config);
                break;
            case 'multiselect':
                controlContainer.innerHTML = this.createMultiSelectFilter(column, config);
                break;
            case 'range':
                controlContainer.innerHTML = this.createRangeFilter(column, config);
                break;
            case 'date':
                controlContainer.innerHTML = this.createDateFilter(column, config);
                break;
            case 'daterange':
                controlContainer.innerHTML = this.createDateRangeFilter(column, config);
                break;
        }
        
        return controlContainer;
    }
    
    createTextFilter(column, config) {
        return `
            <div class="filter-group">
                <label class="filter-label">${config.label}</label>
                <div class="text-filter-container">
                    <select class="filter-operator" data-column="${column}">
                        ${config.operators.map(op => 
                            `<option value="${op}">${this.getOperatorLabel(op)}</option>`
                        ).join('')}
                    </select>
                    <input type="text" 
                           class="filter-value" 
                           data-column="${column}" 
                           placeholder="Enter ${config.label.toLowerCase()}...">
                </div>
            </div>
        `;
    }
    
    createSelectFilter(column, config) {
        return `
            <div class="filter-group">
                <label class="filter-label">${config.label}</label>
                <select class="filter-select" data-column="${column}">
                    <option value="">All ${config.label}</option>
                    ${config.options.map(option => 
                        `<option value="${option.value || option}">${option.label || option}</option>`
                    ).join('')}
                </select>
            </div>
        `;
    }
    
    createMultiSelectFilter(column, config) {
        return `
            <div class="filter-group">
                <label class="filter-label">${config.label}</label>
                <div class="multiselect-container">
                    <button class="multiselect-toggle" data-column="${column}">
                        Select ${config.label}
                        <span class="multiselect-count"></span>
                    </button>
                    <div class="multiselect-dropdown" style="display: none;">
                        ${config.options.map(option => `
                            <label class="multiselect-option">
                                <input type="checkbox" 
                                       value="${option.value || option}" 
                                       data-column="${column}">
                                <span>${option.label || option}</span>
                            </label>
                        `).join('')}
                    </div>
                </div>
            </div>
        `;
    }
    
    createRangeFilter(column, config) {
        return `
            <div class="filter-group">
                <label class="filter-label">${config.label}</label>
                <div class="range-filter-container">
                    <input type="number" 
                           class="range-min" 
                           data-column="${column}" 
                           placeholder="Min ${config.label.toLowerCase()}"
                           min="${config.min || 0}"
                           max="${config.max || 100}">
                    <span class="range-separator">to</span>
                    <input type="number" 
                           class="range-max" 
                           data-column="${column}" 
                           placeholder="Max ${config.label.toLowerCase()}"
                           min="${config.min || 0}"
                           max="${config.max || 100}">
                </div>
            </div>
        `;
    }
    
    createDateRangeFilter(column, config) {
        return `
            <div class="filter-group">
                <label class="filter-label">${config.label}</label>
                <div class="date-range-container">
                    <input type="date" 
                           class="date-start" 
                           data-column="${column}">
                    <span class="date-separator">to</span>
                    <input type="date" 
                           class="date-end" 
                           data-column="${column}">
                </div>
            </div>
        `;
    }
    
    getOperatorLabel(operator) {
        const labels = {
            'equals': 'Equals',
            'contains': 'Contains',
            'startsWith': 'Starts With',
            'endsWith': 'Ends With',
            'greaterThan': 'Greater Than',
            'lessThan': 'Less Than',
            'regex': 'Regex Pattern'
        };
        return labels[operator] || operator;
    }
    
    applyAdvancedFilters() {
        this.activeFilters.clear();
        
        // Collect all filter values
        document.querySelectorAll('.filter-control').forEach(control => {
            const column = control.querySelector('[data-column]')?.dataset.column;
            if (!column) return;
            
            const config = this.filterDefinitions.get(column);
            if (!config) return;
            
            const filterValue = this.extractFilterValue(control, config);
            if (filterValue) {
                this.activeFilters.set(column, filterValue);
            }
        });
        
        // Apply filters to table
        this.filterTableData();
    }
    
    extractFilterValue(control, config) {
        switch (config.type) {
            case 'text':
                const operator = control.querySelector('.filter-operator').value;
                const value = control.querySelector('.filter-value').value.trim();
                return value ? { operator, value } : null;
                
            case 'select':
                const selectValue = control.querySelector('.filter-select').value;
                return selectValue ? { operator: 'equals', value: selectValue } : null;
                
            case 'multiselect':
                const checkedOptions = [...control.querySelectorAll('input[type="checkbox"]:checked')]
                    .map(cb => cb.value);
                return checkedOptions.length > 0 ? { operator: 'in', value: checkedOptions } : null;
                
            case 'range':
                const min = control.querySelector('.range-min').value;
                const max = control.querySelector('.range-max').value;
                if (min || max) {
                    return {
                        operator: 'between',
                        value: { min: min || -Infinity, max: max || Infinity }
                    };
                }
                return null;
                
            case 'daterange':
                const startDate = control.querySelector('.date-start').value;
                const endDate = control.querySelector('.date-end').value;
                if (startDate || endDate) {
                    return {
                        operator: 'dateRange',
                        value: { start: startDate, end: endDate }
                    };
                }
                return null;
        }
        
        return null;
    }
    
    filterTableData() {
        const rows = this.table.querySelectorAll('tbody tr');
        let visibleCount = 0;
        
        rows.forEach(row => {
            let shouldShow = true;
            
            for (const [column, filter] of this.activeFilters) {
                const cell = row.querySelector(`[data-column="${column}"]`);
                if (!cell) continue;
                
                const cellValue = cell.textContent.trim();
                const operator = this.filterOperators[filter.operator];
                
                if (operator && !operator(cellValue, filter.value)) {
                    shouldShow = false;
                    break;
                }
            }
            
            row.style.display = shouldShow ? '' : 'none';
            if (shouldShow) visibleCount++;
        });
        
        this.updateFilterResults(visibleCount, rows.length);
    }
    
    updateFilterResults(visible, total) {
        let resultInfo = document.querySelector('.filter-results-info');
        if (!resultInfo) {
            resultInfo = document.createElement('div');
            resultInfo.className = 'filter-results-info';
            this.table.parentNode.insertBefore(resultInfo, this.table);
        }
        
        if (visible === total) {
            resultInfo.textContent = `Showing all ${total} items`;
        } else {
            resultInfo.textContent = `Showing ${visible} of ${total} items`;
        }
    }
    
    clearAllAdvancedFilters() {
        this.activeFilters.clear();
        
        // Reset all filter controls
        document.querySelectorAll('.filter-control input').forEach(input => {
            if (input.type === 'checkbox') {
                input.checked = false;
            } else {
                input.value = '';
            }
        });
        
        document.querySelectorAll('.filter-control select').forEach(select => {
            select.selectedIndex = 0;
        });
        
        // Show all rows
        this.table.querySelectorAll('tbody tr').forEach(row => {
            row.style.display = '';
        });
        
        this.updateFilterResults(this.table.querySelectorAll('tbody tr').length, 
                                 this.table.querySelectorAll('tbody tr').length);
    }
    
    saveFilterSet() {
        const filterSet = {
            name: prompt('Enter name for this filter set:'),
            filters: Object.fromEntries(this.activeFilters),
            timestamp: new Date().toISOString()
        };
        
        if (filterSet.name) {
            const savedSets = JSON.parse(localStorage.getItem('tableFilterSets') || '[]');
            savedSets.push(filterSet);
            localStorage.setItem('tableFilterSets', JSON.stringify(savedSets));
            
            alert(`Filter set "${filterSet.name}" saved successfully!`);
        }
    }
    
    loadFilterSet(filterSet) {
        this.clearAllAdvancedFilters();
        
        // Apply saved filters
        Object.entries(filterSet.filters).forEach(([column, filter]) => {
            this.activeFilters.set(column, filter);
            // Update UI controls to reflect loaded filters
            this.updateFilterControlsFromState(column, filter);
        });
        
        this.filterTableData();
    }
    
    updateFilterControlsFromState(column, filter) {
        const control = document.querySelector(`.filter-control [data-column="${column}"]`);
        if (!control) return;
        
        const config = this.filterDefinitions.get(column);
        if (!config) return;
        
        // Update UI based on filter type and value
        // Implementation would vary based on filter type
        switch (config.type) {
            case 'text':
                control.closest('.filter-control').querySelector('.filter-operator').value = filter.operator;
                control.closest('.filter-control').querySelector('.filter-value').value = filter.value;
                break;
            case 'select':
                control.value = filter.value;
                break;
            // Add cases for other filter types...
        }
    }
}

// Enhanced CSS for advanced filters
const advancedFilterStyles = `
.advanced-filters {
    margin: 20px 0;
    border: 1px solid #e1e5e9;
    border-radius: 8px;
    background-color: #fafbfc;
}

.filter-header {
    padding: 15px 20px;
    border-bottom: 1px solid #e1e5e9;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.filter-header h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
}

.toggle-filters {
    padding: 6px 12px;
    background-color: #f6f8fa;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.filter-panel {
    padding: 20px;
}

.filter-controls {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 20px;
    margin-bottom: 20px;
}

.filter-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.filter-label {
    font-weight: 500;
    font-size: 14px;
    color: #24292f;
}

.text-filter-container {
    display: flex;
    gap: 8px;
}

.filter-operator {
    flex: 0 0 auto;
    width: 120px;
}

.filter-value {
    flex: 1;
}

.range-filter-container {
    display: flex;
    align-items: center;
    gap: 8px;
}

.range-separator,
.date-separator {
    color: #656d76;
    font-size: 14px;
}

.multiselect-container {
    position: relative;
}

.multiselect-toggle {
    width: 100%;
    padding: 8px 12px;
    background-color: white;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    text-align: left;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.multiselect-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background-color: white;
    border: 1px solid #d0d7de;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 10;
}

.multiselect-option {
    display: flex;
    align-items: center;
    padding: 8px 12px;
    cursor: pointer;
    gap: 8px;
}

.multiselect-option:hover {
    background-color: #f6f8fa;
}

.filter-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
}

.filter-actions button {
    padding: 8px 16px;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.apply-filters {
    background-color: #2da44e;
    color: white;
    border-color: #2da44e;
}

.apply-filters:hover {
    background-color: #2c974b;
}

.filter-results-info {
    margin: 10px 0;
    font-size: 14px;
    color: #656d76;
    text-align: right;
}

.priority-badge,
.status-badge {
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
}

.priority-badge.high {
    background-color: #ffeef0;
    color: #d1242f;
}

.priority-badge.medium {
    background-color: #fff8e1;
    color: #bf8700;
}

.priority-badge.low {
    background-color: #f0f9ff;
    color: #0969da;
}

.status-badge.active {
    background-color: #dcfdf7;
    color: #059669;
}

.status-badge.in-development {
    background-color: #fef3c7;
    color: #d97706;
}

.status-badge.completed {
    background-color: #dcfce7;
    color: #16a34a;
}

.status-badge.testing {
    background-color: #e0e7ff;
    color: #4338ca;
}

.status-badge.planning {
    background-color: #f3f4f6;
    color: #6b7280;
}
`;

// Inject advanced filter styles
const styleSheet = document.createElement('style');
styleSheet.textContent = advancedFilterStyles;
document.head.appendChild(styleSheet);

Server-Side Integration Patterns

API-Based Filtering and Sorting

// server-side-integration.js - Backend integration for large datasets
class ServerSideTableManager {
    constructor(apiEndpoint, options = {}) {
        this.apiEndpoint = apiEndpoint;
        this.options = {
            pageSize: 25,
            enableServerSort: true,
            enableServerFilter: true,
            cacheResults: true,
            debounceDelay: 500,
            ...options
        };
        
        this.currentPage = 1;
        this.totalPages = 1;
        this.totalItems = 0;
        this.currentFilters = {};
        this.currentSort = {};
        this.isLoading = false;
        this.cache = new Map();
        
        this.init();
    }
    
    init() {
        this.createServerTableUI();
        this.setupServerSideEvents();
        this.loadInitialData();
    }
    
    createServerTableUI() {
        const container = document.createElement('div');
        container.className = 'server-side-table-container';
        container.innerHTML = `
            <div class="server-table-controls">
                <div class="search-section">
                    <input type="text" 
                           id="serverSearch" 
                           class="server-search-input" 
                           placeholder="Search across all data...">
                    <button id="serverSearchBtn" class="search-button">Search</button>
                </div>
                
                <div class="filter-section">
                    <select id="serverCategoryFilter" class="server-filter-select">
                        <option value="">All Categories</option>
                    </select>
                    <select id="serverStatusFilter" class="server-filter-select">
                        <option value="">All Statuses</option>
                    </select>
                    <button id="clearServerFilters" class="clear-button">Clear Filters</button>
                </div>
                
                <div class="pagination-info">
                    <span id="serverPaginationInfo"></span>
                </div>
            </div>
            
            <div class="server-table-wrapper">
                <div class="loading-overlay" id="loadingOverlay" style="display: none;">
                    <div class="loading-spinner"></div>
                    <span>Loading data...</span>
                </div>
                
                <table class="server-side-table" id="serverTable">
                    <thead>
                        <tr>
                            <th data-column="name" class="sortable-header">
                                Name
                                <span class="sort-icon"></span>
                            </th>
                            <th data-column="category" class="sortable-header">
                                Category
                                <span class="sort-icon"></span>
                            </th>
                            <th data-column="status" class="sortable-header">
                                Status
                                <span class="sort-icon"></span>
                            </th>
                            <th data-column="priority" class="sortable-header">
                                Priority
                                <span class="sort-icon"></span>
                            </th>
                            <th data-column="date" class="sortable-header">
                                Date
                                <span class="sort-icon"></span>
                            </th>
                            <th data-column="size" class="sortable-header">
                                Size
                                <span class="sort-icon"></span>
                            </th>
                        </tr>
                    </thead>
                    <tbody id="serverTableBody">
                    </tbody>
                </table>
            </div>
            
            <div class="server-pagination" id="serverPagination">
                <button id="firstPage" class="page-button">First</button>
                <button id="prevPage" class="page-button">Previous</button>
                <div class="page-numbers" id="pageNumbers"></div>
                <button id="nextPage" class="page-button">Next</button>
                <button id="lastPage" class="page-button">Last</button>
            </div>
        `;
        
        document.body.appendChild(container);
    }
    
    setupServerSideEvents() {
        // Search functionality
        const searchInput = document.getElementById('serverSearch');
        const searchButton = document.getElementById('serverSearchBtn');
        
        let searchTimeout;
        const handleSearch = () => {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                this.performSearch(searchInput.value);
            }, this.options.debounceDelay);
        };
        
        searchInput.addEventListener('input', handleSearch);
        searchButton.addEventListener('click', () => {
            this.performSearch(searchInput.value);
        });
        
        searchInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                this.performSearch(searchInput.value);
            }
        });
        
        // Sorting functionality
        document.getElementById('serverTable').addEventListener('click', (e) => {
            const header = e.target.closest('.sortable-header');
            if (header) {
                this.handleServerSort(header.dataset.column);
            }
        });
        
        // Filtering functionality
        document.getElementById('serverCategoryFilter').addEventListener('change', (e) => {
            this.setServerFilter('category', e.target.value);
        });
        
        document.getElementById('serverStatusFilter').addEventListener('change', (e) => {
            this.setServerFilter('status', e.target.value);
        });
        
        document.getElementById('clearServerFilters').addEventListener('click', () => {
            this.clearAllServerFilters();
        });
        
        // Pagination functionality
        this.setupPaginationEvents();
    }
    
    setupPaginationEvents() {
        document.getElementById('firstPage').addEventListener('click', () => {
            this.goToPage(1);
        });
        
        document.getElementById('prevPage').addEventListener('click', () => {
            this.goToPage(Math.max(1, this.currentPage - 1));
        });
        
        document.getElementById('nextPage').addEventListener('click', () => {
            this.goToPage(Math.min(this.totalPages, this.currentPage + 1));
        });
        
        document.getElementById('lastPage').addEventListener('click', () => {
            this.goToPage(this.totalPages);
        });
    }
    
    async loadInitialData() {
        await this.fetchData();
        await this.loadFilterOptions();
    }
    
    async fetchData(resetPage = false) {
        if (resetPage) {
            this.currentPage = 1;
        }
        
        const params = new URLSearchParams({
            page: this.currentPage,
            pageSize: this.options.pageSize,
            ...this.currentFilters,
            ...this.currentSort
        });
        
        // Check cache first
        const cacheKey = params.toString();
        if (this.options.cacheResults && this.cache.has(cacheKey)) {
            const cachedData = this.cache.get(cacheKey);
            this.renderServerData(cachedData);
            return cachedData;
        }
        
        this.setLoading(true);
        
        try {
            const response = await fetch(`${this.apiEndpoint}?${params}`);
            
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            
            const data = await response.json();
            
            // Cache the response
            if (this.options.cacheResults) {
                this.cache.set(cacheKey, data);
                
                // Limit cache size
                if (this.cache.size > 50) {
                    const firstKey = this.cache.keys().next().value;
                    this.cache.delete(firstKey);
                }
            }
            
            this.renderServerData(data);
            return data;
            
        } catch (error) {
            console.error('Error fetching data:', error);
            this.handleError(error);
        } finally {
            this.setLoading(false);
        }
    }
    
    renderServerData(data) {
        const tbody = document.getElementById('serverTableBody');
        
        if (!data.items || data.items.length === 0) {
            tbody.innerHTML = `
                <tr>
                    <td colspan="6" class="no-data">
                        No data found matching your criteria.
                    </td>
                </tr>
            `;
            this.updatePagination(0, 0, 0);
            return;
        }
        
        tbody.innerHTML = data.items.map(item => `
            <tr>
                <td>${this.escapeHtml(item.name)}</td>
                <td>${this.escapeHtml(item.category)}</td>
                <td><span class="status-badge status-${item.status.toLowerCase().replace(/\s+/g, '-')}">${item.status}</span></td>
                <td><span class="priority-badge priority-${item.priority}">${this.formatPriority(item.priority)}</span></td>
                <td>${this.formatDate(item.date)}</td>
                <td>${item.size} MB</td>
            </tr>
        `).join('');
        
        this.totalPages = Math.ceil(data.total / this.options.pageSize);
        this.totalItems = data.total;
        
        this.updatePagination(data.total, this.currentPage, this.totalPages);
        this.updateSortIndicators();
    }
    
    updatePagination(total, currentPage, totalPages) {
        const paginationInfo = document.getElementById('serverPaginationInfo');
        const start = total === 0 ? 0 : (currentPage - 1) * this.options.pageSize + 1;
        const end = Math.min(currentPage * this.options.pageSize, total);
        
        paginationInfo.textContent = `Showing ${start}-${end} of ${total} results`;
        
        // Update pagination buttons
        document.getElementById('firstPage').disabled = currentPage === 1;
        document.getElementById('prevPage').disabled = currentPage === 1;
        document.getElementById('nextPage').disabled = currentPage === totalPages || totalPages === 0;
        document.getElementById('lastPage').disabled = currentPage === totalPages || totalPages === 0;
        
        // Update page numbers
        this.renderPageNumbers(currentPage, totalPages);
    }
    
    renderPageNumbers(currentPage, totalPages) {
        const pageNumbers = document.getElementById('pageNumbers');
        const maxVisible = 5;
        let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2));
        let endPage = Math.min(totalPages, startPage + maxVisible - 1);
        
        // Adjust start page if we're near the end
        if (endPage - startPage + 1 < maxVisible) {
            startPage = Math.max(1, endPage - maxVisible + 1);
        }
        
        let html = '';
        
        for (let i = startPage; i <= endPage; i++) {
            html += `
                <button class="page-number ${i === currentPage ? 'active' : ''}" 
                        data-page="${i}">${i}</button>
            `;
        }
        
        pageNumbers.innerHTML = html;
        
        // Add click events to page numbers
        pageNumbers.querySelectorAll('.page-number').forEach(button => {
            button.addEventListener('click', (e) => {
                const page = parseInt(e.target.dataset.page);
                this.goToPage(page);
            });
        });
    }
    
    async performSearch(searchTerm) {
        this.currentFilters.search = searchTerm.trim();
        await this.fetchData(true);
    }
    
    async handleServerSort(column) {
        if (this.currentSort.column === column) {
            this.currentSort.direction = this.currentSort.direction === 'asc' ? 'desc' : 'asc';
        } else {
            this.currentSort.column = column;
            this.currentSort.direction = 'asc';
        }
        
        await this.fetchData(true);
    }
    
    async setServerFilter(filterType, value) {
        if (value === '') {
            delete this.currentFilters[filterType];
        } else {
            this.currentFilters[filterType] = value;
        }
        
        await this.fetchData(true);
    }
    
    async clearAllServerFilters() {
        this.currentFilters = {};
        
        // Reset UI
        document.getElementById('serverSearch').value = '';
        document.getElementById('serverCategoryFilter').value = '';
        document.getElementById('serverStatusFilter').value = '';
        
        await this.fetchData(true);
    }
    
    async goToPage(page) {
        if (page !== this.currentPage && page >= 1 && page <= this.totalPages) {
            this.currentPage = page;
            await this.fetchData();
        }
    }
    
    updateSortIndicators() {
        // Clear all sort indicators
        document.querySelectorAll('.sort-icon').forEach(icon => {
            icon.className = 'sort-icon';
        });
        
        // Set active sort indicator
        if (this.currentSort.column) {
            const header = document.querySelector(`[data-column="${this.currentSort.column}"] .sort-icon`);
            if (header) {
                header.className = `sort-icon active ${this.currentSort.direction}`;
            }
        }
    }
    
    async loadFilterOptions() {
        try {
            const response = await fetch(`${this.apiEndpoint}/filters`);
            const filterData = await response.json();
            
            // Populate category filter
            const categoryFilter = document.getElementById('serverCategoryFilter');
            categoryFilter.innerHTML = '<option value="">All Categories</option>' +
                filterData.categories.map(cat => `<option value="${cat}">${cat}</option>`).join('');
            
            // Populate status filter
            const statusFilter = document.getElementById('serverStatusFilter');
            statusFilter.innerHTML = '<option value="">All Statuses</option>' +
                filterData.statuses.map(status => `<option value="${status}">${status}</option>`).join('');
                
        } catch (error) {
            console.warn('Could not load filter options:', error);
        }
    }
    
    setLoading(isLoading) {
        this.isLoading = isLoading;
        const overlay = document.getElementById('loadingOverlay');
        overlay.style.display = isLoading ? 'flex' : 'none';
        
        // Disable interactive elements while loading
        const controls = document.querySelectorAll('.server-table-controls input, .server-table-controls select, .server-table-controls button, .sortable-header');
        controls.forEach(control => {
            control.disabled = isLoading;
            if (isLoading) {
                control.style.pointerEvents = 'none';
            } else {
                control.style.pointerEvents = '';
            }
        });
    }
    
    handleError(error) {
        const tbody = document.getElementById('serverTableBody');
        tbody.innerHTML = `
            <tr>
                <td colspan="6" class="error-message">
                    <div class="error-content">
                        <strong>Error loading data</strong>
                        <p>${error.message}</p>
                        <button class="retry-button" onclick="this.closest('tr').style.display='none'; location.reload()">
                            Retry
                        </button>
                    </div>
                </td>
            </tr>
        `;
    }
    
    // Utility methods
    escapeHtml(text) {
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
    
    formatDate(dateString) {
        return new Date(dateString).toLocaleDateString();
    }
    
    formatPriority(priority) {
        const priorities = { 1: 'High', 2: 'Medium', 3: 'Low' };
        return priorities[priority] || priority;
    }
    
    // Public API methods
    getCurrentState() {
        return {
            page: this.currentPage,
            filters: { ...this.currentFilters },
            sort: { ...this.currentSort }
        };
    }
    
    restoreState(state) {
        this.currentPage = state.page || 1;
        this.currentFilters = state.filters || {};
        this.currentSort = state.sort || {};
        
        // Update UI to reflect state
        this.updateUIFromState();
        this.fetchData();
    }
    
    updateUIFromState() {
        if (this.currentFilters.search) {
            document.getElementById('serverSearch').value = this.currentFilters.search;
        }
        if (this.currentFilters.category) {
            document.getElementById('serverCategoryFilter').value = this.currentFilters.category;
        }
        if (this.currentFilters.status) {
            document.getElementById('serverStatusFilter').value = this.currentFilters.status;
        }
    }
    
    clearCache() {
        this.cache.clear();
    }
}

// Server-side styles
const serverSideStyles = `
.server-side-table-container {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    margin: 20px 0;
}

.server-table-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    align-items: center;
    margin-bottom: 20px;
    padding: 20px;
    background-color: #f6f8fa;
    border-radius: 8px;
}

.search-section {
    display: flex;
    gap: 8px;
    align-items: center;
}

.server-search-input {
    padding: 8px 12px;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    font-size: 14px;
    min-width: 300px;
}

.search-button,
.clear-button {
    padding: 8px 16px;
    background-color: #2da44e;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.clear-button {
    background-color: #f6f8fa;
    color: #24292f;
    border: 1px solid #d0d7de;
}

.search-button:hover {
    background-color: #2c974b;
}

.clear-button:hover {
    background-color: #f1f3f4;
}

.filter-section {
    display: flex;
    gap: 10px;
    align-items: center;
}

.server-filter-select {
    padding: 8px 12px;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    font-size: 14px;
    background-color: white;
}

.pagination-info {
    margin-left: auto;
    font-size: 14px;
    color: #656d76;
}

.server-table-wrapper {
    position: relative;
    overflow-x: auto;
    border: 1px solid #d0d7de;
    border-radius: 8px;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 255, 255, 0.9);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 10;
    gap: 15px;
}

.loading-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid #f3f4f6;
    border-top: 3px solid #2da44e;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

.server-side-table {
    width: 100%;
    border-collapse: collapse;
    background-color: white;
}

.server-side-table th,
.server-side-table td {
    padding: 12px;
    text-align: left;
    border-bottom: 1px solid #e1e5e9;
}

.server-side-table th {
    background-color: #f6f8fa;
    font-weight: 600;
    position: relative;
}

.sortable-header {
    cursor: pointer;
    user-select: none;
    padding-right: 30px;
}

.sortable-header:hover {
    background-color: #f1f3f4;
}

.sort-icon {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    opacity: 0.3;
    font-size: 12px;
}

.sort-icon.active {
    opacity: 1;
}

.sort-icon.asc::after {
    content: "▲";
}

.sort-icon.desc::after {
    content: "▼";
}

.sort-icon:not(.active)::after {
    content: "↕";
}

.server-pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
    margin-top: 20px;
    padding: 20px;
}

.page-button,
.page-number {
    padding: 8px 12px;
    border: 1px solid #d0d7de;
    background-color: white;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.page-button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.page-number.active {
    background-color: #2da44e;
    color: white;
    border-color: #2da44e;
}

.page-button:hover:not(:disabled),
.page-number:hover:not(.active) {
    background-color: #f6f8fa;
}

.no-data,
.error-message {
    text-align: center;
    padding: 40px;
    color: #656d76;
    font-style: italic;
}

.error-content {
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: center;
}

.retry-button {
    padding: 8px 16px;
    background-color: #2da44e;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
}
`;

// Inject server-side styles
const serverStyleSheet = document.createElement('style');
serverStyleSheet.textContent = serverSideStyles;
document.head.appendChild(serverStyleSheet);

Integration with Static Site Generators

Interactive table functionality integrates seamlessly with comprehensive documentation systems. When combined with navigation menu structures and site organization, dynamic filtering and sorting enable sophisticated content discovery experiences that scale effectively with growing documentation repositories.

For advanced content presentation, interactive tables work effectively with custom CSS styling frameworks to create branded documentation interfaces that maintain consistent visual identity while providing powerful data manipulation capabilities that enhance user engagement and information accessibility.

When building comprehensive content management systems, table interactivity complements performance optimization strategies by implementing efficient client-side processing that reduces server load while maintaining responsive user experiences across different device types and connection speeds.

Jekyll Integration Example

<!-- _includes/interactive-table.html - Jekyll template for interactive tables -->
<div class="interactive-table-container" data-table-config='{{ include.config | jsonify }}'>
    {% if include.search %}
    <div class="table-controls">
        <input type="text" class="table-search" placeholder="Search {{ include.title | default: 'table' }}...">
        {% if include.filters %}
        <div class="table-filters">
            {% for filter in include.filters %}
            <select class="table-filter" data-column="{{ filter.column }}">
                <option value="">All {{ filter.label }}</option>
                {% for option in filter.options %}
                <option value="{{ option.value | default: option }}">{{ option.label | default: option }}</option>
                {% endfor %}
            </select>
            {% endfor %}
        </div>
        {% endif %}
        <button class="clear-filters">Clear Filters</button>
    </div>
    {% endif %}
    
    <div class="table-wrapper">
        <table class="interactive-table">
            <thead>
                <tr>
                    {% for header in include.headers %}
                    <th data-column="{{ header.key }}" 
                        data-type="{{ header.type | default: 'string' }}" 
                        {% if header.sortable != false %}class="sortable"{% endif %}>
                        {{ header.label | default: header.key }}
                        {% if header.sortable != false %}
                        <span class="sort-indicator"></span>
                        {% endif %}
                    </th>
                    {% endfor %}
                </tr>
            </thead>
            <tbody>
                {% for row in include.data %}
                <tr>
                    {% for header in include.headers %}
                    <td data-column="{{ header.key }}">
                        {% assign cell_value = row[header.key] %}
                        {% case header.type %}
                        {% when 'date' %}
                            {{ cell_value | date: "%B %d, %Y" }}
                        {% when 'currency' %}
                            ${{ cell_value | round: 2 }}
                        {% when 'boolean' %}
                            {% if cell_value %}{% else %}{% endif %}
                        {% when 'link' %}
                            <a href="{{ cell_value.url }}" {% if cell_value.external %}target="_blank" rel="noopener"{% endif %}>
                                {{ cell_value.text | default: cell_value.url }}
                            </a>
                        {% when 'badge' %}
                            <span class="badge badge-{{ cell_value | slugify }}">{{ cell_value }}</span>
                        {% else %}
                            {{ cell_value }}
                        {% endcase %}
                    </td>
                    {% endfor %}
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
    
    {% if include.pagination %}
    <div class="table-pagination">
        <div class="pagination-info">
            <span class="results-count"></span>
        </div>
        <div class="pagination-controls">
            <button class="page-btn" data-page="first">First</button>
            <button class="page-btn" data-page="prev">Previous</button>
            <div class="page-numbers"></div>
            <button class="page-btn" data-page="next">Next</button>
            <button class="page-btn" data-page="last">Last</button>
        </div>
    </div>
    {% endif %}
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    // Initialize interactive tables
    document.querySelectorAll('.interactive-table-container').forEach(container => {
        const config = JSON.parse(container.dataset.tableConfig || '{}');
        new InteractiveTable(container.querySelector('.interactive-table'), config);
    });
});
</script>

Hugo Shortcode Implementation











<div class="hugo-interactive-table" id="_container">
    
    <div class="table-controls">
        <input type="text" 
               class="global-search" 
               placeholder="Search table..." 
               data-table="">
        
        
        <div class="filter-controls" id="_filters">
            <!-- Filters will be populated by JavaScript -->
        </div>
        
        
        <div class="table-info">
            <span class="row-count" id="_count"></span>
        </div>
    </div>
    
    
    <table class="interactive-data-table" id="">
        <thead>
            <tr>
                
                <th data-column="" 
                    class="sortable-column">
                    
                    
                    <span class="sort-icon" data-column=""></span>
                    
                </th>
                
            </tr>
        </thead>
        <tbody>
            
            <tr>
                
                <td data-column="">
                    
                        
                            <a href="" 
                               target="_blank" rel="noopener">
                                
                            </a>
                        
                            null
                        
                    
                        
                    
                        
                    
                </td>
                
            </tr>
            
        </tbody>
    </table>
</div>

<script>
(function() {
    const tableId = '';
    const container = document.getElementById(tableId + '_container');
    const table = document.getElementById(tableId);
    
    if (table && typeof InteractiveTable !== 'undefined') {
        new InteractiveTable(table, {
            enableSearch: ,
            enableSort: ,
            enableFilter: ,
            containerId: tableId + '_container'
        });
    }
})();
</script>

Performance Considerations and Optimization

Memory Management for Large Datasets

// performance-optimized-table.js - Memory-efficient table handling
class PerformanceOptimizedTable {
    constructor(tableElement, options = {}) {
        this.table = tableElement;
        this.options = {
            virtualScrolling: true,
            lazyLoading: true,
            maxVisibleRows: 100,
            bufferSize: 20,
            debounceDelay: 300,
            enableWorker: true,
            chunkSize: 1000,
            ...options
        };
        
        this.allData = [];
        this.filteredData = [];
        this.visibleData = [];
        this.viewportStart = 0;
        this.viewportEnd = 0;
        this.scrollContainer = null;
        this.worker = null;
        
        this.init();
    }
    
    init() {
        this.setupVirtualScrolling();
        this.initializeWorker();
        this.setupEventListeners();
    }
    
    setupVirtualScrolling() {
        if (!this.options.virtualScrolling) return;
        
        // Create virtual scrolling container
        this.scrollContainer = document.createElement('div');
        this.scrollContainer.className = 'virtual-scroll-container';
        this.scrollContainer.style.height = '400px';
        this.scrollContainer.style.overflowY = 'auto';
        
        // Create virtual content
        this.virtualContent = document.createElement('div');
        this.virtualContent.className = 'virtual-content';
        
        // Move table into virtual container
        this.table.parentNode.insertBefore(this.scrollContainer, this.table);
        this.virtualContent.appendChild(this.table);
        this.scrollContainer.appendChild(this.virtualContent);
        
        // Setup scroll event with throttling
        let scrollTimeout;
        this.scrollContainer.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = setTimeout(() => {
                this.handleVirtualScroll();
            }, 16); // ~60fps
        });
    }
    
    initializeWorker() {
        if (!this.options.enableWorker || !window.Worker) return;
        
        const workerScript = `
            self.onmessage = function(e) {
                const { type, data, options } = e.data;
                
                switch (type) {
                    case 'FILTER_DATA':
                        const filtered = filterData(data.items, data.filters, options);
                        self.postMessage({ type: 'FILTER_RESULT', data: filtered });
                        break;
                        
                    case 'SORT_DATA':
                        const sorted = sortData(data.items, data.column, data.direction, data.type);
                        self.postMessage({ type: 'SORT_RESULT', data: sorted });
                        break;
                        
                    case 'SEARCH_DATA':
                        const searchResult = searchData(data.items, data.query, options);
                        self.postMessage({ type: 'SEARCH_RESULT', data: searchResult });
                        break;
                }
            };
            
            function filterData(items, filters, options) {
                return items.filter(item => {
                    for (const [column, filter] of Object.entries(filters)) {
                        const value = String(item[column] || '').toLowerCase();
                        const filterValue = String(filter).toLowerCase();
                        
                        if (!value.includes(filterValue)) {
                            return false;
                        }
                    }
                    return true;
                });
            }
            
            function sortData(items, column, direction, type) {
                return items.sort((a, b) => {
                    let aVal = a[column];
                    let bVal = b[column];
                    
                    switch (type) {
                        case 'number':
                            aVal = parseFloat(aVal) || 0;
                            bVal = parseFloat(bVal) || 0;
                            break;
                        case 'date':
                            aVal = new Date(aVal);
                            bVal = new Date(bVal);
                            break;
                        default:
                            aVal = String(aVal).toLowerCase();
                            bVal = String(bVal).toLowerCase();
                    }
                    
                    let comparison = 0;
                    if (aVal < bVal) comparison = -1;
                    if (aVal > bVal) comparison = 1;
                    
                    return direction === 'desc' ? -comparison : comparison;
                });
            }
            
            function searchData(items, query, options) {
                const searchTerm = query.toLowerCase();
                return items.filter(item => {
                    const searchableText = Object.values(item).join(' ').toLowerCase();
                    return searchableText.includes(searchTerm);
                });
            }
        `;
        
        const blob = new Blob([workerScript], { type: 'application/javascript' });
        this.worker = new Worker(URL.createObjectURL(blob));
        
        this.worker.onmessage = (e) => {
            this.handleWorkerMessage(e.data);
        };
    }
    
    setData(data) {
        this.allData = data;
        this.filteredData = [...data];
        this.updateVirtualScroll();
    }
    
    handleVirtualScroll() {
        const scrollTop = this.scrollContainer.scrollTop;
        const containerHeight = this.scrollContainer.clientHeight;
        const rowHeight = 40; // Estimated row height
        
        // Calculate visible range
        const startIndex = Math.floor(scrollTop / rowHeight);
        const endIndex = Math.min(
            startIndex + Math.ceil(containerHeight / rowHeight) + this.options.bufferSize,
            this.filteredData.length
        );
        
        this.viewportStart = Math.max(0, startIndex - this.options.bufferSize);
        this.viewportEnd = endIndex;
        
        this.renderVisibleRows();
        this.updateScrollerHeight();
    }
    
    renderVisibleRows() {
        const tbody = this.table.querySelector('tbody');
        const visibleRows = this.filteredData.slice(this.viewportStart, this.viewportEnd);
        
        // Create row HTML
        const rowsHTML = visibleRows.map((row, index) => {
            const actualIndex = this.viewportStart + index;
            const cells = Object.entries(row).map(([key, value]) => 
                `<td data-column="${key}">${this.formatCellValue(key, value)}</td>`
            ).join('');
            
            return `<tr data-index="${actualIndex}" style="transform: translateY(${actualIndex * 40}px)">${cells}</tr>`;
        }).join('');
        
        tbody.innerHTML = rowsHTML;
    }
    
    updateScrollerHeight() {
        const totalHeight = this.filteredData.length * 40; // Estimated total height
        this.virtualContent.style.height = `${totalHeight}px`;
        this.table.style.position = 'relative';
    }
    
    // Chunked data processing for large datasets
    async processLargeDataset(data, operation) {
        const chunks = [];
        const chunkSize = this.options.chunkSize;
        
        for (let i = 0; i < data.length; i += chunkSize) {
            chunks.push(data.slice(i, i + chunkSize));
        }
        
        const results = [];
        for (const chunk of chunks) {
            const result = await this.processChunk(chunk, operation);
            results.push(...result);
            
            // Yield control to prevent UI blocking
            await new Promise(resolve => setTimeout(resolve, 0));
        }
        
        return results;
    }
    
    async processChunk(chunk, operation) {
        return new Promise((resolve) => {
            if (this.worker) {
                // Use web worker for processing
                this.worker.postMessage({
                    type: operation.type,
                    data: { items: chunk, ...operation.data },
                    options: operation.options || {}
                });
                
                // Worker will handle the response
                resolve([]);
            } else {
                // Fallback to main thread processing
                const result = this.processChunkSync(chunk, operation);
                resolve(result);
            }
        });
    }
    
    processChunkSync(chunk, operation) {
        switch (operation.type) {
            case 'FILTER_DATA':
                return chunk.filter(item => {
                    for (const [column, filter] of Object.entries(operation.data.filters)) {
                        const value = String(item[column] || '').toLowerCase();
                        const filterValue = String(filter).toLowerCase();
                        
                        if (!value.includes(filterValue)) {
                            return false;
                        }
                    }
                    return true;
                });
                
            case 'SEARCH_DATA':
                const searchTerm = operation.data.query.toLowerCase();
                return chunk.filter(item => {
                    const searchableText = Object.values(item).join(' ').toLowerCase();
                    return searchableText.includes(searchTerm);
                });
                
            default:
                return chunk;
        }
    }
    
    handleWorkerMessage(message) {
        switch (message.type) {
            case 'FILTER_RESULT':
            case 'SORT_RESULT':
            case 'SEARCH_RESULT':
                this.filteredData = message.data;
                this.updateVirtualScroll();
                break;
        }
    }
    
    async performSearch(query) {
        if (this.allData.length > this.options.chunkSize) {
            this.filteredData = await this.processLargeDataset(this.allData, {
                type: 'SEARCH_DATA',
                data: { query }
            });
        } else {
            if (this.worker) {
                this.worker.postMessage({
                    type: 'SEARCH_DATA',
                    data: { items: this.allData, query }
                });
            } else {
                this.filteredData = this.processChunkSync(this.allData, {
                    type: 'SEARCH_DATA',
                    data: { query }
                });
            }
        }
        
        this.updateVirtualScroll();
    }
    
    updateVirtualScroll() {
        if (this.options.virtualScrolling) {
            this.handleVirtualScroll();
        } else {
            this.renderAllRows();
        }
    }
    
    renderAllRows() {
        const tbody = this.table.querySelector('tbody');
        const rowsHTML = this.filteredData.map((row, index) => {
            const cells = Object.entries(row).map(([key, value]) => 
                `<td data-column="${key}">${this.formatCellValue(key, value)}</td>`
            ).join('');
            
            return `<tr data-index="${index}">${cells}</tr>`;
        }).join('');
        
        tbody.innerHTML = rowsHTML;
    }
    
    formatCellValue(column, value) {
        // Implement cell formatting logic
        return String(value);
    }
    
    // Memory management
    destroy() {
        if (this.worker) {
            this.worker.terminate();
        }
        
        // Clear data references
        this.allData = null;
        this.filteredData = null;
        this.visibleData = null;
        
        // Remove event listeners
        if (this.scrollContainer) {
            this.scrollContainer.removeEventListener('scroll', this.handleVirtualScroll);
        }
    }
    
    // Performance monitoring
    getPerformanceMetrics() {
        return {
            totalRows: this.allData?.length || 0,
            filteredRows: this.filteredData?.length || 0,
            visibleRows: this.viewportEnd - this.viewportStart,
            memoryUsage: this.estimateMemoryUsage(),
            isUsingWorker: !!this.worker,
            isVirtualScrolling: this.options.virtualScrolling
        };
    }
    
    estimateMemoryUsage() {
        // Rough estimation of memory usage
        const dataSize = JSON.stringify(this.allData || []).length;
        const filteredSize = JSON.stringify(this.filteredData || []).length;
        
        return {
            totalData: `${Math.round(dataSize / 1024)}KB`,
            filteredData: `${Math.round(filteredSize / 1024)}KB`,
            estimated: true
        };
    }
}

// Performance monitoring utility
class TablePerformanceMonitor {
    constructor() {
        this.metrics = {
            renderTimes: [],
            searchTimes: [],
            filterTimes: [],
            sortTimes: []
        };
    }
    
    startMeasurement(operation) {
        return {
            operation,
            startTime: performance.now(),
            startMemory: performance.memory ? performance.memory.usedJSHeapSize : 0
        };
    }
    
    endMeasurement(measurement) {
        const endTime = performance.now();
        const endMemory = performance.memory ? performance.memory.usedJSHeapSize : 0;
        
        const result = {
            operation: measurement.operation,
            duration: endTime - measurement.startTime,
            memoryDelta: endMemory - measurement.startMemory,
            timestamp: Date.now()
        };
        
        this.metrics[measurement.operation + 'Times'].push(result);
        
        // Keep only last 100 measurements
        if (this.metrics[measurement.operation + 'Times'].length > 100) {
            this.metrics[measurement.operation + 'Times'].shift();
        }
        
        return result;
    }
    
    getAveragePerformance(operation) {
        const times = this.metrics[operation + 'Times'] || [];
        if (times.length === 0) return null;
        
        const avgDuration = times.reduce((sum, t) => sum + t.duration, 0) / times.length;
        const avgMemory = times.reduce((sum, t) => sum + Math.abs(t.memoryDelta), 0) / times.length;
        
        return {
            operation,
            averageDuration: Math.round(avgDuration * 100) / 100,
            averageMemoryDelta: Math.round(avgMemory / 1024), // KB
            sampleCount: times.length
        };
    }
    
    generatePerformanceReport() {
        const operations = ['render', 'search', 'filter', 'sort'];
        const report = {};
        
        operations.forEach(op => {
            report[op] = this.getAveragePerformance(op);
        });
        
        return report;
    }
}

Troubleshooting Common Implementation Issues

Browser Compatibility Problems

Problem: Interactive features not working in older browsers

Solutions:

// browser-compatibility.js - Cross-browser compatibility layer
class CompatibilityLayer {
    constructor() {
        this.init();
    }
    
    init() {
        this.polyfillIntersectionObserver();
        this.polyfillCustomEvents();
        this.polyfillArrayMethods();
        this.handleIECompatibility();
    }
    
    polyfillIntersectionObserver() {
        if (!window.IntersectionObserver) {
            // Fallback for lazy loading
            window.IntersectionObserver = function(callback, options) {
                this.observe = function(element) {
                    // Simple visibility check fallback
                    const checkVisibility = () => {
                        const rect = element.getBoundingClientRect();
                        const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
                        
                        if (isVisible) {
                            callback([{
                                target: element,
                                isIntersecting: true
                            }]);
                        }
                    };
                    
                    // Check on scroll
                    window.addEventListener('scroll', checkVisibility);
                    checkVisibility(); // Initial check
                };
                
                this.disconnect = function() {
                    // Cleanup would go here
                };
            };
        }
    }
    
    polyfillCustomEvents() {
        if (!window.CustomEvent) {
            function CustomEvent(event, params) {
                params = params || { bubbles: false, cancelable: false, detail: null };
                const evt = document.createEvent('CustomEvent');
                evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
                return evt;
            }
            
            CustomEvent.prototype = window.Event.prototype;
            window.CustomEvent = CustomEvent;
        }
    }
    
    polyfillArrayMethods() {
        // Array.from polyfill
        if (!Array.from) {
            Array.from = function(arrayLike, mapFn, thisArg) {
                const items = Object(arrayLike);
                const len = parseInt(items.length) || 0;
                const result = new Array(len);
                
                for (let i = 0; i < len; i++) {
                    if (i in items) {
                        if (mapFn) {
                            result[i] = mapFn.call(thisArg, items[i], i);
                        } else {
                            result[i] = items[i];
                        }
                    }
                }
                
                return result;
            };
        }
        
        // Array.includes polyfill
        if (!Array.prototype.includes) {
            Array.prototype.includes = function(searchElement) {
                return this.indexOf(searchElement) !== -1;
            };
        }
    }
    
    handleIECompatibility() {
        // Check if IE
        const isIE = navigator.userAgent.indexOf('MSIE') !== -1 || 
                   navigator.userAgent.indexOf('Trident/') !== -1;
        
        if (isIE) {
            // Add IE-specific handling
            document.documentElement.className += ' ie-browser';
            
            // Object.assign polyfill
            if (!Object.assign) {
                Object.assign = function(target) {
                    for (let i = 1; i < arguments.length; i++) {
                        const source = arguments[i];
                        for (const key in source) {
                            if (Object.prototype.hasOwnProperty.call(source, key)) {
                                target[key] = source[key];
                            }
                        }
                    }
                    return target;
                };
            }
        }
    }
    
    // Fallback for modern features
    createCompatibleEventListener(element, event, handler, options) {
        if (element.addEventListener) {
            element.addEventListener(event, handler, options);
        } else if (element.attachEvent) {
            element.attachEvent('on' + event, handler);
        }
    }
    
    createCompatibleQuerySelector(selector) {
        if (document.querySelectorAll) {
            return document.querySelectorAll(selector);
        } else {
            // Fallback for very old browsers
            const elements = [];
            const allElements = document.getElementsByTagName('*');
            
            for (let i = 0; i < allElements.length; i++) {
                // Simple class selector fallback
                if (selector.startsWith('.')) {
                    const className = selector.slice(1);
                    if (allElements[i].className.indexOf(className) !== -1) {
                        elements.push(allElements[i]);
                    }
                }
            }
            
            return elements;
        }
    }
    
    getCompatibleStyles(element) {
        if (window.getComputedStyle) {
            return window.getComputedStyle(element);
        } else if (element.currentStyle) {
            return element.currentStyle;
        }
        return {};
    }
}

// Initialize compatibility layer
new CompatibilityLayer();

Performance Issues with Large Tables

Problem: Browser becomes unresponsive with large datasets

Solutions:

// performance-solutions.js - Solutions for large table performance
class LargeTableOptimizer {
    constructor(table, options = {}) {
        this.table = table;
        this.options = {
            maxRowsBeforeVirtualization: 1000,
            useWebWorkers: true,
            enableProgressiveLoading: true,
            chunkSize: 100,
            ...options
        };
        
        this.init();
    }
    
    init() {
        this.determineOptimizationStrategy();
    }
    
    determineOptimizationStrategy() {
        const rowCount = this.table.querySelectorAll('tbody tr').length;
        
        if (rowCount > this.options.maxRowsBeforeVirtualization) {
            this.implementVirtualization();
        } else if (rowCount > 500) {
            this.implementProgressiveLoading();
        } else {
            this.implementBasicOptimizations();
        }
    }
    
    implementVirtualization() {
        console.log('Implementing virtual scrolling for large dataset');
        
        // Convert table to virtual scrolling
        const virtualContainer = document.createElement('div');
        virtualContainer.className = 'virtual-table-container';
        virtualContainer.style.height = '500px';
        virtualContainer.style.overflow = 'auto';
        
        this.table.parentNode.insertBefore(virtualContainer, this.table);
        virtualContainer.appendChild(this.table);
        
        // Implement virtual scrolling logic
        this.setupVirtualScrolling(virtualContainer);
    }
    
    implementProgressiveLoading() {
        console.log('Implementing progressive loading');
        
        const tbody = this.table.querySelector('tbody');
        const allRows = Array.from(tbody.querySelectorAll('tr'));
        
        // Hide all rows initially
        allRows.forEach(row => row.style.display = 'none');
        
        // Show rows progressively
        let currentIndex = 0;
        const showNextChunk = () => {
            const endIndex = Math.min(currentIndex + this.options.chunkSize, allRows.length);
            
            for (let i = currentIndex; i < endIndex; i++) {
                allRows[i].style.display = '';
            }
            
            currentIndex = endIndex;
            
            if (currentIndex < allRows.length) {
                requestAnimationFrame(showNextChunk);
            }
        };
        
        showNextChunk();
    }
    
    implementBasicOptimizations() {
        console.log('Implementing basic optimizations');
        
        // Use CSS transforms for better performance
        this.table.style.willChange = 'transform';
        this.table.style.contain = 'layout style paint';
        
        // Optimize reflows
        this.table.style.tableLayout = 'fixed';
        
        // Enable hardware acceleration
        this.table.style.transform = 'translateZ(0)';
    }
    
    setupVirtualScrolling(container) {
        const tbody = this.table.querySelector('tbody');
        const allRows = Array.from(tbody.querySelectorAll('tr'));
        const rowHeight = 40; // Estimated
        
        let visibleStart = 0;
        let visibleEnd = Math.min(50, allRows.length);
        
        const renderVisibleRows = () => {
            // Hide all rows
            allRows.forEach(row => row.style.display = 'none');
            
            // Show only visible rows
            for (let i = visibleStart; i < visibleEnd; i++) {
                if (allRows[i]) {
                    allRows[i].style.display = '';
                    allRows[i].style.transform = `translateY(${i * rowHeight}px)`;
                }
            }
            
            // Set container height
            tbody.style.height = `${allRows.length * rowHeight}px`;
            tbody.style.position = 'relative';
        };
        
        container.addEventListener('scroll', () => {
            const scrollTop = container.scrollTop;
            const containerHeight = container.clientHeight;
            
            visibleStart = Math.floor(scrollTop / rowHeight);
            visibleEnd = Math.min(
                visibleStart + Math.ceil(containerHeight / rowHeight) + 10,
                allRows.length
            );
            
            renderVisibleRows();
        });
        
        renderVisibleRows();
    }
    
    // Memory leak prevention
    cleanup() {
        // Remove event listeners
        // Clear references
        this.table = null;
    }
}

// Automatic optimization based on table size
document.addEventListener('DOMContentLoaded', function() {
    document.querySelectorAll('.interactive-table').forEach(table => {
        new LargeTableOptimizer(table);
    });
});

Conclusion

Interactive Markdown table filtering and sorting transforms static documentation into dynamic, user-centric experiences that enhance content discovery and data exploration capabilities. By implementing comprehensive client-side filtering algorithms, server-side integration patterns, and performance optimization strategies, technical teams can create sophisticated documentation systems that scale efficiently while providing engaging user interactions that improve information accessibility and comprehension.

The key to successful interactive table implementation lies in balancing functionality with performance, choosing appropriate optimization strategies based on dataset size, and maintaining accessibility standards that ensure inclusive user experiences. Whether you’re building documentation platforms, data visualization tools, or content management systems, the techniques covered in this guide provide the foundation for creating professional interactive table experiences that meet modern user expectations.

Remember to test your implementations across different browsers and devices, implement proper error handling and loading states, and optimize performance based on your specific use case and data characteristics. With proper interactive filtering and sorting implementation, your Markdown-based content systems can deliver rich, dynamic experiences that engage users while maintaining the simplicity and portability that makes Markdown such an effective format for structured content presentation.