Множественная инициализация

Работа с несколькими элементами <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-классом, который используется как точка входа для инициализации.


Инициализация через querySelectorAll

Основной способ множественной инициализации заключается в обходе 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-атрибуты

При необходимости различать поведение отдельных селектов используется подход с 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) возникает риск создания дублирующихся экземпляров. Для предотвращения этого используется маркировка элемента.

Вариант через data-атрибут

document.querySelectorAll('.js-select').forEach((select) => {
  if (select.dataset.ssInitialized) return;

  new SlimSelect({
    select
  });

  select.dataset.ssInitialized = 'true';
});

Вариант через WeakMap

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, который обрабатывает только неинициализированные элементы.


Использование MutationObserver для автоматизации

При сложных интерфейсах ручной контроль может быть заменён наблюдением за 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' }
  ]);
});

Пересоздание экземпляров при изменении DOM

При полной перерисовке части интерфейса требуется корректное уничтожение старых экземпляров. 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-состоянием.