FreeArtists.ruFreeartists статьи и рецензии о технологиях

AJAX — реализация связанных списков

Эта статья рассказывает о том, как с помощью технологии AJAX сделать связанные списки удобными для пользователя.

Связанные списки это два или более списка. Выбор значения в одном из них влияет на содержание остальных.

Такие списки очень удобны, если вам надо выбрать некий объект с определенными характеристиками.

Пример

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

пример 1

Почему это важно

Самым простым было бы показывать пользователю два списка. В первом — все модели машин, во втором — все цвета, независимо от их наличия.

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

Варианты реализации связанных списков

Для написания примеров мы будем использовать XHTML, JavaScript и PHP.

Классическая реализация

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

Вместо базы данных мы будем использовать PHP массивы c набором цветов для каждой модели.

Пример кода

<form action="#" method="get">
<fieldset>
<legend><label for="models">Модели</label></legend>
<select id="models" name="models">
	<option value="BentleyAzure">Bentley Azure</option>
	<option value="ChevroletCorvette">Chevrolet Corvette</option>
	<option value="FerrariEnzo">Ferrari Enzo</option>
</select>
</fieldset>
<?php
	// Проверка, пришли ли данные из формы.
	if($_GET["models"]){
		// Как бы выборка из базы данных.
		$colorsArray = array(
			"BentleyAzure"=>array("red"=>"Красный","green"=>"Зеленый"),
			"ChevroletCorvette"=>array("black"=>"Черный","blue"=>"Синий"),
			"FerrariEnzo"=>array("green"=>"Зеленый","black"=>"Черный","yellow"=>"Желтый")
		);
		$colors = $colorsArray[$_GET["models"]];
?>
<fieldset>
<legend><label for="colors">Цвета</label></legend>
<select id="colors" name="colors">
<?php
		// Выводим список доступных цветов.
		foreach($colors as $key => $value){
			echo '<option value="',$key,'">',$value,'</option>';
		}
?>
</select>
</fieldset>
<?php } ?>
<fieldset>
	<input type="submit" value="Выбрать" />
</fieldset>
</form>

пример 2

Недостатки такого подхода

  1. Пересылка клиенту всего HTML кода страницы вместо списка цветов имеющихся в наличии.
  2. Пользователю придется ждать некоторое время, пока данные придут с сервера и отрендерятся в браузере.

JavaScript реализация

Естественно, хотелось бы, не заставляя пользователя долго ждать, менять содержимое списка с цветами динамически.

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

Пример кода

<head>
<title>Связанные списки, пример № 3</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<style type="text/css">
	fieldset{width:10em;display:block;padding:.5em}
	select{width:10em}
	#colors{display:none}
</style>
<script type="text/javascript">
// Данные, как бы из базы, сформированные на стороне сервера.
// Обычно находятся в подключаемом JavaScript файле.
var colorsArray = {
	BentleyAzure : {red:"Красный",green:"Зеленый"},
	ChevroletCorvette : {black:"Черный",blue:"Синий"},
	FerrariEnzo : {green:"Зеленый",black:"Черный",yellow:"Желтый"}
}
// Функция, заполняющая список цветами доступными для выбранной модели. 
function getColors(_this){
	var colors = colorsArray[_this.value];
	var _select = document.getElementById("colors");
	_select.innerHTML = ""; // Удаляем всех потомков.
	for(var i in colors){ // Добавляем доступные цвета.
		var option = document.createElement("option");
		var optionText = document.createTextNode(colors[i]);
		option.appendChild(optionText);
		option.setAttribute("value",i);
		_select.appendChild(option);
	}
	// Делаем список цветов видимым.
	_select.style.display="inline";
}
</script>
</head>
<body>
<form action="#" method="get">
<fieldset>
<legend><label for="models">Модели</label></legend>
<select id="models" name="models" onchange="getColors(this)">
	<option value="BentleyAzure">Bentley Azure</option>
	<option value="ChevroletCorvette">Chevrolet Corvette</option>
	<option value="FerrariEnzo">Ferrari Enzo</option>
