Динамические выпадающие списки
Существует несколько различных способов создания динамических выпадающих списков. Например многостраничная форма, когда в зависимости от выбранной опции на следующей странице с сервера загружается новый список с соответствующими значениями. Недостатки использования подобного метода очевидны: необходимо обращаться к серверу и выполнять некий скрипт, уходит время на загрузку новой страницы с новыми значениями и т.п. Можно использовать XMLHttpRequest для того, чтобы не тянуть всю страницу с сервера, а только некоторую ее часть. Однако, при его использовании также вероятны задержки загрузки нового списка опций.
Можно уменьшить время изменения значений списков, если возложить эту задачу на сторону клиента и задействовать серверный скрипт лишь для обработки уже заполненной формы. Например, можно использовать JavaScript и хранить все значения списков и отношения между ними в массиве. При этом, для избежания проблем с отключенным в браузере JavaScript придется дублировать значения списков в скрипте и html-разметке, при этом начинаются сложности с возможным изменением значений списков и увеличивается объем кода.
Однако, вышеописанных трудностей можно избежать и сделать динамически изменяемые списки, обрабатываемые на стороне клиента (браузером). При этом не требуется обращения к серверу при каждом выборе значений списка, а также значения в списках можно будет выбирать и при отключенном JavaScript. Разумеется, в таком случае нарушатся отношения между значениями, зато в списках остается возможность выбора этих значений. А это означает, что работоспособность формы сохраняется не зависимо от возможности выполнения клиентских скриптов, без затрат на соединение с сервером и выполнение серверных скриптов.
Порядок действий будет таким:
- Нужно сделать html-разметку страницы. Написать выпадающие списки (
select
). Такая страница будет почти одинаково выглядеть практически в любом браузере при соблюдении правил разметки. На этом этапе не требуется применения каких-либо действий или стилей к элементам выпадающих списков. - Теперь надо добавить к разметке несколько id и class. Это может изменить внешний вид, но еще не дает возможности производить какие-либо действия при изменении значений списков.
- Следующим этапом оставляем разметку и контент, переходим к обработчику. Берем код JavaScript, размещаем его во внешнем файле скрипта, подключаем его к странице и выполняем сразу после ее загрузки.
- Перед применением кода скрипта не забываем проверить: может ли браузер его выполнить? Т.е. проверка на поддержку браузером JavaScript/DOM. ? в лучшем случае получаем полностью работоспособные динамически изменяемые списки. В худшем случае получаем обычные не взаимосвязанные списки, которые все-равно остаются работоспособными, хотя уже не такими удобными.
Разметка - основа примера.
Сделаем два выпадающих списка: страны и города. В конце рассмотренного примера должно получиться следующее: при выборе страны в одном списке, другой будет состоять из городов только этой страны. В чистом виде разметка выглядит так:
Следующим шагом будет добавление некоторых атрибутов class
и id
элементам списков. Сперва добавим уникальные id
для select
, чтобы можно было легко обращаться к каждому из них. Затем нужно установить отношения между списками. Каждому элементу списка городов нужно добавить атрибут class
, значение которого должно быть таким же, как значение атрибута value
у элементов списка стран.
Тут следует сделать небольшое отступление на тему использования атрибута class
. Дело в том, что он, хотя и должен использоваться для связи с таблицами стилей, является единственным из возможных атрибутов элемента option
, со значением которого можно достаточно свободно обращаться. Поэтому для решения поставленной задачи с соблюдением правил разметки используется именно он.
На этом можно закончить с разметкой, которая теперь будет выглядеть так:
JavaScript - двигатель примера.
Самый простой способ динамически изменить список — скрыть ненужные и показать нужные элементы. Все было бы хорошо и просто, если бы все браузеры могли адекватно применить конструкцию style.display
для option
. Поэтому, этот вариант придется отложить до лучших времен.
Объектная Модель Документа (DOM) позволяет легко и просто обращаться к элементам разметки, производить с ними какие-нибудь действия: удалять, создавать новые, копировать, клонировать и т.п.
Клонирование, как это будет выглядеть и что с этим можно сделать? Как только страница будет загружена браузером, с помощью скрипта клонируется динамически изменяемый второй список (в этом примере - список городов) и этот клон остается в памяти. Затем при загрузке страницы (событие onload
) либо при выборе значения первого статичного списка (событие onchange
для списка стран), список городов будет удаляться и, затем, будет создаваться новый список, состоящий из городов, принадлежащих выбранной стране из первого списка. Элементы option
для динамического списка выбранных городов будут браться из клонированных ранее.
function dynamicSelect(id1, id2) {
// Сперва необходимо проверить поддержку W3C DOM в браузере
if (document.getElementById && document.getElementsByTagName) {
// Определение переменных, ссылающихся на списки
var sel1 = document.getElementById(id1);
var sel2 = document.getElementById(id2);
// Клонирование динамического списка
var clone = sel2.cloneNode(true);
// Определение переменных для клонированных элементов списка
var clonedOptions = clone.getElementsByTagName("option");
// Вызов функции собирающей вызываемый список
refreshDynamicSelectOptions(sel1, sel2, clonedOptions);
// При изменении выбранного элемента в первом списке:// вызов функции пересобирающей вызываемый список
sel1.onchange = function() {
refreshDynamicSelectOptions(sel1, sel2, clonedOptions);
}
}
}
// Функция для сборки динамического списка
function refreshDynamicSelectOptions(sel1, sel2, clonedOptions) {
// Удаление всех элементов динамического списка
while (sel2.options.length) {
sel2.remove(0);
}
var pattern1 = /( |^)(select)( |$)/;
var pattern2 = new RegExp("( |^)
(" + sel1.options[sel1.selectedIndex].value + ")( |$)");
// Перебор клонированных элементов списка
for (var i = 0; i
// Если название класса клонированного option эквивалентно "select"// либо эквивалентно значению option первого списка
if (clonedOptions[i].className.match(pattern1) ||
clonedOptions[i].className.match(pattern2)) {
// его нужно клонировать в динамически создаваемый список
sel2.appendChild(clonedOptions[i].cloneNode(true));
}
}
}
// Вызов скрипта при загрузке страницы
window.onload = function() {
dynamicSelect("Country", "City");
}
Вот собственно и все!
Живой работающий пример можно посмотреть здесь. Разумеется, все было бы не так интересно, если рассматривать возможность динамического изменения только одного списка. Логично продолжить, например, добавив третий список, как здесь. ?ли если на странице будет две пары списков, как здесь. При этом все примеры используют один и тот же JavaScript.
Спасибо Bobby van der Sluis!
У вас в алгоритме ошибочка маленькая вот тут: /examples/dynamicselect/examp1.html
Приведу алгоритм, к ней приводящий:
Выбираем в первом списке, допустим, A
Выбираем в втором списке, допустим, A-A
? в третьем списке выбираем, допустим, A-A-A
Теперь меняем значение в первом списке, например, на B
После этого третий список не «обнуляется»…
Мелочь, конечно…
Да и раз я уж тут, то хочу поблагодарить вас за ресурс. Спасибо ;)
2006-02-13 at 10:19 pm
Можно, наверно, по умолчанию прятать первый список, чтобы при отключенном JS он не мешался. А JS будет его делать видимым. Это, конечно, применимо только для данного случая (со странами и городами)…
2006-02-15 at 12:03 pm
?дея интересная, но
попытка разобраться в коде споткнулась об ошибку в строке :
(” + sel1.options[sel1.selectedIndex].value + “)( |$)”).
Если не сложно , можно ли увидеть исхоный рабочий код ?
2006-03-11 at 10:24 am
Когда списки реально содержат страны и города (а ещё могут содежать регионы), то всё это чушь.
Название поста не соответствует содержанию, это не динамические списки, что здесь подругается??
Фишка копирования тупых постов не делает чести вашему ресурсу.
2006-03-12 at 12:27 am
Для Eddi: в последнем абзаце есть ссылки на исходники.
2006-03-12 at 1:47 pm
Ваш код как раз кстати, то что я искал!
Большое спасибо Вам и вашему гуманизму, не каждый найдёт время и желание “поделиться с ближним”!
Реализация кода будет использована на сайте http://flat.net.ua
2006-03-15 at 2:51 pm
Для Fixer :
Наиболее проще обсуждать предоставленный код с отрицательной стороны, что соответствует нашему менталитету, гораздо труднее предложить свой вариант решение данного, обсуждаемого вопроса, что в итоге послужило бы аргументом для оценки Ваших способностей в данной сфере разработки. Слово – Дело!
2006-03-15 at 3:37 pm
Bond: Слушай, я позвонил по телефону 555555, и мне никто не хотел продать 222 кв.метровую квартиру за $50 штук, как же так!? Оптимистичный у Вас сайт =)
2006-04-05 at 6:55 pm
Присоеденяюсь к Бонд -, спасибо за материал! Только вот 1 вопрос есть: как данній скрипт подружить с selected? Т.е. указать, какой город, к примеру, должен выбираться по умолчанию?
2006-06-12 at 11:08 pm
Привет!
Очень долго искал что-нибудь подобное! Огромное спасибо!
Правда в процессе работы возникли такие вопросы:
1. Если второй селект имеет 200 записей (а таких селектов допустим 20) – имеем немеряное (4000) количество опшенов. В связи с этим размер страницы увеличевается многократно. Нельзя ли каким-то образом подгружать списки из файла?
2. Допустим есть три селекта. Как сделать, чтобы при выборе в первом, в двух других появлялся один и тот же набор опшенов?
Думаю данные вопросы будут актуальны для многих посетителей. Заранее благодарен за возможные варианты.
? еще хотелось бы увидеть пару других скриптов для выполнения этой же цели.
Салют!
2006-06-23 at 8:50 pm
?так, у нас будет файл jscript и html документ.
Как и на сайте, будем иметь дело со странами на материках. Но стран будет
много.
Начнём со скрипта.
?спользуя скрипт, нельзя одновременно задавать event тегу body, такие, как
onload.
Файл скрипта лучше всего подключать внутри тега head
Комментарии на английском, прошу прощения.
var contin = new Array(); //each cell will hold an array of all the
//countries in a continent
var numincont = new Array('0','51','2','47','15','42','27','15'); //number
//of countries in each continent
//prepare the continent arrays
for( var z = 1; z //tell it to set up the select menu when the page loads
window.onload = myprep;
function myprep() {
//Because Netscape 4 will act strangely on reloading . . .
if( document.mainform.Country.options.length }
//now that the document has fully loaded, take out all of the countries and
//put them into
//an array representing that continent (the continent arrays)
var y = 1; //y = number of options to bypass at the start - 1
for( var z = 1; z //each continent in turn. start at 1 because options[0] is 'Please select
//one'
for( x = 1; x //insert countries into arrays
contin[z][x] = new Option(document.mainform.Country.options[x+y].text, document.mainform.Country.options[x+y].value);
}
//offset by the number we have already done
y += numincont[z] + 1; //the 1 allows for the ' ------ Continent
//name ------' options
}
refillme();
}
function refillme() {
//erase the select menu then refill it with all countries from the selected
//continent
//the reason I deconstruct then reconstruct is to allow non JavaScript
//browsers to work
while( document.mainform.Country.options.length ) { document.mainform.Country.options[0] = 'null'; }
if( document.mainform.Continent.selectedIndex ) {
//they have selected a continent. insert a 'Please select one' option
document.mainform.Country.options[0] = new Option("Please select one","");
for( var z = 1; z //for the selected continent, put in each country
document.mainform.Country.options[z] = contin[document.mainform.Continent.selectedIndex][z];
}
//give them an 'Other' option and enable the select menu (if it was
//disabled)
document.mainform.Country.options[z] = new Option("Other (please use the box below)","Other");
document.mainform.Country.disabled = 'false';
} else {
//wait for them to select a continent
document.mainform.Country.options[0] = new Option("Please select an area above","");
document.mainform.Country.disabled = 'true';
}
document.mainform.Country.options[0].selected = 'true';
document.mainform.other.disabled = 'true';
}
function ableother() {
//If they have selected "other", enable the "other" box
if( document.mainform.Country.options.length > 1 && document.mainform.Country.selectedIndex == document.mainform.Country.options.length - 1 ) {
document.mainform.other.disabled = 'false';
} else {
document.mainform.other.disabled = 'true';
}
}
/'_______________________________________________________________________________________
The continent menu:
The countries menu:
The 'other' box
'/
Далее следует форма, куда и войдут представленные выше silect’ы:
ttp://w3.org/TR/html4/strict.dtd%22>" rel="nofollow">http://w3.org/TR/html4/strict.dtd">
form method="get" action="/tutorials/">
Site navigation
Details and download
selector