DOM + javascript порою позволяют создавать удивительные вещи. Одна из этих вещей, которой я хочу с вами поделиться — это динамически формируемые выпадающие списки.
Постановка задачи
Так как я по сути практик, то лучше сразу рассмотреть практическую задачу. Допустим, требуется создать форму выбора модели картриджа принтера. При этом, сначала пользователь может выбрать брендовое название или производителя, а потом уже ограниченный первым выбором — конкретную модель картриджа. В итоге это определяет, например, стоимость услуги связанной с данным картриджем.
Итак, наша задача отталкивается от работы с некоторой таблицей. При формировании массива данных из php удобно пользоваться библиотекой json. А в конечных движках для этих целей бывают реализованы собственные встроенные средства. Например, Drupal имеет функцию drupal_to_js($variable). Не стану вдаваться в подробности json и конкретных движков, будем считать, что нам удалось объявить прайс как двумерный ассоциативный массив в javasctipt. Выглядит это примерно так:
1 2 3 4 5 |
var priceData = [ { "firm": "Brother", "device": "Brother HL-1030/1230/1240/1250/1270", "cart": "TN6300/6600", "p": "380" }, { "firm": "Brother", "device": "Brother HL-1430/1440/1450/1470", "cart": "TN6300/6600", "p": "360"}, { "firm": "Brother", "device": "Brother MFC-8350/8750/9650/9660/9750/9760", "cart": "TN6300/6600", "p": "400"}, { "firm": "Brother", "device": "Brother MFC-9850/9860/9870/9880", "cart": "TN6300/6600", "p": "420"}, ... ]; |
После этого нам понадобятся два списка select, в которых мы и будем отображать наши списки динамически. Вот HTML код этих списков.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<form> Выберите бренд: <select id="firm"> <option value="">-- бренд не выбран --</option> </select> <div id="modelCont" style="display: none;"> Выберите модель картриджа: <select id="model"> <option value="">-- модель не выбрана --</option> </select> </div> </form> |
Обратите внимание, что списки уже содержат по одному элементу option. Второй список изначально скрыт от пользователя установкой стиля контейнера display = none. Мы будем также прятать список моделей, если бренд не выбран.
Вернемся к данным прайса. В каждой строке у нас есть — наименование бренда (firm), модель устройства (device), совместимый с устройством картридж (cart) и цена заправки в рублях (p). Для первого списка нам нужно выбрать не повторяющиеся названия брендов, а для второго использовать название устройства или название картриджа. Чтобы за один проход выбрать из прайса не повторяющиеся названия брендов, лучше всего позаботиться при формировании массива, чтобы строки были отсортированы по firm. Тогда можно написать такую функцию инициализации первого списка:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function firm_init() { var firmSelObj = document.getElementById('firm'); var priceLen = priceData.length; var selLen = firmSelObj.options.length; var lastFirm = ""; for (var i=0; i < priceLen; i++) { var t = priceData[i]; if (lastFirm != t.firm) { firmSelObj.options[selLen ++] = new Option(t.firm, t.firm); lastFirm = t.firm; } } } |
Для создания новых элементов списка мы используем конструктор Option(text, value), где text — это отображаемая метка элемента списка, а value — её значение. Сортированный по firm массив позволяет нам последовательно выбрать значения бредов и добавить их в список.
Далее нам нужно будет создавать и показывать список картриджей. Этот функционал привяжем к событию onchange первого списка. Но перед тем как добавить нужные элементы в список, его нужно очистить. Вот что получается:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function onchangeFirmSelect() { var firmSelObj = document.getElementById('firm'); var modelContainer = document.getElementById('modelCont'); var currentFirm = firmSelObj.value; if (currentFirm == "") { modelContainer.style.display = "none"; //бренд не выбран } else { var modelSelObj = document.getElementById('model'); //очистим список моделей - все, кроме первого элемента for (var i=modelSelObj.options.length-1; i > 0; i--) { modelSelObj.remove(i); } //загрузим список моделей соответствующего бренда var priceLen = priceData.length; //число строк прайса var selLen = modelSelObj.options.length; //текущее число строк будет равно единице for (var i=0; i < priceLen; i++) { var t = priceData[i]; if (currentFirm == t.firm) modelSelObj.options[selLen ++] = new Option(t.device + " (" + t.cart + ")", t.p); //модель устройства (картридж), цена заправки в рублях } modelContainer.style.display = "block"; //бренд выбран, покажем список } } |
Осталось лишь что то делать при выборе конкретной модели. Привяжем к событию onchange второго списка функцию для вывода цены заправки выбранной модели картриджа.
1 2 3 4 |
function onchangeModelSelect(th) { if (th.value != "") alert("Цена заправки выбранного картриджа : " + th.value + " рублей."); } |
Я позволил себе небольшую оптимизацию для данной функции, передавая в неё как входной параметр ссылку на второй список. Давайте посмотрим как все выглядит вместе (без объявления прайса).
HTML часть:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<form> Выберите бренд: <select id="firm" onchange="onchangeFirmSelect();"> <option value="">-- бренд не выбран --</option> </select> <div id="modelCont" style="display: none;"> Выберите модель картриджа: <select id="model" onchange="onchangeModelSelect(this);"> <option value="">-- модель не выбрана --</option> </select> </div> </form> |
javascript часть:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function firm_init() { var firmSelObj = document.getElementById('firm'); var priceLen = priceData.length; var selLen = firmSelObj.options.length; var lastFirm = ""; for (var i=0; i < priceLen; i++) { var t = priceData[i]; if (lastFirm != t.firm) { firmSelObj.options[selLen ++] = new Option(t.firm, t.firm); lastFirm = t.firm; } } } function onchangeFirmSelect() { var firmSelObj = document.getElementById('firm'); var modelContainer = document.getElementById('modelCont'); var currentFirm = firmSelObj.value; if (currentFirm == "") { modelContainer.style.display = "none"; //бренд не выбран } else { var modelSelObj = document.getElementById('model'); //очистим список моделей - все, кроме первого элемента for (var i=modelSelObj.options.length-1; i > 0; i--) devElm.remove(i); //загрузим список моделей соответствующего бренда var priceLen = priceData.length; //число строк прайса var selLen = modelSelObj.options.length; //текущее число строк будет равно единице for (var i=0; i < priceLen; i++) { var t = priceData[i]; if (currentFirm == t.firm) modelSelObj.options[selLen ++] = new Option(t.device + " (" + t.cart + ")", t.p); //модель устройства (картридж), цена заправки в рублях } modelContainer.style.display = "block"; //бренд выбран, покажем список } } function onchangeModelSelect(th) { if (th.value != "") alert("Цена заправки выбранного картриджа : " + th.value + " рублей."); } firm_init(); |
Ну и для демонстрации — примерчик вживую:
А как сделать, чтобы в конце скрипт выводил не цену, а была ссылка на файл, который нужно скачать?
Я бы сделал ещё один контейнер, в котором бы формировал html ссылку на нужный файл.
Хороший примерчик, но у меня на сайте (под JOOMLa) при смене бренда не меняется модели… остаются теже что и при первом выборе… А если выбрать в бренда вы не выбрали бренд, и снова выбрать кокой нить бренд, то модели вообще больше не появляются))) В чем может быть проблема??
Попробуйте использовать какой-либо отладчик, чтобы найти в чем проблема. Судя по описанию, скрипт частично функционален, тут может быть «виноват» и браузер и какие то настройки браузера.
твой же пример работает))) Я с этим же браузером проверяю свой, и нифига) какой отладчик то примерно можно попробовать??? У меня такое оощущение что скрипт только при первом нажатии срабатывает, а при последующих выборах почему то нет…
выберите рубрику: куплюпродамсдам в арендувозьму в аренду
тип недвижимости:
—тип недвижимости не выбран—
подтип недвижимости:
—подтип недвижимости не выбран—
var Data = [{ \»tip\»: \»Жилая недвижимость\», \»pod_tip\»: \»Комната\» },
{ \»tip\»: \»Жилая недвижимость\», \»pod_tip\»: \»1-комнатная квартира\» },
{ \»tip\»: \»Жилая недвижимость\», \»pod_tip\»: \»2-х комнатная квартира\» },
{ \»tip\»: \»Коммерческая недвижимость\», \»pod_tip\»: \»офис\» },
{ \»tip\»: \»Коммерческая недвижимость\», \»pod_tip\»: \»торговая площадка\» }];
function tip_init() {
var tipSelObj = document.getElementById(\’tip\’);
var Len = Data.length;
var selLen = tipSelObj.options.length;
var lastTip = \»\»;
for (var i=0; i 0; i—) devElm.remove(i);
var Len = Data.length;
var selLen = podtipSelObj.options.length;
for (var i=0; i < Len; i++) {
var t = Data[i];
if (currentTip == t.tip)
podtipSelObj.options[selLen ++] = new Option(t.pod_tip,t.pod_tip);
}
podtipContainer.style.display = \"block\"; //бренд выбран, покажем список
}
}
tip_init();
Вот код, вроде все правильно… но не работает
podtipSelObj — не определен, судя по коду. Ну и сложно что то комментировать дальше. Зачем дважды Len, selLen объявляются? Где инициализация currentTip? Много вопросов возникает.
У тя ошибка)))) вместо devElm.remove(i); должно быть modelSelObj.remove(i); )))
Да, действительно ошибка. То ли специально ляпнул, чтобы народ подумал чуток :), то ли ошибся как то. В самом то коде правильно написано, потому и работает.
Да это просто коряво вставилось)) Половину текста урезало при вставке)))
Бывает))) Спасибо за скрипт, очень нужен был, а я с js не ахти)))
Не за что, эдакая типовая задача, часто бывает нужно что то такое сварганить.
Здравствуйте, что то не получается у меня объявить прайс, не могли бы Вы немного по подробнее объяснить, по поводу объявления прайса?
Заранее благодарен.
Что можно сказать по поводу объявления прайса… Я предполагаю, что у вас на сайте, к примеру, есть некая база данных, где хранится информация о стоимости услуг. В зависимости от структуры этой БД, а также используемого движка сайта, надстроек и т.п., а возможно, и просто написав не шибко сложную функцию, вы представляете эти данные из БД в виде кода javascript — а именно, как объявление двумерного массива. Пример такого массива вы видите в листниге.
Поможет вам такое объяснение? :)
А как сюда подключить еще 2 выпадающих окна?
«Выпадающих списка», имеете в виду? А что вас останавливает их добавить?
Да выпадающие списки пробую добавить еще 2 не получается тоесть они есть только они не работают. Подскажите пожалуйста вариант с 4 выпадающими списками.
Создавать какой то абстрактный код вещь нудная и неблагодарная. Возможно, если вы покажете что за задачу вы решаете, на каком этапе застопорились, то я смогу вам, Павел, помочь.