3-4 взаимозависимых списка (AJAX)

Anton_Fadeev

Постоялец
Регистрация
3 Фев 2014
Сообщения
50
Реакции
10
Задача. Есть 3 списка (а лучше 4, но если сложно реализовать 4, то 3 достаточно) Все 3 взаимозависимые. Под "взаимозависимостью" я имею в виду то, что при выборе в любом из них, остальные автоматом обновляют допустимые значения. Реализация списков, где сначала выбираешь первый, потом второй, а потом третий у меня есть.
Подробно распишу механику:
Список1, Список2 и Список3 изначально одинаковые:
белый
черный
зеленый
красный
оранжевый

При выборе в любом из списков элемента, например "черный", остальные списки проверяют зависимости и выводят доступные для выбора элементы. Допустим, с черным может сочетаться "белый" и "зеленый". Значит два других списка будут иметь элементы "белый" и "зеленый". При выборе во втором списке "белый" или "зеленый", в третьем списке соответственно оставался либо "зеленый" либо "белый". Возможно ли реализовать подобную зависимость из списков?
 
Недавно писал похожу тему. Но только для научной деятельности. Адаптировал по javascript. В основе стоит перебор матрицы 3х ранговой. Только для 3х списков.
Код:
<html>
<head>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
    <script>
        $(document).ready(function(){
            var ListColor = ['red', 'orange', 'blue', 'green', 'white', 'black'];//Данный для списка
            var arrValue = [];//Промежуточный массив
            var arr1 = [];//массив состояний 1 списка
            var arr2 = [];//массив состояний 2 списка
            var arr3 = [];//массив состояний 3 списка
            var id;// id каждого списка

            //Матричная функция возможных состояний списков рангом 3. Тоесть для 3х списков.
            function BuildArray(arr, arrlist){
                var len = arr.length - 1;
                if(len == 1){ //Выбран первый селект
                    arr2 = arr3 = delElementArr(arrlist, arr[len]);                 
                }else if(len == 2) //Могут быть выбраны 1ый и 2ой списки
                { 
                    for(var i=0; i <= len; i++){
                        if(arr[i] == 'undefined'){
                            continue;
                        }
                        if(arr[len-1]){//Если выбран первый список
                            arr1 = delElementArr(arrlist, arr[len]);
                            arr2 = delElementArr(arrlist, arr[len-1]);
                            arr3 = delElementArr(arr1, arr[len-1]);
                        }else{
                            arr1 = arr3 = delElementArr(arrlist, arr[len]);
                        }
                    }
                }else if(len == 3) //Могут быть выбраны 1ый , 2ой и 3ий списки
                {
                    for(var i=0; i <= len; i++){
                        if(arr[i] == 'undefined'){
                            continue;
                        }
                        if(arr[len-2] && arr[len-1]){//Выбраны 1 и 2 и 3 списки
                            arr1 = delElementArr(arrlist, arr[len]);
                            arr1 = delElementArr(arr1, arr[len-1]);
                            arr2 = delElementArr(arrlist, arr[len]);
                            arr2 = delElementArr(arr2, arr[len-2]);
                            arr3 = delElementArr(arrlist, arr[len-1]);
                            arr3 = delElementArr(arr3, arr[len-2]);
                        }else if(arr[len-2] && !arr[len-1]){//Выбраны только 1 и 3 списки
                            arr1 = delElementArr(arrlist, arr[len]);
                            arr2 = delElementArr(arr1, arr[len-2]);
                            arr3 = delElementArr(arrlist, arr[len-2]);
                        }else if(arr[len-1] && !arr[len-2]){//Выбраны только 2 и 3 списки
                            arr2 = delElementArr(arrlist, arr[len]);
                            arr3 = delElementArr(arrlist, arr[len-1]);
                            arr1 = delElementArr(arr3, arr[len]);
                        }
                    }
                }
            }
         
            //Заполнили списки содержимым
            function BuildSelect(array){
                var select = $('select');
                select.find('option').remove();
                for(i=0; i < array.length; i++){
                    var options = new Option(array[i], array[i]);
                    select.append(options);
                }
            }
         
            //Заполнение списков в процессе изменения
            function BuildSelectId(array, id){
                //Строим списки по id
                var select = $('select#'+id);
                select.find('option').remove();
                for(i=0; i < array.length; i++){
                    if(array[i] == arrValue[id])
                        var options = new Option(array[i], array[i], 'defaultSelected', 'selected');
                    else
                        var options = new Option(array[i], array[i]);
                    select.append(options);
                }
            }
            //Вспомогательная функция для форматирования спиков
            function delElementArr(array, value){
                var copy = array.slice(0);
                return $.grep(copy, function(val){
                    if(val != value)    return val;
                });
            }
            //Пуск построения данных
            function run(){
                if(arr1.length)    BuildSelectId(arr1, 1);
                if(arr2.length)    BuildSelectId(arr2, 2);
                if(arr3.length)    BuildSelectId(arr3, 3);
            }
                     
            $('select').change(function(){
             
                arrValue[id] = $(this).val();//Заполняем массив выбранным значением
             
                BuildArray(arrValue, ListColor);
                run();//Запускаем построение списков
            });
         
            BuildSelect(ListColor);

    });
    </script>
