Фильтрация таблиц на основе выбранных значений — один из наиболее распространённых сценариев взаимодействия интерфейса с данными. Комбинация табличного представления и многофункционального селектора позволяет строить гибкие системы отбора, в которых Tom Select выступает слоем управления критериями, а таблица — визуализацией результата.
Основная идея заключается в том, что Tom Select формирует набор активных фильтров, которые затем применяются к массиву данных таблицы либо на стороне клиента, либо через серверный запрос. Такой подход разделяет ответственность: компонент выбора отвечает за состояние фильтров, а таблица — за отображение уже отфильтрованного результата.
Связь между Tom Select и таблицей строится через единый источник данных и механизм реактивного обновления.
Типичная архитектура включает:
Tom Select в этой схеме управляет состоянием фильтров, генерируя события изменения значения.
const tableData = [
{ id: 1, name: "Alpha", status: "active", category: "A" },
{ id: 2, name: "Beta", status: "archived", category: "B" },
{ id: 3, name: "Gamma", status: "active", category: "A" }
];
let filters = {
status: [],
category: []
};
Для каждого поля фильтрации создаётся отдельный экземпляр Tom Select. Значения синхронизируются с объектом состояния фильтров.
const statusSelect = new TomSelect("#status-filter", {
maxItems: null,
onChange(values) {
filters.status = values;
applyFilters();
}
});
const categorySelect = new TomSelect("#category-filter", {
maxItems: null,
onChange(values) {
filters.category = values;
applyFilters();
}
});
Каждое изменение состояния селектора приводит к пересчёту набора строк таблицы.
Локальная фильтрация реализуется через последовательное применение условий к исходному массиву.
function applyFilters() {
let result = tableData;
if (filters.status.length > 0) {
result = result.filter(row =>
filters.status.includes(row.status)
);
}
if (filters.category.length > 0) {
result = result.filter(row =>
filters.category.includes(row.category)
);
}
renderTable(result);
}
Функция renderTable отвечает за обновление DOM-таблицы и
перерисовку строк на основе отфильтрованного набора.
Отрисовка таблицы может выполняться через прямое управление DOM или через шаблонную функцию.
function renderTable(data) {
const tbody = document.querySelector("tbody");
tbody.innerHTML = "";
data.forEach(row => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${row.id}</td>
<td>${row.name}</td>
<td>${row.status}</td>
<td>${row.category}</td>
`;
tbody.appendChild(tr);
});
}
При изменении фильтров пересобирается только тело таблицы, без изменения структуры.
В сложных интерфейсах используется несколько уровней фильтрации, где каждый селектор влияет на отдельное поле данных. Tom Select поддерживает множественный выбор, что позволяет формировать комбинированные условия.
Расширение модели состояния:
let filters = {
status: [],
category: [],
tags: []
};
Добавляется дополнительный селектор:
const tagsSelect = new TomSelect("#tags-filter", {
maxItems: null,
onChange(values) {
filters.tags = values;
applyFilters();
}
});
Фильтрация по массивным полям требует дополнительной логики сопоставления:
if (filters.tags.length > 0) {
result = result.filter(row =>
filters.tags.some(tag => row.tags.includes(tag))
);
}
Tom Select позволяет задавать собственную логику поиска внутри выпадающего списка, но фильтрация таблицы может иметь отдельные правила, не совпадающие с поисковым алгоритмом селектора.
Пример кастомной логики сравнения:
function matchRule(row, filters) {
return (
(filters.status.length === 0 || filters.status.includes(row.status)) &&
(filters.category.length === 0 || filters.category.includes(row.category)) &&
(filters.tags.length === 0 ||
filters.tags.some(tag => row.tags.includes(tag)))
);
}
Использование унифицированного предиката упрощает масштабирование системы фильтрации.
При больших объёмах данных локальная фильтрация становится неэффективной. В таких случаях Tom Select используется как генератор параметров для серверного запроса.
async function applyFilters() {
const query = new URLSearchParams();
filters.status.forEach(v => query.append("status", v));
filters.category.forEach(v => query.append("category", v));
const response = await fetch(`/api/table?${query.toString()}`);
const data = await response.json();
renderTable(data);
}
Tom Select при этом остаётся источником состояния, но не участвует в обработке данных.
Фильтры могут подгружаться динамически, особенно при больших справочниках.
const categorySelect = new TomSelect("#category-filter", {
load: function(query, callback) {
fetch(`/api/categories?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => callback(data))
.catch(() => callback());
}
});
Такая модель позволяет масштабировать фильтры до тысяч значений без предварительной загрузки.
Состояние фильтров может быть сериализовано в адресную строку для сохранения представления таблицы.
function syncToUrl() {
const params = new URLSearchParams();
filters.status.forEach(v => params.append("status", v));
filters.category.forEach(v => params.append("category", v));
history.replaceState(null, "", "?" + params.toString());
}
Обратная синхронизация:
function readFromUrl() {
const params = new URLSearchParams(location.search);
filters.status = params.getAll("status");
filters.category = params.getAll("category");
statusSelect.setValue(filters.status);
categorySelect.setValue(filters.category);
}
При росте количества строк таблицы ключевым фактором становится стоимость фильтрации и перерисовки DOM.
Оптимизационные подходы включают:
Пример debounce:
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const applyFiltersDebounced = debounce(applyFilters, 150);
В сложных интерфейсах фильтры часто работают совместно с поиском, сортировкой и пагинацией. Tom Select в таких системах формирует только часть параметров запроса.
Типовая цепочка обработки данных:
Такая модель позволяет сохранять предсказуемость поведения интерфейса при изменении любого из параметров.
Фильтры могут зависеть друг от друга. Например, выбор категории влияет на доступные статусы. Tom Select в этом случае используется как каскадный контроллер.
categorySelect.on("change", async (values) => {
const res = await fetch(`/api/statuses?category=${values.join(",")}`);
const statuses = await res.json();
statusSelect.clearOptions();
statusSelect.addOptions(statuses);
});
Такая связка позволяет формировать адаптивные фильтрационные панели, где доступные значения изменяются в зависимости от текущего контекста данных.