</select>
</fieldset>
<fieldset>
<legend><label for="colors">Цвета</label></legend>
<select id="colors" name="colors"></select>
</fieldset>
<fieldset>
	<input type="submit" value="Выбрать" />
</fieldset>
</form>
</body>

пример 3

Недостатки такого подхода

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

AJAX реализация

С точки зрения usability и минимизации количества данных передаваемых от сервера клиенту, наилучшей будет AJAX реализация.

Для этого мы напишем небольшое JavaScript приложение, которое в ответ на выбор определенной модели, будет динамически менять содержание списка с цветами, загружая данные о цветах с сервера, используя AJAX. Но, сначала, сделаем так, чтобы все цвета присутствовали в списке и напишем проверку с помощью PHP — на случай, если пользователь выберет несуществующую комбинацию. Это нужно для того, чтобы пользователь в любом случае имел возможность выбора, даже если его браузер не поддерживает JavaScript или AJAX.

Пример кода

<body>
<?php
	// Проверка, пришли ли данные из формы.
	if($_GET["models"]){
		// Как бы выборка из базы данных.
		$colorsArray = array(
			"BentleyAzure"=>array("red"=>"Красный","green"=>"Зеленый"),
			"ChevroletCorvette"=>array("black"=>"Черный","blue"=>"Синий"),
			"FerrariEnzo"=>array("green"=>"Зеленый","black"=>"Черный","yellow"=>"Желтый")
		);
		$colors = $colorsArray[$_GET["models"]];
		// Проверка на доступность модели этого цвета.
		if(array_key_exists($_GET["colors"],$colors)){
			echo '<h3 style="color:#090">Модель такого цвета есть в наличии</h3>';
		}else{
			echo '<h3 style="color:#900">Модель такого цвета отсутствует</h3>';
		}
	}
?>
<form action="#" method="get">
<fieldset>
<legend><label for="models">Модели</label></legend>
<select id="models" name="models">
	<option value="BentleyAzure">Bentley Azure</option>
	<option value="ChevroletCorvette">Chevrolet Corvette</option>
	<option value="FerrariEnzo">Ferrari Enzo</option>
</select>
</fieldset>
<fieldset>
<legend><label for="colors">Цвета</label></legend>
<select id="colors" name="colors">
	<option value="red">Красный</option>
	<option value="black">Черный</option>
	<option value="green">Зеленый</option>
	<option value="blue">Синий</option>
	<option value="yellow">Желтый</option>
</select>
</fieldset>
<fieldset>
	<input type="submit" value="Выбрать" />
</fieldset>
</form>
</body>

пример 4

Теперь мы обеспечили доступность нашего интерфейса пользователям тех браузеров, где отключен JavaScript или не поддерживается AJAX.

Напишем небольшой PHP скрипт, отдающий по AJAX запросу доступные цвета машин в формате XML.

Пример кода

<?php
header("Content-Type: text/xml; charset=windows-1251");
$request = $_GET["models"];
if($request){
	echo loadData($request);
}
function loadData($request){
	// Функция, как бы делающая выборку из базы.
	if($request == "BentleyAzure"){
		$result = '<color value="red">Красный</color><color value="green">Зеленый</color>';
	}elseif($request == "ChevroletCorvette"){
		$result = '<color value="black">Черный</color><color value="blue">Синий</color>';
	}elseif($request == "FerrariEnzo"){
		$result = '<color value="green">Зеленый</color><color value="black">Черный</color>
				   <color value="yellow">Желтый</color>';
	}
	if($result){
		return $result = '<?xml version="1.0" encoding="windows-1251"?><colors>'.$result.'</colors>';
	}
}
?>

И теперь, собственно, AJAX. Наше AJAX приложение будет состоять из двух частей: первая часть будет выполнять AJAX запрос к написанному ранее скрипту ajax.php, а вторая часть обрабатывать полученные данные и записывать их в список цветов.