</head>
<body>
<select id="1"></select>
<select id="2"></select>
<select id="3"></select>
</body>
</html>
 
Видимо, я массив состояний 1-3 списков заполнил неверно, т.к. вне зависимости от выбора, выводит одно и то же. Можете объяснить как их правильно заполнить?
 
Их заполнять не нужно. Они автоматом заполняются при выборе, списков. Тебе только нужно менять массив значений ListColor. Конструктор списков BuildSelect в коде заполняет вначале все списки, значения которых берет ListColor.
 
Тогда как строится зависимость списков? Сейчас они все независимые друг от друга.
 
Ты можешь сам списки делать. Их зависимость строится через проверку состояний списков. Я для удобства взял один массив из него построил 3 списка. Так как ты указывал что списки одинаковые. То есть если ты запустишь код, как есть у тебя вначале js создаст через dom 3 списка. После этого проверяется скриптом их состояния каждый раз как ты изменяешь значение любого списка, у тебя строится зависимость через проверку возможных вариантов значений списка.
То что кодом ниже. Это вспомогательные массивы. И они заполняются по ходу выполнения скрипта. Дает больше вариантов зависимости любой длинны списков.
Код:
            var arr1 = [];//массив состояний 1 списка
            var arr2 = [];//массив состояний 2 списка
            var arr3 = [];//массив состояний 3 списка
Ты можешь и так заполнить списки, но тогда нужно менять в коде.
Код:
var ListColor1 = ['red', 'orange', 'blue', 'green', 'white', 'black'];//Данный для списка
var ListColor2 = ['red', 'orange', 'blue', 'green', 'white', 'black'];//Данный для списка
var ListColor3 = ['red', 'orange', 'blue', 'green', 'white', 'black'];//Данный для списка
 
Последнее редактирование:
мож так?
HTML:
<select class="sel1">
   <option value="red" selected>-- Выбрать</option>
  <option value="red">Красный</option>
  <option value="green">Зеленый</option>
  </select>
  <select class="sel2" disabled>
    <option value="white" selected>Белый</option>
    <option value="orange">Оранжевый</option>
    <option value="кув">Красный</option>
  </select>
  <select class="sel3" disabled>
    <option value="orange">Оранжевый</option>
    <option value="blue">Голубой</option>
    <option value="green">Зеленый</option>
    <option value="black">Черный</option>
    <option value="white" selected>Белый</option>
  </select>
