Сортировка выбранных элементов

Сортировка в интерфейсе выбора играет ключевую роль в восприятии данных пользователем, особенно когда список опций становится длинным или формируется динамически. В Choices.js управление порядком элементов разделяется на две независимые области: сортировка доступных опций и порядок уже выбранных элементов. Эти механизмы настраиваются отдельно и влияют на разные части UI.

Внутренне библиотека оперирует двумя коллекциями:

  • список доступных опций (dropdown / search results);
  • список выбранных элементов (items / tags в поле мультивыбора).

Каждая из этих коллекций может иметь собственную стратегию сортировки, что позволяет гибко управлять UX.

Для управления порядком доступных значений используется параметр:

shouldSort: true

Он определяет, будут ли опции упорядочены автоматически при отображении.

Для выбранных элементов используется отдельный механизм:

shouldSortItems: false

Этот параметр отвечает за то, будет ли изменяться порядок уже выбранных значений.


Сортировка списка опций

Когда shouldSort включён, Choices.js сортирует список доступных опций при каждом обновлении данных или инициализации компонента.

По умолчанию сортировка лексикографическая, основанная на строковом сравнении меток (label). Это поведение подходит для простых списков, например стран, городов, категорий.

const choices = new Choices('#select', {
  shouldSort: true
});

Однако стандартная сортировка часто оказывается недостаточной при работе с локализованными данными или числовыми значениями, записанными как строки.


Пользовательская функция сортировки

Для тонкой настройки порядка элементов используется функция сравнения. В Choices.js она может быть передана через sortFn (или аналогичный comparator в зависимости от версии).

Принцип работы аналогичен стандартному Array.prototype.sort:

const choices = new Choices('#select', {
  shouldSort: true,
  sortFn: function(a, b) {
    return a.label.localeCompare(b.label, 'ru');
  }
});

Использование localeCompare позволяет учитывать языковые особенности, что особенно важно для кириллицы.

Более сложный пример включает приоритетизацию:

sortFn: (a, b) => {
  if (a.customProperties?.priority && !b.customProperties?.priority) {
    return -1;
  }
  if (!a.customProperties?.priority && b.customProperties?.priority) {
    return 1;
  }
  return a.label.localeCompare(b.label, 'ru');
}

В этом случае элементы с флагом priority всегда оказываются выше остальных.


Поведение выбранных элементов

Выбранные значения отображаются в виде тегов (chips). Их порядок управляется независимо от dropdown-списка.

shouldSortItems: true

Если этот параметр включён, Choices.js автоматически сортирует выбранные элементы по алфавиту или по внутреннему правилу сортировки.

Если выключен — порядок сохраняется в том виде, в котором пользователь добавлял элементы.

const choices = new Choices('#select', {
  shouldSortItems: false
});

Это поведение критично для сценариев, где важна последовательность выбора, например:

  • построение маршрутов;
  • ранжирование предпочтений;
  • настройка приоритетов;
  • формирование ordered list.

Контроль порядка добавления

При shouldSortItems: false библиотека сохраняет FIFO-логику (first in — first out). Последний добавленный элемент всегда отображается последним.

При shouldSortItems: true порядок переопределяется сразу после каждого изменения состояния, что может визуально “перемещать” уже выбранные элементы.


Кастомная сортировка выбранных элементов

Начиная с расширенных конфигураций Choices.js, порядок выбранных элементов может быть дополнительно контролируем через пользовательскую логику, используя обработку событий и программное управление состоянием.

Пример внешнего управления:

const choices = new Choices('#select', {
  shouldSortItems: false
});

choices.passedElement.element.addEventListener('addItem', () => {
  const items = choices.getValue();

  const sorted = items.sort((a, b) => {
    return a.label.length - b.label.length;
  });

  choices.clearStore();
  choices.setValue(sorted);
});

В этом случае сортировка реализуется вручную, вне встроенного механизма.


Взаимодействие shouldSort и shouldSortItems

Обе настройки независимы, но их комбинация влияет на итоговый UX.

shouldSort shouldSortItems Поведение
true false опции отсортированы, выбранные в порядке добавления
true true оба списка отсортированы автоматически
false false полный контроль порядка вручную
false true выбранные сортируются, опции сохраняют исходный порядок

Такое разделение позволяет строить как строго структурированные интерфейсы, так и полностью свободные системы выбора.


Локализация и стабильность сортировки

При работе с многоязычными интерфейсами важно учитывать нестабильность стандартной сортировки JavaScript. Простое сравнение строк может давать неожиданные результаты при наличии диакритики, разных алфавитов или смешанных языков.

Рекомендуемый подход:

sortFn: (a, b) => {
  return a.label.localeCompare(b.label, ['ru', 'en'], {
    sensitivity: 'base'
  });
}

Это обеспечивает предсказуемый порядок вне зависимости от регистра и диакритических знаков.


Сортировка с учётом числовых значений

Если значения содержат числа, записанные строками, стандартная сортировка приводит к лексикографическому порядку:

1, 10, 2, 20

Для корректного результата применяется преобразование:

sortFn: (a, b) => {
  return Number(a.value) - Number(b.value);
}

Или более универсальный вариант:

sortFn: (a, b) => {
  return parseInt(a.label, 10) - parseInt(b.label, 10);
}

Динамическое обновление сортировки

В сценариях, где данные поступают асинхронно (например, через API), сортировка может применяться после загрузки:

fetch('/api/options')
  .then(res => res.json())
  .then(data => {
    choices.setChoices(data, 'value', 'label', true);
  });

Параметр true в setChoices может инициировать перезапись и потенциально вызвать повторную сортировку, если shouldSort активен.


Типичные проблемы при сортировке

Одной из распространённых ошибок является конфликт между:

  • пользовательской сортировкой;
  • автоматической сортировкой Choices.js;
  • порядком данных, приходящих с сервера.

В таких случаях итоговый порядок может отличаться от ожидаемого, поскольку библиотека применяет сортировку после каждого обновления состояния.

Другой частый случай — потеря порядка выбранных элементов при обновлении списка опций. Это происходит при повторной инициализации или очистке store:

choices.clearStore();

После этого состояние выбора сбрасывается, и порядок необходимо восстанавливать вручную.


Производительность при больших списках

При больших объёмах данных (тысячи элементов) сортировка становится заметной операцией. Особенно при включённых обоих параметрах сортировки.

Оптимизация достигается за счёт:

  • предварительной сортировки данных до передачи в Choices.js;
  • отключения shouldSort при статичных наборах;
  • минимизации вызовов setChoices и setValue.

В высоконагруженных интерфейсах предпочтительнее централизованная сортировка на уровне данных, а не на уровне UI-компонента.


Поведение при фильтрации и поиске

Поиск внутри Choices.js влияет только на отображаемый subset опций. При включённом shouldSort результат поиска также сортируется заново, что может менять визуальный порядок совпадений.

Это особенно заметно при поиске по частичным совпадениям, когда релевантность не учитывается, а применяется только алфавитный порядок.

Для сохранения релевантности сортировку лучше отключать:

shouldSort: false

и полагаться на порядок, возвращаемый сервером или алгоритмом поиска.