Для получения данных от сервера мы используем объект XMLHttpRequest. Проблема в том, что его синтаксис и название в разных браузерах неодинаковы. Поэтому мы воспользуемся готовой функцией, предоставляющей кроссбраузерное решение работы с этим объектом.

Пример кода

// Функция, осуществляющая AJAX запрос.
function loadXMLDoc(method,url){
    if(window.XMLHttpRequest){
        req = new XMLHttpRequest();
        req.onreadystatechange = processReqChange;
        req.open(method, url, true);
        req.send(null);
    }else if(window.ActiveXObject){
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if(req){
            req.onreadystatechange = processReqChange;
            req.open(method, url, true);
            req.send(null);
        }
    }
}
// Функция, выполняемая при изменении статуса
// запроса, если статус  равен 200, данные получены.
function processReqChange(){
    if(req.readyState == 4){
        if(req.status == 200){
			getColors(req.responseXML.documentElement);
        }else{
            alert("There was a problem retrieving the XML data:\n" + req.statusText);



        }
    }
}

Не будем вдаваться в подробности реализации AJAX запроса. Укажем лишь, что функция loadXMLDoc осуществляет запрос HTTP методом, переданным ей в качестве параметра по указанному URL. Функция processReqChange выполняется как только придет ответ от сервера. В случае, если данные пришли и HTTP статус равен 200 т. е. «OK», данные передаются в виде XML в функцию getColors.

Осталось только распарсить XML данные с сервера, и вписать их в список доступных цветов.

Пример кода

<head>
<title>Связанные списки, пример № 5</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251" />
<style type="text/css">
	fieldset{width:10em;display:block;padding:.5em}
	select{width:10em}
</style>
<script type="text/javascript">
// Функция, осуществляющая AJAX запрос.
function loadXMLDoc(method,url){
    if(window.XMLHttpRequest){
        req = new XMLHttpRequest();
        req.onreadystatechange = processReqChange;
        req.open(method, url, true);
        req.send(null);
    }else if(window.ActiveXObject){
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if(req){
            req.onreadystatechange = processReqChange;
            req.open(method, url, true);
            req.send(null);
        }
    }
}
// Функция, выполняемая при изменении статуса
// запроса, если статус  равен 200, данные получены.
function processReqChange(){
    if(req.readyState == 4){
        if(req.status == 200){
			getColors(req.responseXML.documentElement);
        }else{
            alert("There was a problem retrieving the XML data:\\n" + req.statusText);
        }
    }
}
function onChange(_this){
	var url = "ajax.php?models="+_this.value;
	loadXMLDoc("get",url);
}
function getColors(xml){
	var colors = xml.getElementsByTagName("color");
	var _select = document.getElementById("colors");
	_select.innerHTML = ""; // Удаляем всех потомков.
	// Создаем список с доступными цветами.
	for(i=0;i<colors.length;i++){ 
		var option = document.createElement("option");
		var optionText = document.createTextNode(colors[i].firstChild.data);
		option.appendChild(optionText);
		option.setAttribute("value",colors[i].getAttribute("value"));
		_select.appendChild(option);
	}
}
</script>
</head>
<body>
<?php
	// Проверка, пришли ли данные из формы.
	if($_GET["models"]){
		// Как бы выборка из базы данных.
		$colorsArray = array(
			"BentleyAzure"=>array("red"=>"Красный","green"=>"Зеленый"),
			"ChevroletCorvette"=>array("black"=>"Черный","blue"=>"Синий"),
			"FerrariEnzo"=>array("green"=>"Зеленый","black"=>"Черный","yellow"=>"Желтый")
		);
		$colors = $colorsArray[$_GET["models"]];
		// Проверка на доступность модели этого цвета.
		if(array_key_exists($_GET["colors"],$colors)){
			echo '<h3 style="color:#090">Модель такого цвета есть в наличии</h3>';
		}else{
			echo '<h3 style="color:#900">Модель такого цвета отсутствует</h3>';
		}
	}
