Модуль "Выбор города в админке" для 1C-Битрикс

Проблема, которую мы рассмотрим, встречается довольно редко, однако раз попавшись, может убить немало времени и нервных клеток. Практически в любом интернет-магазине, работающем на 1С Bitrix, приходилось привязывать новые службы доставки к определенному городу или региону, или же создавать группы местоположений. Сколько счастливых минут проводил несчастный настройщик, пытаясь найти маленьком окошке селекта размером в 5 элементов нужный город из тысяч возможных. Ситуация эта немного напоминает проблему, решаемую модулем Список 2.0, только в данном случае мы имеем дело с более простой ее разновидностью: простым поиском необходимого элемента списка в огромном селекте.

Краткий ликбез

Селекты, как лаконично называют выпадающие списки, чаще всего используются в формах для вывода небольшого числа вариантов какого-либо значения. Удобство использования этого элемента формы по сравнению с чекбоксами и радиокнопками спорное, да и обсуждать мы его не будем, так как не оно является темой статьи . Проблема огромного количества значений в Битриксе встретилась один раз, в несчастных местоположениях. Это свойство выводится именно в селекте, и, если магазин работает с парой городов, такой подход идеален, а вот если загрузить большую часть городов России и СНГ еще добавить...


В этом селекте 2346 пунктов. Приятных поисков?

Работу описываемого решения можно сразу оценить, загрузив на сайт, управляемый Битриксом, наш модуль. Ниже расскажем, как оно разрабатывалось и, самое главное, с какими подводными камнями столкнулись.

Постановка задачи

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

Организация интерфейса

Функционал должен быть лаконичным и негромоздким, чтобы не забивать собой и без того перегруженную элементами страницу админки. Он будет доступен при нажатии на кнопку, что появится около селекта при наведении на него указателя мыши. Для ввода искомого значения селекта подойдет одинокий текстовый инпут. А чтобы сделать поиск более удобным, искомое значение должно подсвечиваться, допустим, зеленым цветом.
Чтобы особо не заморачиваться, привяжем поиск ко всем селектам с множественным выбором, у которых количество элементов больше, чем его размер:

		$("select[multiple]").each(function(){
			var size=$(this).attr("size");
			if(size && $(this).find("option").length>size)
				//на событие onmouseover вешаем обработчик, который будет двигать кнопку и инпут к селекту
				$(this).hover(function(wat){IPOLSESR_selInHover(wat)})
		});

Поиск указанного значения

Организуем инпут, следующий за селектами, таким образом:

		
IPOLSESR_sel - это контейнер, в котором находится кнопка активации инпута (первый div) и сам инпут, невидимый до нажатия на активирующий div (функция IPOLSESR_clickSel). При вводе какого-либо значения в инпут запускается функция поиска значения в селекте (IPOLSESR_search), а при двойном клике - закроем инпут (IPOLSESR_selDeact).

Чтобы найти нужный элемент в списке достаточно взять все дочерние теги option у рассматриваемого селекта и сравнить их содержимое с введенным в инпут значением (то бишь определить функцию IPOLSESR_search). Для обеспечения прокрутки к элементу нам так же нужно знать порядковый номер рассматриваемого варианта .

		//запоминаем введенное значение
		var search = $("#IPOLSESR_sel input").val();

		//ищем все дочерние select"ы у рассматриваемого инпута, запоминаем их номер
		IPOLSESR_hdlr.find("option").each(function(ind){
			if($(this).html().toLowerCase().indexOf(search.toLowerCase())!==-1){
				//тут мы потом магическим образом прокрутим селект к искомому варианту
			}
		}

Прокрутка селекта к нужному пункту

Как прокрутить select к определенному option?
Задача куда сложнее, чем кажется на первый взгляд. Подхода может быть два:

  • Прокрутка с помощью функции scrollTop
  • Выделение искомого option
Реализованы будут оба варианта, но отнюдь не из-за хорошей жизни. Получается следующий код (который мы, как уважаемый читатель наверняка догадался, вставим в предыдущий на место саркастичного комментария):
				//прокрутка с помощью scrollTop
					var size=IPOLSESR_hdlr.attr("size");//определяем размер селекта
					if(size && typeof size!="undefined"){
						//определяем, на сколько прокручивать селект
						var scrolling=Math.round(parseInt(IPOLSESR_hdlr.height())/size)*(ind-size+1);
						if(scrolling < 0)
							scrolling=0;
						//прокрутка
						IPOLSESR_hdlr.scrollTop(scrolling);
					}
				//прокрутка с помощью выделения
					//а был ли option выделен ранее?
					var ifSel=false;
					if($(this).prop("selected")){
						ifSel=true;
						$(this).removeProp("selected");
					}
					$(this).prop("selected",true);//выделяем
					//если не был ранее выделем - снимаем выделение
					if(!ifSel)
						$(this).removeProp("selected");
Первый вариант будет работать в большинстве браузеров, кроме оперы 12 и ниже. Второй не работает в Google Chrome. Следовательно, для успешной работы необходимо (как бы это ни звучально костыльно) делать проверку на браузер, например, следующим условием:
	if(typeof($.browser.chrome)!="undefined"){
		//наш пациент - хром
	}
Как уже отмечалось выше, подводных камней при поиске решения было найдено немало, но описывать их полностью долго и нудно, если только, конечно, уважаемому читателю, не интересно о них узнать.

Прокрутка селекта к нужному пункту

Собирая в кучу все вышенаписанное, собираем готовый код. Подключая его на нужной странице, получаем поиск по громоздким селектам (или по каким-либо другим, если прописать соответствующее условие в функцию инициализации).
Посмотреть код.

Дополнительный функционал

Разумеется, не стоит останавливаться на достигнутом, ведь проблема поиска значения в выпадающем списке характерна не только для развернутых select"ов (то есть тех, у которых установлен атрибут size). Иногда CMS (или чокнутый верстальщик) выдает множество вариантов и в однострочном select"е. Самый простой способ решения этой проблемы - это использование библиотеки jquery ui Autocomplite, но и функционал нашего модуля так же можно адаптировать для такого случая, насильно принудительно разворачивая выпадающий список и применяя на нем указанные выше функции

Подключение и примеры

В первую очередь обеспечиваем себя библиотекой jquery. Скачать ее можно, например, с официального сайта.
Или же просто вставить в шапку (то, что внутри тега <head>) сайта строку

<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
Кроме того можно подключить jquery функцией Битрикса CJSCore::Init(array("jquery"));
Затем устанавливаем модуль для вашего сайта и указываем страницы, на которых будет доступен его функционал в настройках.
Если вдруг ваш сайт не на Битриксе, но данный функционал необходим - просто скопируйте код и настройте его как нужно, не забыв задать стили для элементов функционала, например, такие:
		#IPOLSESR_sel{
			position:absolute;
			display:none;
		}
		#IPOLSESR_sel div{
			 /*фон и размеры для кнопки*/
			background: ;
			width:20px;
			height:20px;
			cursor:pointer;
		}
		#IPOLSESR_sel input{
			width:0px;
			display:none;
		}
А пример работы модуля покажем ниже.

Все, что можно было сказать по теме и без, сказано, спасибо за внимание.
В качестве домашней работы можно взять следующие пункты:

  1. Сделать подсветку элементов, которые ищутся
  2. Оптимизация скрипта для любого select"a и динамическое подключение функционала к указанному select"у