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

1. Что входит в BX.selectUtils.*
Метод | Назначение | Ключевые параметры |
---|---|---|
addNewOption(node, value, name, sort = false, unique = true) |
Добавляет новую опцию | sort — сразу отсортировать?unique — проверять дубликаты? |
deleteOption(node, value) |
Удаляет опцию с заданным value |
— |
deleteSelectedOptions(node) |
Удаляет все выбранные опции | — |
deleteAllOptions(node) |
Полностью очищает список | — |
sortSelect(node) |
Сортирует опции по тексту | — |
selectAllOptions(node) |
Выделяет каждую опцию | — |
selectOption(node, value) |
Выделяет опцию по value |
— |
moveOptionsUp(node) / moveOptionsDown(node) |
Поднимает/опускает выбранные элементы | — |
2. Полный рабочий пример
Код ниже работает даже без установленного Битрикса благодаря мини-заглушке BX
. Если вы вставляете его в реальный проект Bitrix, просто удалите блок *“BX-stub”* и подключите ядро Битрикса.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Демо BX.selectUtils (самодостаточный)</title>
<style>
.panel{max-width:340px;margin:16px auto;font-family:sans-serif}
.panel button{width:100%;margin:6px 0;padding:8px;border:1px solid #888;
background:#f2f2f2;border-radius:4px;cursor:pointer}
.panel button:hover{background:#e0e0e0}
.panel select{width:100%;height:130px;margin-bottom:8px}
</style>
</head>
<body>
<div class="panel">
<select id="demo-select" size="5"></select>
<button id="btn-add">Добавить «По дате»</button>
<button id="btn-del-selected">Удалить выбранное</button>
<button id="btn-del-date">Удалить «По дате»</button>
<button id="btn-sort">Сортировать ↑</button>
<button id="btn-move-up">Поднять выбранное ↑</button>
<button id="btn-move-down">Опустить выбранное ↓</button>
<button id="btn-select-all">Выделить всё</button>
<button id="btn-clear">Очистить список</button>
</div>
<script>
/* ----------------- BX-stub: удалить при использовании в Bitrix ---------------- */
(function(w){
if(w.BX) return; // ядро уже подключено
const $ = id => document.getElementById(id);
const selectUtils = {
addNewOption(node,val,text,sort=false,unique=true){
if(unique && [...node.options].some(o=>o.value===val)) return;
node.add(new Option(text,val));
if(sort) this.sortSelect(node);
},
deleteOption(node,val){ [...node.options].forEach(o=>o.value===val && o.remove()); },
deleteSelectedOptions(node){ [...node.selectedOptions].forEach(o=>o.remove()); },
deleteAllOptions(node){ node.length = 0; },
sortSelect(node){
[...node.options]
.sort((a,b)=>a.text.localeCompare(b.text,'ru'))
.forEach(o=>node.add(o));
},
selectAllOptions(node){ [...node.options].forEach(o=>o.selected = true); },
selectOption(node,val){ [...node.options].forEach(o=>o.selected = (o.value===val)); },
moveOptionsUp(node){
for(let i=1;i<node.options.length;i++){
const cur=node.options[i], prev=node.options[i-1];
if(cur.selected && !prev.selected) node.insertBefore(cur,prev);
}
},
moveOptionsDown(node){
for(let i=node.options.length-2;i>=0;i--){
const cur=node.options[i], next=node.options[i+1];
if(cur.selected && !next.selected) node.insertBefore(next,cur);
}
}
};
w.BX = Object.assign(
function(id){ return $(id); },
{
ready(fn){ document.addEventListener('DOMContentLoaded',fn); },
bind:(el,ev,fn)=>el.addEventListener(ev,fn),
selectUtils
}
);
})(window);
/* ----------------------------------------------------------------------------- */
BX.ready(()=>{
const $list = BX('demo-select');
// начальные опции
[
{v:'NAME', t:'По наименованию'},
{v:'PRICE', t:'По цене'}
].forEach(o=>BX.selectUtils.addNewOption($list,o.v,o.t));
const a={
add:() => {
BX.selectUtils.addNewOption($list,'DATE','По дате',false,true);
BX.selectUtils.selectOption($list,'DATE');
},
delSel: () => BX.selectUtils.deleteSelectedOptions($list),
delDate:() => BX.selectUtils.deleteOption($list,'DATE'),
sort: () => BX.selectUtils.sortSelect($list),
up: () => BX.selectUtils.moveOptionsUp($list),
down: () => BX.selectUtils.moveOptionsDown($list),
all: () => BX.selectUtils.selectAllOptions($list),
clear: () => BX.selectUtils.deleteAllOptions($list)
};
BX.bind(BX('btn-add'),'click',a.add);
BX.bind(BX('btn-del-selected'),'click',a.delSel);
BX.bind(BX('btn-del-date'),'click',a.delDate);
BX.bind(BX('btn-sort'),'click',a.sort);
BX.bind(BX('btn-move-up'),'click',a.up);
BX.bind(BX('btn-move-down'),'click',a.down);
BX.bind(BX('btn-select-all'),'click',a.all);
BX.bind(BX('btn-clear'),'click',a.clear);
});
</script>
</body>
</html>
Как запустить:
- Сохраните файл под любым именем и откройте в браузере — все кнопки будут активны.
- Для «боевого» проекта в Битрикс удалите блок BX-stub (обёрнут в комментарии) и убедитесь, что ядро подключено.
3. Практические сценарии
Сценарий | Как помогает selectUtils |
---|---|
Динамические фильтры в модуле CRM | Создавайте поля сортировки «на лету» без перерендера страницы |
Множественный перенос опций | moveOptionsUp/Down ускоряет работу с длинными списками (например, настройка отчётов) |
Защита от дубликатов | Передавайте unique=true и избавьтесь от повторов при добавлении |
Предзаполнение через AJAX | deleteAllOptions перед новой подгрузкой + последующий цикл addNewOption |
4. Частые ошибки и лечение
Проблема | Симптом | Решение |
---|---|---|
«BX is not defined» | Открыли HTML вне Битрикса | Подключите ядро (CJSCore::Init ) или используйте stub-версию, показанную выше |
Дубликаты опций | При каждом клике пункт «По дате» добавляется снова | Параметр unique=true или предварительная deleteOption |
Ничего не удаляется | Кликаете «Удалить выбранное», а список не меняется | Убедитесь, что у <select> стоит multiple или используется Ctrl/Shift для выделения |
5. Итоги
- BX.selectUtils.* — это 8 небольших, но очень полезных функций для работы со списками без лишнего кода.
- В административных разделах они избавляют от громоздких циклов
appendChild
/removeChild
. - Пример выше можно копировать напрямую: он обучает, демонстрирует все методы и не требует установленного Битрикса.
Остаётся только интегрировать приёмы в ваши компоненты — и управлять DOM-структурой станет проще и быстрее!