?>
<form action="#" method="get">
<fieldset>
<legend><label for="models">Модели</label></legend>
<select id="models" name="models" onchange="onChange(this)">
	<option value="BentleyAzure">Bentley Azure</option>
	<option value="ChevroletCorvette">Chevrolet Corvette</option>
	<option value="FerrariEnzo">Ferrari Enzo</option>
</select>
</fieldset>
<fieldset>
<legend><label for="colors">Цвета</label></legend>
<select id="colors" name="colors">
	<option value="red">Красный</option>
	<option value="black">Черный</option>
	<option value="green">Зеленый</option>
	<option value="blue">Синий</option>
	<option value="yellow">Желтый</option>
</select>
</fieldset>
<fieldset>
	<input type="submit" value="Выбрать" />
</fieldset>
</form>
</body>

пример 5

Это AJAX приложение работает в браузерах IE 5+, Opera 9+ и FF 1+.

43 комментария

  1. Владислав

    Здравствуйте!
    Спасибо за статью!
    Можете пояснить каким образом сделать так что бы при выборе элемента из выпадающего списка (это действие реализовано с помощью вашей статьи), не открывался новый элемент , а открывалось поле ввода текста (<input type=»»text…)?

    12 января 2009, 10:47
  2. Sq.Piglet

    JavaScript:
    function getColors(){
    var conteiner = document.getElementById(«conteiner»);
    if(conteiner.getElementsByTagName(«input»)[0]) conteiner.removeChild(conteiner.getElementsByTagName(«input»)[0]);
    var input = document.createElement(«input»);
    input.setAttribute(«value»,»текст»);
    conteiner.appendChild(input);
    }

    Замените слово «текст», на данные с сервера.

    HTML:

    <select id=»colors»… меняем на <div id=»conteiner»></div>

    12 января 2009, 12:37
  3. Владислав

    Благодарю!

    12 января 2009, 13:56
  4. Владислав

    Извините, меня за бестактность.
    Можете еще подсказать как реализовать следующее:
    При выборе из списка нужно что бы при выборе разных параметров параметр
    </select id="models" name="models" onchange=»»
    Менял свое значение в зависимости от выбранного из списка пункта.
    Еще раз извиняюсь за наглость.

    12 января 2009, 19:45
  5. Sq.Piglet

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

    12 января 2009, 20:03
  6. Владислав

    Я Вас понимаю можете посоветовать хороший учебник так сказать, просто я порылся — ничего толкового не нашол.

    12 января 2009, 20:14
  7. Sq.Piglet

    AJAX в действии

    12 января 2009, 20:21
  8. Владислав

    Здравствуйте, жутко извиняюсь все сделал кроме вот этого:

    Есть ссылка вида:
    Подробнее…

    При нажатии на которую запускается скрипт:

    function showLogin() {

    document.getElementById(‘login’).style.display = ‘block’;

    }

    Подскажите пожалусто как значение 34 (в onclick=showLogin(34)) передать в php скрипт?

    19 января 2009, 20:43
  9. Sq.Piglet

    Добавите в адрес вызова AJAX’ом PHP скрипта параметр. var url = «ajax.php?models=»+_this.value+»&param_name=»+34;. Тогда в PHP в массиве $_GET[«param_name»] будет значение вашего параметра.

    19 января 2009, 20:58
  10. Владислав

    Благодорю!
    Огромное спасибо!

    19 января 2009, 21:00
  11. Андрей

    Здравствуйте!
    У меня такой вопрос с помощью AJAX я делаю динамические селекты со странами, регионами и городами, мне нужно выделить столицы красным цветом, для добавления опшинов я использую следующий код:

    function addOption (oListbox, text, value, isDefaultSelected, isSelected, highLight)
    {
    var oOption = document.createElement(«option»);
    oOption.appendChild(document.createTextNode(text));
    oOption.setAttribute(«value», value);

    if(highLight) oOption.style=»color:red»;

    if (isDefaultSelected) oOption.defaultSelected = true;
    else if (isSelected) oOption.selected = true;

    oListbox.appendChild(oOption);
    }
    к сожалению он корректно работает только в Опере9. IE6 и Mozzila виснут на строчке:
    if(highLight) oOption.style=»color:red»;
    Можно ли эту ошибку как ни будь обойти, или я неправильно задаю стиль для option?

    20 января 2009, 0:54
  12. Sq.Piglet

    Попробуйте так: oOption.style.color=»#f00″;

    20 января 2009, 9:55
  13. atuego

    Я сделал это!!!!!
    К большому сожалению полный 0 в JS и AJAX, но общий принцип работы скрипта понял. Теперь МОЯ модификация работает!
    Спасибо!

    29 января 2009, 18:16
  14. Виталий

    Спасибо за информацию, очень полезно и интересно, потому что подобную задачу сейчас решаю. Могу ли я задать вопрос? если я всё правильно понял, то данные берутся из XML, а можно ли это исключить и взять их из MySQL, если да то как это реализовать ..?
    Буду признателен если подскажете, нет — тоже не обижусь.

    31 марта 2009, 15:01
  15. Sq.Piglet

    Можно. Для этого вам на PHP написать прокладку, которая будет делать запросы к БД и отдавать данные в виде XML.

    31 марта 2009, 16:12
  16. Иван

    Здравствуйте!
    Я полный ноль по этой теме, но надо сделать такое чудо.
    Подскажите, пожалуйста, пример 5 он состоит из одного документа или нет, а то у меня пишет ошибку: «There was a problem retrieving the XML data:\nNot Found».
    Какой код в этом документе надо разместить?
    Заранее благодарен

    19 апреля 2009, 10:52
  17. Иван

    Спасибо огромное за статью, очень выручила.
    Во всем разобрался, и книга классная

    21 апреля 2009, 10:56
  18. Алексей

    Спасибо, наконец-то начал въезжать в аякс. Пытаюсь написать подобное для n-числа связанных селектов. Вот в этом месте
    req.onreadystatechange = processReqChange;
    не могу понять как в функцию processReqChange мне еще одну переменную передать, что-то вроде req.onreadystatechange = processReqChange(selectname) — это не работает, я может вообще ересь какуюто спрашиваю, в js слабо разбираюсь. Не подскажите заранее благодарен

    1 июня 2009, 21:28
  19. Дмитрий

    Думаю, что вопрос давно решенный, но все же напишу ответ:

    при перехвате каких либо событий в javascript используется либо псевдоним
    функции(как в примере) либо функция генерируется «на лету» как анонимная

    пример:
    req.onreadystatechange = function(){тело функции};
    Естественно всю функцию по понятным причинам вы так писать не будете,

    я выхожу из ситуации всегда очень просто и со вкусом так сказать)

    req.onreadystatechange = function(){processReqChange(selectname)};

    25 июня 2009, 2:24
  20. Владимир

    Код из примера 5 не работает в IE v 7.0
    Выдает
    Строка 41 символ 2
    «null» — есть null или не является объектом

    20 июля 2009, 11:19
  21. Владимир

    Извиняюсь, не обновился кэш IE

    20 июля 2009, 11:22
  22. Герман

    Большое спасибо за статью — очень хорошо и пошагово описана работа со связанными списками. Можете добавить в статью, что пример 5 работает и на Google Chrome :)

    23 июля 2009, 9:34
  23. Nayre

    Нормальный пример, хотя я и редко пользуюсь ajax в основном для написания CMS. Но сказать хочу не об этом
    цитата:
    // Функция, как бы делающая выборку из базы.
    не лучше ли jav-ой on change получать выбраный элемент и тоже запрашивать данные из базы (т.е. изначально элемент пуст) ведь наличие марки автомобиля тоже может менятся ;)
    ЗЫ конечно это несколько усложнит пример

    14 сентября 2009, 21:48
  24. Александр

    Добрый день.

    у меня вопросю. все работает. Нопочему то после выбора сбивается кодировка. т.е. цвета отображаются кракозяблей… хотя в кодах прописан чар сет наш

    10 февраля 2010, 15:30
  25. Sq.Piglet

    Если на стороне сервера и клиента одинаковая кодировка, проблем быть не должно. Обратитесь к вашему серверному приложению на прямую и посмотрите в какой кодировке оно отдает XML.

    10 февраля 2010, 15:42
  26. Юрий

    utf-8 вместо windows-1251 и проблем с кракозябриками не будет.
    У меня вопрос= пытаюсь запустить этот скрипт на локальном xammp’e — не работает, пробовал также разные скрипты с Ajax’ом с других сайтов — ни одни не пошел. Может быть что у меня что-то на сервере отключено, что HTTPRequest не работает? или в чем проблема?

    14 февраля 2010, 0:59
  27. Sq.Piglet

    Все должно работать. Это обычное HTTP взаимодействие. Проверяете вручную адреса, по которым ходит AJAX.

    14 февраля 2010, 16:42
  28. Вартан

    Спасибо за статью

    7 марта 2010, 2:21
  29. Олеся

    добрый день,
    а как мне вместо перечисления вручную цветов для каждой можелит сделать выборку из базы? Я прописываю это в файле где формат xml, ничего не поулчается, как я поняла, там запросы выполнять нельзя..

    18 марта 2010, 18:33
  30. Павел

    День добрый! Такой вопрос — копирую пример №5 на сервер, при попытке выбора марки автомобиля он пишет — There was a problem retrieving the XML data:\\n

    что надо сделать чтобы запустить ?

    16 апреля 2010, 22:23
  31. Sq.Piglet

    По адресу ajax.php должен лежать файл, генерирующий XML. Смотри пример выше «Напишем небольшой PHP скрипт, отдающий по AJAX запросу доступные цвета машин в формате XML».

    17 апреля 2010, 11:30
  32. denis

    Народ скиньте архивчик с пятым примером, вродебы всё норм сделал, а цвета не подгружаются… если можно на депозит пожалуйста…

    28 апреля 2010, 0:38
  33. Richi

    Спасибо большое за статью. Всё понятно и просто достаточно написано!

    23 августа 2010, 20:12
  34. Мужик

    Очень полезно. Спасибо.

    3 сентября 2010, 10:52
  35. Mich

    что то в примерах одни ошибки…

    1 декабря 2010, 13:59
  36. Sq.Piglet

    Ошибки исправил. Это следствие переезд на новый хостинг.

    1 декабря 2010, 14:25
  37. Piv

    Подскажите пожайлуста. в чём проблема, по примеру 5. списки работают на виртуалке отлично и в мозиле и в IE, переношу на основной сайт, мозила работает, ИЕ ни в какую, как я понял вообще не срабатывает Change, реакции никакой, даже ошибки…

    24 декабря 2010, 14:00
  38. Piv

    з.ы. дословно пример работает везде, поэтому следовало бы искать ошибку у себя, но на виртуалке же всё работает, да и на основном в мозиле…уже не знаю что и думать.

    24 декабря 2010, 14:06
  39. Sq.Piglet

    Ну, например, сервер отдает не правильную кодировку для XML или не правильный тип (должно быть text/xml).

    Поставьте FireBug в FF и займитесь дебагингом.

    24 декабря 2010, 17:31
  40. Роман

    Добрый день как можно сделать несколько таких списков например 4-5?
    с запросом данных из одной таблицы SQL в 4-5 столбцов

    9 марта 2011, 23:20
  41. Александр

    где можно скачать исходники примеров?

    29 августа 2011, 17:59
  42. Аноним

    статья устарела

    4 января 2012, 6:48
  43. Дмитрий

    Подскажите пожалуйста, как можно при нажатии кнопки отправить сделать так чтобы переходило по определенной ссылке — в зависимости от выбранной модели и цвета?

    24 октября 2012, 11:46

RSS комментариев