На одном из сайтов, который я имею честь обслуживать, для кастомизации селекта используется замечательный jQuery-плагин cuSel. Он удобен тем, что достаточно прост в настройке и позволяет кастомизировать даже полосу прокрутки.
Но иногда случается так, что список элементов может быть очень большим. И быстро найти нужный пункт не так просто.
Однажды мне поступила задача реализовать возможность ввода текста и фильтрации элементов по первым буквам. Список состоял из названий городов Украины и был достаточно длинным. Поскольку в самом плагине данная возможность отсутствовала, а отказываться от красивого селекта как и я так и заказчик не хотел, было принято решение внести небольшие доработки в код скрипта.
Сегодня я постараюсь повторить все это вместе с вами. Для этого возьмем демонстрационный пример со странички плагина. В архиве содержаться все необходимые библиотеки и стили. На странице index.html автор подготовил несколько примеров подключения и использования плагина. Давайте добавим фильтрацию для первого селекта, в котором содержится список стран.
Для ввода текста мы будем использовать прозрачное текстовое поле, которое по клику будет налаживаться верхним слоем на селект. Добавим для этого поля соответствующий css-класс в файл css/cusel.css и установим такие же размеры для блока, в котором отображается текст выбранного элемента списка.
.cusel .search-field {
position: absolute;
outline:0;
border: 0;
background: transparent;
display:none;
}
.cusel .search-field, .cuselText {
width: 144px;
height: 20px;
line-height:20px;
padding: 3px 30px 3px 6px;
}
Для того чтобы была возможность добавлять фильтрацию только нужным селектам, будем помечать их дополнительным классом filtering.
<select class="sel80 filtering" id="country" name="country" tabindex="2">
Откроем файл js/cusel.js и добавим следующий код в функцию cuselEvents.
jQuery(".cusel").each(function () {
var itv;
var elm = $(this);
if (elm.hasClass("filtering") && elm.find(".search-field").length == 0) {
var f = $("<input type='text' />")
f.addClass("search-field");
f.keydown(function () {
clearInterval(itv);
var list = elm.find(".cusel-scroll-pane > span");
itv = setInterval(function () {
list.each(function () {
var item = $(this);
item.show();
if (item.text().toLowerCase().indexOf(f.val().toLowerCase()) != 0)
item.hide();
});
var d = elm.find(".cusel-scroll-pane").eq(0).attr("id");
jQuery("#" + d)[0].scrollTo(0);
clearInterval(itv);
}, 100);
});
f.click(function () {
$(this).val('').hide();
elm.focus();
});
elm.append(f);
}
});
Что делает данный код? Находит на странице все элементы с классом «cusel» т.к. именно этот класс используется в стилизированных селектах. Далее, если для селекта указан класс «filtering» и он не имеет дочерний элемент с классом «search-field» (наше поле для ввода текста), то такое поле создается и на него вешается обработчик события. Обработчик, по каждому нажатию клавиш в поле, находит пункты в списке, текст которых не соответствует тексту в поле и просто скрывает их.
Теперь нам нужно сделать так, чтобы по клику на селекте над ним появлялось наше прозрачное поле. Для этого в той же функцие cuselEvents найдем обработчик события «click». В нем необходимо отыскать часть кода помеченную комментарием автора «если кликнули по самому селекту (текст)» и изменить следующим образом.
/* если кликнули по самому селекту (текст) */
if((clickedClass.indexOf("cuselText")!=-1 || clickedClass.indexOf("cuselFrameRight")!=-1) && clicked.parent().prop("class").indexOf("classDisCusel")==-1)
{
var cuselWrap = clicked.parent().find(".cusel-scroll-wrap").eq(0);
var parent = clicked.parents(".cusel");
if (parent.hasClass("filtering")) {
var txt = parent.children(".cuselText");
var sf = parent.children(".search-field");
if(sf.is(":hidden")) {
txt.text("");
sf.show().focus();
}
else {
txt.text(parent.find(".cuselActive").eq(0).text());
sf.val("").hide();
}
}
/* если выпадающее меню скрыто - показываем */
cuselShowList(cuselWrap);
}
Поскольку поле для ввода текста прозрачное, чтобы не перекрывать фон, мы очищаем текст выбранного элемента в селекте. Повторный клик будет возвращать исходное состояние элемента.
Теперь сделаем, чтобы при выборе элемента в списке, поле наоборот скрывалось. Ниже найдите блок помеченный комментарием автора «если выбрали позицию в списке» и добавьте туда следующий код
else if(clicked.is(".cusel-scroll-wrap span") && clickedClass.indexOf("cuselActive")==-1) {
var parent = clicked.parents(".cusel");
if (parent.hasClass("filtering")) {
parent.children(".search-field").val('').hide();
parent.find(".cusel-scroll-pane > span").show();
}
//оригинальный код
}
Как видно при этом мы делаем видимыми скрытые при фильтрации элементы списка.
Логично бы было, если пользователь кликнет все списка, закрыть список и спрятать поле фильтра. Поэтому находим ниже блок, помеченный комментарием автора «скрываем раскрытые списки, если кликнули вне списка» и изменяем его следующим образом.
/*
скрываем раскрытые списки, если кликнули вне списка
*/
else {
var wrap = jQuery(".cusel-scroll-wrap");
var parent = wrap.parents(".cusel");
parent.each(function () {
var elm = $(this);
if (elm.hasClass("filtering")) {
elm.find(".cuselText").text(elm.find(".cuselActive").eq(0).text());
elm.find(".search-field").val('').hide();
elm.find(".cusel-scroll-pane > span").show();
}
});
wrap.hide();
parent.removeClass("cuselOpen")
}
И теперь последний штрих. В самом конце функции cuselEvents вы найдете кусок кода помеченный следующим комментарием
/*
функция отбора по нажатым символам (от Alexey Choporov)
отбор идет пока пауза между нажатиями сиволов не будет больше 0.5 сек
keypress нужен для отлова символа нажатой клавиш
*/
Функция Алексея подбирает подходящее значение по первой букве. Мы же реализовываем возможность отбора по нескольким буквам. Поэтому во избежание конфликтов код Алексея необходимо закомментировать.
Автор: aleksey_bober