<script src="https://code.jquery.com/jquery-1.9.1.js"></script>
  <script>
    $(document).ready(function(){
var langObj = {
    red : 'Красный',
    orange : 'Оранжевый',
    blue : 'Голубой',
    green : 'Зеленый',
    white : 'Белый',
    black : 'Черный'
};
var lispOption = {
      red : {
        red : ['black', 'blue'],
        orange : ['green', 'white', 'black'],
        white : ['red', 'orange', 'black']
        },                
      green : {
        blue :  ['red', 'orange', 'blue', 'green', 'white', 'black'],
        red : ['black', 'orange', 'blue'],
        white : ['red', 'orange'],
        black : ['blue', 'red', 'orange'],
        orange : ['orange', 'blue', 'black']
        }
};
var tempSel1 = $('.sel1'), tempSel2 = $('.sel2'), tempSel3 = $('.sel3');
tempSel1.on('change', function(){
tempSel2.children('option').remove();
  tempSel2.removeAttr('disabled');
  tempSel3.children('option').remove();
  for(var i in lispOption[this.value]) {
    $('<option/>', {
     value :  i,
     text : langObj[i],
    }).appendTo(tempSel2); 
  }
var lastSel = lispOption[this.value][tempSel2.val()];
  for(var s=0; s<lastSel.length; s++) {
     $('<option/>', {
     value :  lastSel[s],
     text : langObj[lastSel[s]],
    }).appendTo(tempSel3); 
  }
});
tempSel2.on('change', function(){
  tempSel3.children('option').remove();
  tempSel3.removeAttr('disabled');
   var lastSel = lispOption[tempSel1.val()][this.value];
  for(var i=0; i < lastSel.length; i++) {
    $('<option/>', {
     value :  lastSel[i],
     text : langObj[lastSel[i]],
    }).appendTo(tempSel3); 
  } 
});
});
  </script>
 
я примерно тоже самое сейчас делаю. Только число селектов не определено. Т.е. может быть и 2, а может быть и 8.
В вашем ТЗ не хватает по крайней мере одной вводной.
Предположим, есть значения. Для простоты размышлений в кажом селекте по 2 оптиона:
Код:
select1{ param1, param2}
select2{ param3, param4}
select3{ param5, param6}
Итого 2*2*2 = 8 комбинаций могут быть максимально.
Собственно, комбинации:
Код:
param1 => param3 => param5
param1 => param3 => param6
param1 => param4 => param5
param1 => param4 => param6
<-- param2 => param3 => param5 --> --эту исключим.
param2 => param3 => param6
param2 => param4 => param5
param2 => param4 => param6
Так вот, при выборе param3 во втором селекте итоговое состояние селектов будет таким:
Код:
select1{ param1 }
select2{ param3, param4 }
select3{ param5, param6 }
или таким:
Код:
select1{ param1, param2 }
select2{ param3, param4 }
select3{ param6 }
??
То есть не ясно по какому из соседних селектов строить.
Следующее. Адекватный обход удается построить, если комбинации в многомерном массиве.
Т.е.
Код:
var array = [
['param1', 'param1', 'param1'],
['param1', 'param1', 'param2'],
... и т.д.
];
Можно использовать быстрые циклы for/while. Это прокатит, если не так много комбинаций.
Если же у вас например 5 селектов по 10 оптионов в каждом, то выходит
100 тыс. вероятных комбинаций? А если 10 селектов по 10 оптионов? 10 млрд? Я не матан, к сожалению, а практик и вынужден признать мозги вскипают. Как запилить обход объекта, как в коде выше опираясь на значение последнего по уровню ключа так и не придумал.
...И я вот что подумал. Что есть смысл строить массивы комбинаций, производить тысячи итераций, создавать временные массивы, делать туеву хучу сравнений, а потом строить. Если можно сразу начать строить? ...А соответствия писать прямо в HTMLе.
И написал вот этот код в виде плагина JQuery:
HTML:
(function($){

  jQuery.fn.slipoptions = function(options){
    options = $.extend({
      class:false,
      val:false
    }, options);

var f = function(){
var enData = $(this).children('option[value="'+$(this).val()+'"]').data();
var i;
for(i in enData){
  if(i in f){
    $('.'+i).children('option').remove();
   } else {
      f[i] = $('.'+i).children('option').detach();
   }
      f[i].each(function(){
       var str = $(this).val();
       if(enData[i].indexOf(str)>-1){
        $(this).clone().appendTo($('.'+i));
       }
      });
}
   };
$(this).on('change', f);
    if(options.class){
      if(options.val){
        $('.'+options.class).val(options.val).trigger('change');
       } else {
         $('.'+options.class).trigger('change');
       }
    }
   return this;
};
})(jQuery);
То есть пишем прямо в option, что, при её выборе, будут содержать селекты. Пример:
HTML:
<select class="selone">
<option data-seltwo="size_s,param_name2,small" data-sel3="size_m,green,verysmall" value="size_xxl">Размер XXL</option>
<option data-selone="red,param_name1" data-seltwo="param_name2,small,size_s" data-sel3="green,verysmall" value="red">Красный</option>
<option data-seltwo="white,small" data-sel3="size_m,green,verysmall" value="param_name1">Name param</option>
<option data-seltwo="param_name2,white" data-sel3="verybig,green,verysmall" value="big">Большой</option>
</select>

