тег option <<многоуровневый>>
Добрый день!
Возможно кто то подскажет.... Задача: Нужно реализовать такое... при загрузке страницы высвечивается два поля <option></option>, поумолчанию второе неактивно к примеру в первом поле название страны во стором название городов, нужно при выборе страны в первом списке чтобы второй становился активным и в нем вываливались города из этой страны. Проблемы: все данные динамичны находятся в базе mysql код страницы генерируется в php соответственно данные из базы выводятся в цикле, примеров в сети оч много но с ними проблема именно из за цикла php подскажите пожалуйста вариант решения вопроса, сам неосилю :((( |
Цитата:
Или я неправилно понял. Другое дело, что это не самое оптимальное решение. ИМХО, наилучший вариант(в случае большого количества стран и городов), это на событие изменения первого optiona повесить Ajax-функцию, которая подгружает список городов во второй option, и делает его доступным. также, для ускорения стоит настроить кэширование этих запросов, с помощью ХТТП-заголовков. Если список городов постоянно изменяется, тогда делаем кеширование как описано в статье Умное Кеширование и Версионность в Javascript/CSS |
Традиционно ваша задача решается так.
Есть два списка select: <!-- Вызываем при изменении нашу функцию --> <select name="contry" onChange="loadList(this)"> <option value="" selected="selected"></option> <option value="rus">Россия</option> <option value="usa">США</option> </select> <select disabled="disabled" name="city" id="cityList"> <option></option> </select> Пишется код, работающий по концепции AJAX: // Создаём объект асинхронной загрузки с сервера. var HTTPReq = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); // Устанавливаем обработчик ответа от сервера: HTTPReq.onreadystatechange = function() { // Если загружено полностью: if(HTTPReq.readyState == 4) { // Обрабатываем ответ: var Cities = eval("("+HTTPReq.responseText+")"); // Заполняем список: fillCityList(Cities); } } // Запоминаем выбранную страну, чтоб не // дублировать запросы в будущем. var selectedContry = null; // Функция, вызываемая при изменении списка // (при выборе страны) function loadList(contryList) { // Если выбран "пустой" вариант - очищаем список: if(contryList.value === "") { clearCityList(); return; } // Проверяем выбрана ли уже какая-то страна: if(selectedContry !== null) { // Если выбрана, проверяем не текущая ли выбрана: if(selectedContry == contryList.value) { // Если текущая уже выбрана - делать нечего. return; } } // Если никакая не выбрана, или выбрана не текущая, ставим текущую. selectedContry = contryList.value; // Далее запрашиваем сервер на предмет списка городов выбранной страны. // Тут должна быть ваша страница php, её работа будет описана далее: HTTPReq.open("GET", "page.php?country="+contryList.value); HTTPReq.send(null); // После этого будет вызван обработчик (который мы писали ранее, так что выходим) return true; } // Фукнция, которая добавляет элементы в список городов: function fillCityList(elements) { // elements передан в формате JSON, так что щас он - объект // для начала очистим текущий список городов clearCityList(); // Уберём статус неактивен с элемента: var cityList = document.getElementById("cityList"); cityList.disabled = false; // Скидываем счётчик option'ов (1 - потому что первый элемент пуст) var i = 1; // Рассмотрим elements, в котором ключи это values, а значения - имена. for(var key in elements) { // Создаём и добавляем элемент в список: var City = new Option(elements[key], key, false, false); cityList.options[i++] = City; } // Всё. return true; } // Функция, которая очищает все элементы из второго списка: function clearCityList() { var cityList = document.getElementById("cityList"); // Такое очищение, походу, работает во всех браузерах нормально. while (cityList.length > 0) cityList.options[0] = null; cityList.options[0] = new Option('', 0, false, false); // Установим его в неактивный cityList.disabled = "disabled"; } PHP скрипт должен найти в базе данных городов, города из данной страны, и возвратить их назад в воспринимаемом для JavaScript виде. Лучше всего, если это будет JSON. Т.е ответ должен выглядить так: {"new_york": "New York", "dallas": "Dallas", "city": "city"} Допустим база данных у вас такая: | identy | name | contry | ------------- | spb | Санкт-Петербург | rus | | spb | Санкт-Петербург | rus | | ny | Нью-Йорк | usa | Тогда PHP скрипт должен быть примерно такой: <?php // Если не получен параметр - выходим if(!array_key_exists('contry', $_GET)) die(); // Подключаемся к БД mysql_connect('localhost', 'login', 'passwd'); mysql_select_db('database'); // Составляем безопасный запрос: $query = sprintf("SELECT identy, name FROM cities_list WHERE country='%s'", mysql_real_escape_string($_GET['country'])); // Выполняем запрос: $result = mysql_query($query); $output = array(); // Проходим циклом по результату while($row = mysql_fetch_assoc($result)) { $output[] = '"'.addslashes($row['identy'].'": "'. '"'.addslashes($row['title']).'"'; } // Выводим, если есть что выводить: if(sizeof($output) > 0) { echo '{'. implode(',', $output) .'}'; } |
Спасибо огромное за детальный ответ, в самом скрипте пару мелких ошибочек исправил, но к сожалению он не работает :(((
хотя если обращаюсь напрямую к старнице по типу: HTTPReq.open("GET", "page.php?country="+contryList.value); тоесть обращаюсь: page.php?country=1 то просто в тексте страницы просто высвечивается информация в таком виде: {"new_york": "New York", "dallas": "Dallas", "city": "city"} а список второй селект как был неактивным так и остался :((( |
DieseL,
Что-то я не понял. По адресу "page.php?country=1" и должен высвечиваться JSON ответ - это данные для JavaScript скрипта. Селект должен изменятся только на html странице, без перезагрузки страницы. А код должен быть рабочим, я вроде тестил. В общем, щас гляну еще раз, из-за чего может не работать. |
Цитата:
|
Ужасная новость :(((( Скрипт корректно работает только в Opera. В IE6, IE7, Mozilla скрипт неработает:(((
Что нетак? |
DieseL,
Попробуйте заменить: // Создаём объект асинхронной загрузки с сервера. var HTTPReq = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); На это: try { var HTTPReq = new XMLHttpRequest(); } catch(e) { var HTTPReq = new ActiveXObject("Microsoft.XMLHTTP"); } |
Цитата:
<meta http-equiv="cache-control" content="no-cache"> а может заголовок документа влиять на корректность работы скрипта в ИЕ??? заголовок у меня такой: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> |
DieseL,
Только что проверил код в Opera, Firefox, Ie 5, Ie5.5, Ie6. Везде работало (после замены из предыдущего сообщения). Какие именно вы изменения сделали в скрипте, и выводится ли ошибка в Ie (слева снизу восклицательный знак). Не работает частично или полностью? |
Цитата:
подключаю его так: <script type="text/javascript" src="city.js"></script> скрипт начинался: var HTTPReq = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); изменил на: try { var HTTPReq = new XMLHttpRequest(); } catch(e) { var HTTPReq = new ActiveXObject("Microsoft.XMLHTTP"); } После изменений ,Опера 9.27 как работала корректно так и работает, а вот ИЕ7 при загрузке страницы - все ок, выбираю страну, он активирует селект с городами, но если я меняю страну список уже не обновляется, при этом ИЕ7 ошибок не выдает никаких, как будто все в порядке, просто данные не меняются :((( Возможно нужно какие то дополнительные заголовки добавлять? по типу нокеш или что то ещё? |
DieseL,
Попробуйте заменить строчку HTTPReq.open("GET", "page.php?country="+contryList.value); На HTTPReq.open("GET", "page.php?country="+contryList.value+"&rand="+Math.random()); |
Андрей Параничев,
Спасибо огромное! Решил вопрос сам :) но не без Вас! З.ы. Если кому будет нужно/полезно/интересно говорите, пример скрипта выложу :) |
Андрей Параничев,
Скажите, а возможно ли как то модифицировать код чтобы при выборе во втором селекте пункт "Другой город", вместо селекта появлялась форма <input type=text> и внесенные в нее данные отправлялись на сервер в таком же виде как запрос на выборку? P/s/ ну а там уже скрипт пхп который добавляет в базу новый город... |
Уважаемый, Андрей Параничев!
Подскажите пожалуйса почему невыходит две подобные формы на одной страницы установить? Они конфликтуют друг с другом, естевственно имена функций/елементов изменены на другие во второй форме :(( |
Цитата:
|
ВотЪ (:
Файл index.php <script type="text/javascript" src="district.js"></script> Область: <select name="Area" style="width:130px" onchange="AreaList(this)"> <option value="0" selected="selected">Выбор</option> <option value="1">Крым</option> <option value="2">Киевская</option> <option value="3">Волынская</option> <option value="4">Одесская</option> </select> Районный центр: <select disabled="disabled" name="District_List" style="width:130px" id="District_List"> <option>Выбор</option> </select> Файл district.js function District_makeRequest(url) { http_request = false; if (window.XMLHttpRequest) { // Mozilla, Safari,... http_request = new XMLHttpRequest(); if (http_request.overrideMimeType) { http_request.overrideMimeType('text/xml'); } } else if (window.ActiveXObject) { // IE try { http_request = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { http_request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } } if (!http_request) { alert('Не получается отправить XMLHTTP запрос'); return false; } http_request.onreadystatechange = alertContents; http_request.open('GET', url, true); http_request.send(null); } function alertContents() { if (http_request.readyState == 4) { var Districties = eval("("+http_request.responseText+")"); // Заполняем список: fillDistrict_List(Districties); } } // Запоминаем выбранную страну, чтоб не // дублировать запросы в будущем. var selectedArea = null; // Функция, вызываемая при изменении списка // (при выборе страны) function AreaList(AreaList) { // Если выбран "пустой" вариант - очищаем список: if(AreaList.value === "") { clearDistrict_List(); return; } // Проверяем выбрана ли уже какая-то страна: if(selectedArea !== null) { // Если выбрана, проверяем не текущая ли выбрана: if(selectedArea == AreaList.value) { // Если текущая уже выбрана - делать нечего. return; } } // Если никакая не выбрана, или выбрана не текущая, ставим текущую. selectedArea = AreaList.value; // Далее запрашиваем сервер на предмет списка городов выбранной страны. // Тут должна быть ваша страница php, её работа будет описана далее: District_makeRequest("district_select.php?district="+AreaList.value+"&rand="+Math.random()); // После этого будет вызван обработчик (который мы писали ранее, так что выходим) return true; } // Фукнция, которая добавляет элементы в список городов: function fillDistrict_List(elements) { // elements передан в формате JSON, так что щас он - объект // для начала очистим текущий список городов clearDistrict_List(); // Уберём статус неактивен с элемента: var District_List = document.getElementById("District_List"); District_List.disabled = false; // Скидываем счётчик option'ов (1 - потому что первый элемент пуст) var i = 1; // Рассмотрим elements, в котором ключи это values, а значения - имена. for(var key in elements) { // Создаём и добавляем элемент в список: var District = new Option(elements[key], key, false, false); District_List.options[i++] = District; } return true; } // Функция, которая очищает все элементы из второго списка: function clearDistrict_List() { var District_List = document.getElementById("District_List"); // Такое очищение, походу, работает во всех браузерах нормально. while (District_List.length > 0) District_List.options[0] = null; District_List.options[0] = new Option('', 0, false, false); // Установим его в неактивный District_List.disabled = "disabled"; } Файл district_select.php if(!array_key_exists('district', $_GET)) die(); // Подключаемся к БД mysql_connect("$user_host", "$user_name", "$user_pass"); mysql_select_db ("$user_base"); // Составляем безопасный запрос: $query = sprintf("SELECT * FROM district WHERE area='%s'", mysql_real_escape_string($_GET['district'])); // Выполняем запрос: $result = mysql_query($query); $output = array(); // Проходим циклом по результату while($row = mysql_fetch_assoc($result)) { $output[] = '"'.addslashes($row['id']).'": '. '"'.addslashes($row['name']).'"'; } // Выводим, если есть что выводить: if(sizeof($output) > 0) { echo '{'. implode(',', $output) .'}'; } |
Можно ли как то запихнуть в select option не используя при этом цикл на JavaScript. Например при клике на элемент select c сервера должны загрузиться значения options. Их много. Перебор в цикле ( new Options) займёт много времени. А innerHTML и outerHTML с select не работают корректно. И ещё вопрос такой: почему в ваших примерах используете HttpRequest ? У меня чешутся руки написать загрузку option-сов через динамически созданный тэг script. Будет ли это иметь какие нибудь негативные последствия ?
|
ну как бы пример добавления оптионсов на форуме точно есть, именно это я когдато уже спрашивал.. юзайте поиск
можно просто динамически создавать оптионсы |
Проблема
Попробовал реализовать предложенный код - в результате во втором окне select появляется только одно значение, хотя в базе их много.
Подскажите, в чем может быть загвоздка |
Без кода проблемного места подсказать не получится.
|
Закинул все три файла: index.php, district.js, district_select.php на сервер, в файле district_select.php изменил подключение к БД под свои данные, при запросе к базе district сменил на своё поле. Пока не работает... Что-то ещё нужно откорректировать? Спасибо.
|
А никто не подскажет - есть ли какойнибудь сервис, который по запросу страны выдает в xml или json области этой страны и города? Тот же могучий Google?
|
пока нарыл это - http://ws.geonames.org/search?countr...ureCode=A DMD
выдает регионы России. Но нифига не на русском (( И непонятно как запросить города отдельного региона. И желательно указать какую инфу хочу получить. |
danik.js,
вы ошиблись адресом это форум по JS, а не поисковая система введите пожалуйста в адресной строке http://google.ru/, и ищите там. до свидания |
нет ну почему же.. это всётаки по теме...
у гугла есть такой инструмент есть даже реализация твоей задачи на jQuery плагин называется geo-autocomplete http://code.google.com/p/geo-autocomplete/ |
Проблема с имплементацией
Пытаюсь адаптировать ваш код под себя, но столкнулся с проблемой:
Браузер показывает следующую ошибку: "VM3395:1 Uncaught SyntaxError: Unexpected token < at XMLHttpRequest.HTTPReq.onreadystatechange" Ошибка возникает на этой строке скрипта: var Subcategories = eval("("+HTTPReq.responseText+")"); Мой файл с формой: <?php include('head.php'); // Список категорий $sql_cat = "SELECT DISTINCT category FROM expenses_cat"; $result_cat = $conn->query($sql_cat); while($row_cat = $result_cat->fetch_assoc()) { $category[] = $row_cat['category']; } ?> <form action="/scripts/add_expenses.php" method="post" name="add_expenses"> <fieldset> <div class="row"> <label for="category">Категория*:</label> <select name="category" required onchange="loadList(this)"> <option value="" selected="selected"></option> <?php foreach ($category as $KEY => $category) { echo '<option value="' . $category . '">' . $category . '</option>' . "\n"; } ?> </select> </div> <div class="row"> <label for="subcategory">Подкатегория*:</label> <select name="subcategory" id="subcatList" disabled="disabled" required> </select> </div> </fieldset> <div class="button"><input id="button" type="submit" value="Добавить запись" /></div> </form> <script> // Создаём объект асинхронной загрузки с сервера. var HTTPReq = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); // Устанавливаем обработчик ответа от сервера: HTTPReq.onreadystatechange = function() { // Если загружено полностью: if(HTTPReq.readyState == 4) { // Обрабатываем ответ: var Subcategories = eval("("+HTTPReq.responseText+")"); // Заполняем список: fillSubcatList(Subcategories); } } // Запоминаем выбранную страну, чтоб не // дублировать запросы в будущем. var selectedCategory = null; // Функция, вызываемая при изменении списка // (при выборе страны) function loadList(categoryList) { // Если выбран "пустой" вариант - очищаем список: if(categoryList.value === "") { clearSubcatList(); return; } // Проверяем выбрана ли уже какая-то страна: if(selectedCategory !== null) { // Если выбрана, проверяем не текущая ли выбрана: if(selectedCategory == categoryList.value) { // Если текущая уже выбрана - делать нечего. return; } } // Если никакая не выбрана, или выбрана не текущая, ставим текущую. selectedCategory = categoryList.value; // Далее запрашиваем сервер на предмет списка городов выбранной страны. // Тут должна быть ваша страница php, её работа будет описана далее: HTTPReq.open("GET", "page.php?category="+categoryList.value); HTTPReq.send(null); // После этого будет вызван обработчик (который мы писали ранее, так что выходим) return true; } // Фукнция, которая добавляет элементы в список подкатегорий: function fillSubcatList(elements) { // elements передан в формате JSON, так что щас он - объект // для начала очистим текущий список подкатегорий clearSubcatList(); // Уберём статус неактивен с элемента: var subcatList = document.getElementById("subcatList"); subcatList.disabled = false; // Скидываем счётчик option'ов (1 - потому что первый элемент пуст) var i = 1; // Рассмотрим elements, в котором ключи это values, а значения - имена. for(var key in elements) { // Создаём и добавляем элемент в список: var Subcategory = new Option(elements[key], key, false, false); subcatList.options[i++] = Subcategory; } // Всё. return true; } // Функция, которая очищает все элементы из второго списка: function clearSubcatList() { var subcatList = document.getElementById("subcatList"); // Такое очищение, походу, работает во всех браузерах нормально. while (subcatList.length > 0) subcatList.options[0] = null; subcatList.options[0] = new Option('', 0, false, false); // Установим его в неактивный subcatList.disabled = "disabled"; } </script> Файл-обработчик page.php в той же папке: <?php if(!array_key_exists('category', $_GET)) die(); include('/scripts/connect.php'); $query = "SELECT id, subcategory FROM expenses_cat WHERE category='" . $_GET['category'] . "' AND inmenu = 1"; $result = $conn->query($query); $output = array(); while($row = $result->fetch_assoc()) { $output[] = '"'.addslashes($row['id']).'": "'. '"'.addslashes($row['subcategory']).'"'; } if(sizeof($output) > 0) { echo '{'. implode(',', $output) .'}'; } ?> Файл connect.php (с ним проблем нет) <?php $servername = "localhost"; $username = "root"; $password = ""; $dbname = "u177653819_reg"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } ?> Буду благодарен за помощь, в javascript я полный новичек... |
griga, перед строкой
var Subcategories = eval("("+HTTPReq.responseText+")"); Добавь это: console.log(HTTPReq.responseText); После открой консоль и посмотри, что возвращает сервер. Там наверняка HTML-код найдется. |
была проблема с подключением конфига connect.php
Убрал "/" и эта ошибка исчезла Но в целом проблему это не решило(( Ругается на ту же строку: var Subcategories = eval("("+HTTPReq.responseText+")"); Но теперь ошибка следующая: Uncaught SyntaxError: Unexpected token ) at XMLHttpRequest.HTTPReq.onreadystatechange Все скобки в файле page.php вроде сходятся... UPD. В целях проверки заменил переменную $_GET['category'] на одно из значений category и запустил как отдельный файл. Команда echo '{'. implode(',', $output) .'}';дает результат в таком формате: {"9": ""Значение1","14": ""Значение2","16": ""ЗначениеN"} |
griga, у тебя json не валидный получается.
в файле page.php смени содержимое на это: <?php if(!array_key_exists('category', $_GET)) die(); include('/scripts/connect.php'); $category=$conn->real_escape_string($_GET['category']); $query = "SELECT id, subcategory FROM expenses_cat WHERE category='".$category."' AND inmenu = 1"; $result = $conn->query($query); $output = array(); while($row=$result->fetch_assoc()){ $output[]=array( $row['id']=>addslashes($row['subcategory']) ); }; echo json_encode($output); exit; А строку с чтением категорий из ответа замени этим: var Subcategories=JSON.parse(HTTPReq.responseText); PS. Рекомендую почитать статьи про SQL-инъекции, как минимум. Статей по этой теме полно, вот первая попавшаяся: https://habrahabr.ru/post/148151/ |
мы на верном пути!
Теперь в теге <select>выдается валидное количество опций подкатегорий, но все они называются Код:
[object Object] |
griga, посмотри, что передается функции «fillSubcatList», в начало этой функции засунь это:
console.log(elements); |
Nexus,
Вот сейчас вообще не понял((( Снова выдает ошибку на строке var Subcategories=JSON.parse(HTTPReq.responseText); Ошибка: Uncaught SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.HTTPReq.onreadystatechange Команда echo json_encode($output); в файле page.php возвращает следующий результат: [{"9":"\u0420\u0430\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0430"},{"14":"\u041e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438"},{"16":"\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442"}] |
griga,
в page.php while замените на это и все будет работать: while($row=$result->fetch_assoc()){ $output[$row['id']]=addslashes($row['subcategory']); }; |
Цитата:
|
Цитата:
|
Итак,
Теперь код page.php выглядит так: if(!array_key_exists('category', $_GET)) die(); include('scripts/connect.php'); $query = "SELECT id, subcategory FROM expenses_cat WHERE category='" . $_GET['category'] . "' AND inmenu = 1"; $result = $conn->query($query); $output = array(); while($row=$result->fetch_assoc()){ $output[$row['id']]=$row['subcategory']; }; echo json_encode($output); exit; Если заменить $_GET['category'] на одно из реальных значений категории, на выходе получаем данные типа {"9":"\u0420\u0430\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u043a\u043e\u0441\u043c\u0435\u0442\u0438\u043a\u0430","14":"\u041e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438","16":"\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442"} В самой же форме при выборе категории консоль отображает ошибку на строке var Subcategories=JSON.parse(HTTPReq.responseText); VM3836:1 Uncaught SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.HTTPReq.onreadystatechange Может проблема кроется в передаче данных на обработку посредством $_GET? |
Nexus,
сервер отдает валидный json, а убрать кодирование многобайтных строк можно начиная с версии 5.4 опцией JSON_UNESCAPED_UNICODE. Ошибка на клиенте потому, что до передачи json есть еще вывод в браузер. |
Цитата:
Можно воспользоваться опцией, а можно убрать экранирование. (Версия PHP вопрощающего неизвестна, а проверять её в коде - глупость.) griga, Цитата:
$category=$conn->real_escape_string($_GET['category']); $query = "SELECT id, subcategory FROM expenses_cat WHERE category='".$category."' AND inmenu = 1"; Цитата:
|
Цитата:
Что касается среды: локальный сайт на OpenServer: Apache-PHP-7 PHP 7.0 MySQL 5.6 P.S. Про инъекции почитаю обязательно. Спасибо |
Часовой пояс GMT +3, время: 22:00. |