Каскадные выпадающие списки (dependent dropdowns) в Slim Select реализуются через управление состоянием нескольких инстансов селекта и динамическое обновление их опций на основе выбора в родительском списке. Основная идея заключается в том, что значение одного select определяет набор доступных значений в следующем, формируя цепочку зависимостей уровня «страна → регион → город», «категория → подкатегория → товар» и аналогичных структур.
Каскадные списки строятся вокруг иерархических данных. На практике это чаще всего JSON-структура:
const data = {
kazakhstan: {
karaganda: ["Balkhash", "Temirtau"],
astana: ["Esil", "Almaty District"]
},
russia: {
moscow: ["Central", "North"],
spb: ["Nevsky", "Primorsky"]
}
};
Каждый уровень зависит от предыдущего, поэтому изменение значения в первом селекте должно приводить к полной переработке второго и третьего уровней.
Каждый уровень каскада — отдельный экземпляр Slim Select.
<select id="country"></select>
<select id="region"></select>
<select id="city"></select>
const countrySelect = new SlimSelect({
select: '#country'
});
const regionSelect = new SlimSelect({
select: '#region'
});
const citySelect = new SlimSelect({
select: '#city'
});
Первоначально нижние уровни обычно инициализируются пустыми или с disabled состоянием.
Первый select не зависит ни от чего и заполняется статически:
countrySelect.setData([
{ text: 'Kazakhstan', value: 'kazakhstan' },
{ text: 'Russia', value: 'russia' }
]);
После выбора страны становится возможным построение следующего уровня.
Ключевой механизм каскада — обработка события изменения значения.
document.querySelector('#country').addEventListener('change', (e) => {
const country = e.target.value;
updateRegions(country);
resetCities();
});
Функция updateRegions отвечает за формирование второго
уровня.
Slim Select не требует пересоздания инстанса для обновления данных.
Используется метод setData.
function updateRegions(country) {
const regions = data[country];
const options = Object.keys(regions).map(region => ({
text: region,
value: region
}));
regionSelect.setData(options);
}
После обновления второго уровня важно сбрасывать выбранное значение:
regionSelect.set('');
Каскадная логика требует строгого сброса всех дочерних селектов при изменении родителя.
function resetCities() {
citySelect.setData([]);
citySelect.set('');
}
При изменении региона аналогично пересоздается список городов.
document.querySelector('#region').addEventListener('change', (e) => {
const region = e.target.value;
const country = document.querySelector('#country').value;
updateCities(country, region);
});
function updateCities(country, region) {
const cities = data[country][region];
const options = cities.map(city => ({
text: city,
value: city
}));
citySelect.setData(options);
}
Каскадные списки часто требуют блокировки зависимых уровней до выбора родителя.
regionSelect.disable();
citySelect.disable();
После выбора страны:
regionSelect.enable();
После выбора региона:
citySelect.enable();
Это предотвращает некорректные состояния интерфейса.
В реальных приложениях данные часто приходят с сервера.
async function fetchRegions(country) {
const res = await fetch(`/api/regions?country=${country}`);
return await res.json();
}
Интеграция с Slim Select:
async function updateRegions(country) {
regionSelect.setData([]);
regionSelect.disable();
const regions = await fetchRegions(country);
regionSelect.setData(
regions.map(r => ({ text: r.name, value: r.id }))
);
regionSelect.enable();
}
Чтобы избежать повторных запросов, используется локальный кеш.
const cache = {};
async function getRegions(country) {
if (cache[country]) return cache[country];
const data = await fetchRegions(country);
cache[country] = data;
return data;
}
Это особенно важно при частых переключениях значений.
При расширении логики до 4–5 уровней важно централизовать управление состоянием.
function resetFrom(level) {
if (level <= 2) {
regionSelect.setData([]);
regionSelect.set('');
regionSelect.disable();
}
if (level <= 3) {
citySelect.setData([]);
citySelect.set('');
citySelect.disable();
}
}
При асинхронной загрузке возможна ситуация, когда пользователь быстро меняет значение, и старый запрос перезаписывает новый результат.
Решение — контроль актуальности запроса:
let currentRequestId = 0;
async function updateRegions(country) {
const requestId = ++currentRequestId;
const regions = await fetchRegions(country);
if (requestId !== currentRequestId) return;
regionSelect.setData(
regions.map(r => ({ text: r.name, value: r.id }))
);
}
Иногда требуется реакция не только на пользовательские события, но и на программные изменения.
countrySelect.onCha nge = () => {
const value = countrySelect.getSelected();
updateRegions(value);
resetCities();
};
При редактировании формы необходимо восстановить каскадное состояние.
async function initForm(country, region, city) {
countrySelect.set(country);
await updateRegions(country);
regionSelect.set(region);
await updateCities(country, region);
citySelect.set(city);
}
Ключевой момент — строгое последовательное ожидание загрузки данных.
При масштабных формах каскады становятся дорогими по производительности. Используются техники:
Каскадные списки с Slim Select фактически реализуют реактивную цепочку:
Такая модель масштабируется до сложных форм, включая административные панели, фильтры товаров и географические справочники.