Работа с несколькими элементами <select> на одной
странице требует единообразного подхода к инициализации и управлению
состоянием экземпляров Slim Sel ect. При большом количестве форм,
фильтров, таблиц и динамически создаваемых блоков возникает
необходимость централизованного создания экземпляров, контроля повторной
инициализации и корректного обновления конфигураций.
Slim Select применяется к стандартным HTML-элементам
<select>. При множественной инициализации на странице
структура обычно выглядит следующим образом:
<select class="js-select">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<select class="js-select">
<option value="a">Option A</option>
<option value="b">Option B</option>
</select>
<select class="js-select">
<option value="x">Option X</option>
<option value="y">Option Y</option>
</select>
Все элементы объединены общим CSS-классом, который используется как точка входа для инициализации.
Основной способ множественной инициализации заключается в обходе
NodeList, возвращаемого querySelectorAll, с созданием
отдельного экземпляра Slim Select для каждого элемента.
import SlimSelect fr om 'slim-select';
const selects = document.querySelectorAll('.js-select');
selects.forEach((select) => {
new SlimSelect({
select: select
});
});
Каждый <select> получает независимый экземпляр,
что позволяет изолировать состояние, выбранные значения и
конфигурацию.
При множественной инициализации часто используется общий набор опций. Это уменьшает дублирование кода и обеспечивает единообразное поведение компонентов.
const config = {
showSearch: true,
placeholder: 'Выберите значение',
searchText: 'Поиск...',
};
document.querySelectorAll('.js-select').forEach((select) => {
new SlimSelect({
select,
...config
});
});
Расширение конфигурации через spread-оператор позволяет сохранять базовые настройки и при необходимости дополнять их индивидуальными параметрами.
При необходимости различать поведение отдельных селектов используется
подход с data-* атрибутами. Это позволяет задавать
параметры прямо в HTML.
<select class="js-select" data-placeholder="Город">
<option value="1">Алматы</option>
</select>
<select class="js-select" data-placeholder="Страна">
<option value="kz">Казахстан</option>
</select>
Jav * aScript:
document.querySelectorAll('.js-select').forEach((select) => {
const placeholder = select.dataset.placeholder;
new SlimSelect({
select,
placeholder: placeholder || 'Выберите значение'
});
});
Такой подход снижает зависимость от внешней логики и делает конфигурацию декларативной.
При повторном вызове инициализации (например, после AJAX-обновления DOM) возникает риск создания дублирующихся экземпляров. Для предотвращения этого используется маркировка элемента.
document.querySelectorAll('.js-select').forEach((select) => {
if (select.dataset.ssInitialized) return;
new SlimSelect({
select
});
select.dataset.ssInitialized = 'true';
});
const instances = new WeakMap();
function initSelect(select) {
if (instances.has(select)) return;
const instance = new SlimSelect({ select });
instances.set(select, instance);
}
document.querySelectorAll('.js-select').forEach(initSelect);
Использование WeakMap обеспечивает автоматическое
освобождение памяти при удалении DOM-элемента.
В интерфейсах с динамическим добавлением форм (модальные окна, таблицы, вкладки) требуется повторная инициализация только новых узлов.
function initAll() {
document.querySelectorAll('.js-select').forEach((select) => {
if (!select.dataset.ssInitialized) {
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
}
});
}
После добавления нового HTML-блока вызывается initAll,
который обрабатывает только неинициализированные элементы.
При сложных интерфейсах ручной контроль может быть заменён наблюдением за DOM:
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType !== 1) return;
if (node.matches?.('.js-select')) {
new SlimSelect({ select: node });
node.dataset.ssInitialized = 'true';
}
node.querySelectorAll?.('.js-select').forEach((select) => {
if (!select.dataset.ssInitialized) {
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
}
});
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
Такой механизм обеспечивает автоматическую интеграцию новых селектов без дополнительного вызова функций инициализации.
При необходимости последующего управления всеми экземплярами (например, обновление или уничтожение) применяется хранение ссылок:
const slimSelectInstances = [];
document.querySelectorAll('.js-select').forEach((select) => {
const instance = new SlimSelect({ select });
slimSelectInstances.push(instance);
});
Позднее возможно массовое взаимодействие:
slimSelectInstances.forEach((instance) => {
instance.setData([
{ text: 'New Option', value: 'new' }
]);
});
При полной перерисовке части интерфейса требуется корректное
уничтожение старых экземпляров. Slim Select предоставляет метод
destroy.
const instances = new WeakMap();
function init(select) {
if (instances.has(select)) {
instances.get(select).destroy();
}
const instance = new SlimSelect({ select });
instances.set(select, instance);
}
Такой подход предотвращает утечки памяти и дублирование UI-обёрток.
Элементы внутри скрытых контейнеров (display: none)
могут инициализироваться некорректно из-за отсутствия вычисляемых
размеров. В таких случаях применяется отложенная инициализация:
function initVisibleSelects() {
document.querySelectorAll('.js-select').forEach((select) => {
if (select.offsetParent === null) return;
if (!select.dataset.ssInitialized) {
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
}
});
}
Компоненты, создаваемые внутри модальных окон, часто требуют инициализации в момент открытия:
modal.addEventListener('open', () => {
modal.querySelectorAll('.js-select').forEach((select) => {
if (!select.dataset.ssInitialized) {
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
}
});
});
При закрытии модального окна экземпляры обычно сохраняются, чтобы избежать повторной инициализации при повторном открытии.
При большом количестве элементов важным становится уменьшение количества DOM-операций. Оптимизированный подход включает единоразовое получение списка и минимизацию логики внутри цикла:
const selects = document.querySelectorAll('.js-select');
const length = selects.length;
for (let i = 0; i < length; i++) {
const select = selects[i];
if (select.dataset.ssInitialized) continue;
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
}
Использование классического цикла вместо forEach снижает
накладные расходы в высоконагруженных сценариях.
При сложных интерфейсах применяется фабричный подход:
function createSlimSelectConfig(type) {
const base = {
showSearch: true
};
if (type === 'country') {
return {
...base,
placeholder: 'Страна'
};
}
if (type === 'city') {
return {
...base,
placeholder: 'Город'
};
}
return base;
}
document.querySelectorAll('.js-select').forEach((select) => {
const type = select.dataset.type;
new SlimSelect({
select,
...createSlimSelectConfig(type)
});
});
В SPA-подобных структурах DOM может полностью пересоздаваться. В таких случаях логика инициализации должна учитывать отсутствие старых узлов:
function reinitSlimSelect() {
document.querySelectorAll('.js-select').forEach((select) => {
if (select.dataset.ssInitialized) {
delete select.dataset.ssInitialized;
}
new SlimSelect({ select });
select.dataset.ssInitialized = 'true';
});
}
Такой подход гарантирует синхронизацию UI-компонентов с текущим DOM-состоянием.