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

Тема в разделе "JavaScript", создана пользователем Anton_Fadeev, 21 авг 2015.

Модераторы: ZiX
  1. Anton_Fadeev

    Anton_Fadeev Постоялец

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

    При выборе в любом из списков элемента, например "черный", остальные списки проверяют зависимости и выводят доступные для выбора элементы. Допустим, с черным может сочетаться "белый" и "зеленый". Значит два других списка будут иметь элементы "белый" и "зеленый". При выборе во втором списке "белый" или "зеленый", в третьем списке соответственно оставался либо "зеленый" либо "белый". Возможно ли реализовать подобную зависимость из списков?
     
  2. Aglok

    Aglok ∞³

    Регистр.:
    9 янв 2012
    Сообщения:
    162
    Симпатии:
    46
    Недавно писал похожу тему. Но только для научной деятельности. Адаптировал по 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>
     
  3. Anton_Fadeev

    Anton_Fadeev Постоялец

    Регистр.:
    3 фев 2014
    Сообщения:
    50
    Симпатии:
    10
    Видимо, я массив состояний 1-3 списков заполнил неверно, т.к. вне зависимости от выбора, выводит одно и то же. Можете объяснить как их правильно заполнить?
     
  4. Aglok

    Aglok ∞³

    Регистр.:
    9 янв 2012
    Сообщения:
    162
    Симпатии:
    46
    Их заполнять не нужно. Они автоматом заполняются при выборе, списков. Тебе только нужно менять массив значений ListColor. Конструктор списков BuildSelect в коде заполняет вначале все списки, значения которых берет ListColor.
     
  5. Anton_Fadeev

    Anton_Fadeev Постоялец

    Регистр.:
    3 фев 2014
    Сообщения:
    50
    Симпатии:
    10
    Тогда как строится зависимость списков? Сейчас они все независимые друг от друга.
     
  6. Aglok

    Aglok ∞³

    Регистр.:
    9 янв 2012
    Сообщения:
    162
    Симпатии:
    46
    Ты можешь сам списки делать. Их зависимость строится через проверку состояний списков. Я для удобства взял один массив из него построил 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'];//Данный для списка
     
    Последнее редактирование: 23 авг 2015
  7. Absolute

    Absolute Крокодил ;)

    Регистр.:
    9 авг 2009
    Сообщения:
    362
    Симпатии:
    178
    мож так?
    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>
     
  8. Anton_Fadeev

    Anton_Fadeev Постоялец

    Регистр.:
    3 фев 2014
    Сообщения:
    50
    Симпатии:
    10
    Такая реализация у меня есть. Спасибо
     
  9. Absolute

    Absolute Крокодил ;)

    Регистр.:
    9 авг 2009
    Сообщения:
    362
    Симпатии:
    178
    я примерно тоже самое сейчас делаю. Только число селектов не определено. Т.е. может быть и 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>, которые могут быть в селекте - должны там быть изначально.
     
    Последнее редактирование: 31 авг 2015
    Anton_Fadeev нравится это.
  10. Aglok

    Aglok ∞³

    Регистр.:
    9 янв 2012
    Сообщения:
    162
    Симпатии:
    46
    Так тоже интересное решение, но она для частных случаев.
    У тебя на то и связные списки, допускается выбор только одного значения в списке.
    5 списков 5 изменений опций = 25 возможных комбинаций.
    10 списков 10 опций = 100 возможных комбинаций. Другое дело мультивыбор там вариаций больше.
    Да именно работа ввиде матрицы. Можно быстро перебрать все возможные состояния. Быстрым циклом for. Выше я уже описывал перебор в матричном типе. Получается очень быстро.
     
    Последнее редактирование: 28 авг 2015