<select class="seltwo">
<option data-selone="param_name1,big" data-sel3="verybig,green,verysmall" value="white">Белый</option>
<option data-selone="red,param_name1,big" data-sel3="size_m,green,verysmall" value="param_name2">Имя параметра 2</option>
<option data-selone="red,param_name1" data-sel3="green,verysmall" value="size_s">Размер S</option>
<option data-selone="size_xxl,red,param_name1" data-sel3="size_m,green,verysmall" value="small">Маленький</option>
</select>

<select class="sel3">
<option data-selone="size_xxl,red,param_name1" data-seltwo="white,small,size_s" value="verybig">Очень большой</option>
<option data-selone="param_name1,big" data-seltwo="white,small" value="size_m">Размер M</option>
<option data-selone="red,param_name1,big" data-seltwo="param_name2,small,size_s" value="verysmall">Очень маленький</option>
<option data-selone="red,param_name1" data-seltwo="size_s,small" value="green">Зеленый</option>
</select>
Инициализация:
Код:
$(document).ready(function(){
   $('select[class^="sel"]').slipoptions();
});
Если передать:
Код:
$('select[class^="sel"]').slipoptions({
class: 'seltwo',
val: 'small'
});
то при загрузке страницы значения селектов будут построены по данным, содержащимся в option со значением small селекта с классом seltwo. Если передать только класс, то значения будут построены по текущему значению селекта с этим классом. Если не передавать этот параметр, то при загрузке страницы никакие селекты исключены не будут. Особого опыта написания плагинов для JQuery у меня нет
Тестил в Opere на вебките, в Opere на Presto, в Лисе, в ie9, Хроме, на мобильных браузерах - работает. А так же с версиями JQuery: 1.7.2, 1.8.3, 1.9.1, 1.11.3, 2.1.4, 3.0.0-alpha1
Важно усвоить, что все <option>, которые могут быть в селекте - должны там быть изначально.
 
Последнее редактирование:
Так тоже интересное решение, но она для частных случаев.
Если же у вас например 5 селектов по 10 оптионов в каждом, то выходит
500 тыс. вероятных комбинаций? А если 10 селектов по 10 оптионов? 10 млрд?
У тебя на то и связные списки, допускается выбор только одного значения в списке.
5 списков 5 изменений опций = 25 возможных комбинаций.
10 списков 10 опций = 100 возможных комбинаций. Другое дело мультивыбор там вариаций больше.
Следующее. Адекватный обход удается построить, если комбинации в многомерном массиве.
Да именно работа ввиде матрицы. Можно быстро перебрать все возможные состояния. Быстрым циклом for. Выше я уже описывал перебор в матричном типе. Получается очень быстро.
 
Последнее редактирование:
Назад
Сверху