根据先前的下拉列表选择来更新下拉列表

时间:2018-10-01 16:19:22

标签: javascript jquery

我有一个电子商务网站,网站上的商品具有多种属性(例如尺寸,颜色等)。

在每个产品页面上,每个属性都有一个类别为'attribute_price'的下拉列表。

我还从数据库中将隐藏的输入内容预加载到页面上,其中包含'hidden_attribute_value'类的每种产品的价格。

因此,并非每种尺寸和颜色的组合都是一个选择。例如,我们可能有'small_red''medium_red',但没有'large_red'

因此,如果他们从尺寸下拉菜单中选择'large''red'则不是该颜色的选项。

到目前为止,我有:

$("select.attribute_price").on("change", function(){

    var id = event.target.id;
    // determine which dropdown was changed (size or colour)
    var attribute_value = document.getElementById(id).value+'_';
    // get the value of the dropdown that they selected

    var other_attribute_ids = []
    var i;
    var other_attributes = document.getElementsByClassName("attribute_price");
    for(i=0; i<other_attributes.length; i++){
        if(other_attributes[i].id != id){
            var other_attribute_id = document.getElementById(other_attributes[i].id).id;
            other_attribute_ids.push(other_attribute_id);
        }
    }
    // create an array of all of the other dropdown ids excluding the one they changed

    var all_attribute_ids = []
    var i;
    var all_attributes = document.getElementsByClassName("hidden_attribute_value");
    for(i=0; i<all_attributes.length; i++){
        all_attribute_ids.push(all_attributes[i].id);
    }
    // create an array of all of the possible values that it can be

});

所以我有一个变量'attribute_value',它类似于'red_'或'blue _'。

我有一个名为'all_attribute_values'的数组,其中包含所有可能组合的隐藏输入ID。这些将具有“ small_red_”或“ small_blue”之类的值。

我有一个名为'other_attribute_ids'的数组,该数组具有尚未选择的其他下拉菜单的ID。

因此,如果'all_attribute_values'中的项目不包含'attribute_value',请从'other_attribute_ids'中删除该选项。

任何帮助或建议,将不胜感激。

5 个答案:

答案 0 :(得分:5)

我根据您的用例创建了一个示例html。解决方案同样如此,但是您应该从中获得启发来解决您的问题。

我考虑了独立的属性,因此该解决方案将扩展为具有不同值的新属性。我还认为服务器响应不可编辑。

我在jsfiddle中有一个快速链接来签出解决方案

  

https://jsfiddle.net/nfLx6aok/1/

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>

<select id="size" class="attribute_price">
  <option value="small">Small</option>
  <option value="large">Large</option>
</select>

<select id="color" class="attribute_price">
  <option value="red">Red</option>
  <option value="green">Green</option>
  <option value="black">Black</option>
</select>

<select id="pattern" class="attribute_price">
  <option value="solids">Solids</option>
  <option value="checked">Checked</option>
  <option value="dots">Dots</option>

</select>

<input type="hidden" id="small_red_solids" class="hidden_attribute_value">
<input type="hidden" id="small_black_dots" class="hidden_attribute_value">
<input type="hidden" id="large_green_checked" class="hidden_attribute_value">

<script>

  // on page load 
  $( document ).ready(function() {
    renderOptions();
});


  $("select.attribute_price").on("change", function () {
    renderOptions();
  });

  function renderOptions() {
    // create an array of all of the possible values that it can be
    // allowed_attribute_values = ['small_red', 'large_blue']
    var allowed_attribute_values = [];
    var all_attributes = document.getElementsByClassName("hidden_attribute_value");
    for (var i = 0; i < all_attributes.length; i++) {
      allowed_attribute_values.push(all_attributes[i].id);
    }

    function getAllPossibleValues(current_level, all_attributes) {
      var depth_index = all_attributes.length;
      var selected_combination = '';
      for (var i = 0; i < depth_index; i++) {
        if (i <= current_level) {
          selected_combination += all_attributes[i].value;
          if (i != all_attributes.length - 1) {
            selected_combination += '_';
          }
        }
      }

      // hide all lower options
      for (var i = current_level + 1; i < depth_index; i++) {
        var selectedIdOptions = all_attributes[i].options;
        all_attributes[i].value = null
        for (var j = 0; j < selectedIdOptions.length; j++) {
          // hide all lower options
          selectedIdOptions[j].hidden = true;
          var el = allowed_attribute_values.find(a => a.includes(selected_combination + selectedIdOptions[j].value));
          if (el) {
            selectedIdOptions[j].hidden = false;
          }
        }
      }
    }

    if (event) {
      var id = event.target.id;
    } else {
      var id = document.getElementsByClassName("attribute_price")[0].id;
    }

    var other_attributes = document.getElementsByClassName("attribute_price");
    for (var i = 0; i < other_attributes.length; i++) {
      if (other_attributes[i].id == id) {
        allPossibleValues = getAllPossibleValues(i, other_attributes);
        // we dont want to go deeper as of now
        break;
      }
    }
  }
</script>

答案 1 :(得分:1)

这将适用于任意数量的下拉菜单。

您可以生成随机属性进行测试。

$(document).ready(function () {
    /* generates random attributes */
    var div_attributes = $('#div_attributes');
    var select_colors = $('#select_colors');
    var select_sizes = $('#select_sizes');
    var select_attr0 = $('#select_attr0');
    var select_attr1 = $('#select_attr1');

    $('#btnGenerate').on('click', function () {
        var result = "";

        //
        var index = getRandomArbitrary(1, select_sizes.find('option').get().length);
        var random_attribute = select_sizes.find('option').eq(index).attr('value');
        result += random_attribute;

        //
        index = getRandomArbitrary(1, select_colors.find('option').get().length);
        random_attribute = select_colors.find('option').eq(index).attr('value');
        result += '_' + random_attribute;

        //
        index = getRandomArbitrary(1, select_attr0.find('option').get().length);
        random_attribute = select_attr0.find('option').eq(index).attr('value');
        result += '_' + random_attribute;

        //
        index = getRandomArbitrary(1, select_attr1.find('option').get().length);
        random_attribute = select_attr1.find('option').eq(index).attr('value');
        result += '_' + random_attribute;

        $('<div>' + result + '</div>').appendTo(div_attributes);

        div_attributes.find('div').each(function () {
            var item = $(this);
            attributes.push(item.text());
        });
        SetFirstSelect();
    });

    var attributes = [];
    //sets first select
    SetFirstSelect();
    function SetFirstSelect() {
        $.each(attributes, function (i, val) {
            var attribute = val.split('_')[0];
            $('.attribute_price').eq(0).find('option[value="' + attribute + '"]').show();
        });
    }
    //control attributes array
    var selected_val = [];
    $('.attribute_price').on('change', function () {
        var item = $(this);
        var index = item.index('.attribute_price');
        selected_val[index] = item.val();
        var select = $('.attribute_price').eq(index + 1);
        var selected_attribute = item.val();
        for (var i = index + 1; i < $('.attribute_price').get().length; i++) {
            $('.attribute_price').eq(i).find('option').hide();
            $('.attribute_price').eq(i).prop('selectedIndex', 0)
        }
        var selected_val_str = selected_val[0];
        for (var i = 1; i <= index; i++) {
            selected_val_str += '_' + selected_val[i];
        }
        $.each(attributes, function (j, val) {
            if (val.indexOf(selected_val_str) >= 0) {
                var attribute1 = val.split('_')[index + 1];

                select.find('option[value="' + attribute1 + '"]').show();
            }
        });
    });

    function getRandomArbitrary(min, max) {
        return Math.floor(Math.random() * (max - min) + min);
    }
});
.attribute_price option {
            display: none;
        }
        
        .container {
        margin:30px;
        }
        
        .row > div {
        padding:10px;
        }
        .btn {
        margin-top:20px;
        }
<script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
  <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
   <div class="container">
        <div class="row">
        <div style="width:50%; float:left">
                <input type="button" class="btn btn-primary" id="btnGenerate" value="generate random attributes" />
                <div id="div_attributes"></div>
            </div>
            <div style="width:50%; float:left">
                <div class="form-group">
                    <label>Sizes</label>
                    <select class="form-control attribute_price" id="select_sizes">
                        <option value="">select size</option>
                        <option value="small">small</option>
                        <option value="large">large</option>
                        <option value="medium">medium</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>color</label>
                    <select id="select_colors" class="form-control attribute_price">
                        <option value="">select color</option>
                        <option value="black">black</option>
                        <option value="yellow">yellow</option>
                        <option value="red">red</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>attrType0</label>
                    <select id="select_attr0" class="form-control attribute_price">
                        <option value="">select attr0</option>
                        <option value="attr00">attr00</option>
                        <option value="attr01">attr01</option>
                        <option value="attr02">attr02</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>attrType1</label>
                    <select id="select_attr1" class="form-control attribute_price">
                        <option value="">select attr1</option>
                        <option value="attr10">attr10</option>
                        <option value="attr11">attr11</option>
                        <option value="attr12">attr12</option>
                    </select>
                </div>
            </div>

            
        </div>
    </div>

答案 2 :(得分:1)

我不知道我是否完全了解您要解释的内容,但这就是我的理解 您有一个网站,该网站的下拉菜单中假设衣服具有属性,尺寸,价格,颜色,品牌,并且您有一个对象数组,其中包含每个对象的所有这些属性。

我将在reactjs中对此进行解释,因为我比php更熟悉

因此,对于每个下拉列表,您都可以拥有一个onchange处理函数,该处理函数调用一个函数来检查其他下拉列表的值。如果选择大小M,则在过滤大小为M的衣服之后启用并填充下拉列表。完成后,调用类似的功能来检查其他属性。

这时您的所有下拉菜单都将处于活动状态,现在,如果用户对第一个下拉菜单进行了任何更改(即大小),您可以重置其他下拉菜单以重新选择,也可以根据您的处理方式通过新列表< / p>

这是我为设置日期所做的类似操作

const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July','August', 'September', 'October', 'November', 'December']
const Month30 = ['4', '6', '9', '11']
const Month31 = ['1', '3', '5', '7', '8', '10', '12']

class Dropdown extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      birthDay: '',
      birthMonth: '',
      birthYear: ''
    }
  }

  componentDidMount() {
  }

  getMonthTotalDays = (birthMonth, birthYear) => {
    if (birthMonth === '02') {
      if (birthYear !== '' && birthYear % 4 === 0) {
        return 29
      } else {
        return 28
      }
    } else if (Month30.includes(birthMonth)) {
      return 30
    } else {
      return 31
    }
  }

  handleChange = e => {
    this.setState({
      [e.target.name]: e.target.value
    }, () => {
      const { birthMonth, birthYear, birthDay } = this.state
      const days = this.getMonthTotalDays(birthMonth, birthYear)
      if (birthDay > days) {
        this.setState({ birthDay: '' })
      }
    })

  }


  renderMonths = () => {
    return months.map((month, i) => {
      if(i < 9){
        return (<option key={`${month}-${i}`} value={'0' + (i + 1)}>{month}</option>)
      }
      else
        return (<option key={`${month}-${i}`} value={i + 1}>{month}</option>)
    })
  }

  renderDay = () => {
    const { birthMonth, birthDay, birthYear } = this.state
    const daysOptions = []
    let days = this.getMonthTotalDays(birthMonth, birthYear)

    for (let day=1; day<=days; day++) {
      daysOptions.push(<option key={`'date-'${day}`} value={day}> { day } </option>)
    }

    return daysOptions
  }

  renderYears = () => {
    const toYear = (new Date()).getFullYear() - 16
    const yearOptions = []
    for (let year = 1960; year <= toYear; year++) {
      yearOptions.push(<option key={`year-${year}`} value={year}> { year } </option>)
    }

    return yearOptions
  }


  render() {
    const { birthDay, birthMonth, birthYear } = this.state
    return (
      <div>
          <label>Month</label>
          <select
            name="birthMonth"
            value={ birthMonth }
            onChange={this.handleChange}
          >
            <option disabled selected value=''>Month</option>
            { this.renderMonths() }
          </select>
          <label>Day</label>
          <select
            name='birthDay'
            value={ birthDay }
            onChange={this.handleChange}
          >
            <option disabled selected value=''>Day</option>
            { this.renderDay() }
          </select>
          <label>Year</label>
          <select
            name='birthYear'
            value={ birthYear }
            onChange={this.handleChange}
          >
            <option disabled selected value=''>Year</option>
            { this.renderYears() }
          </select>
      </div>
    )
  }
}


ReactDOM.render(
  <Dropdown />,
  document.getElementById('drop')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="drop"></div>

答案 3 :(得分:0)

这是一个将skus数组作为输入并为每个属性创建一个下拉列表的解决方案。当任何下拉列表值更改时,所有其他下拉列表中的选项都会更新,以仅显示与所选选项一致的选项。

https://codepen.io/rockysims/pen/PyJbbv

<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script type="text/javascript">
    skus = [{
        id: 1,
        attributes: {
            color: "red",
            size: "small",
            shape: "circle"
        }
    }, {
        id: 2,
        attributes: {
            color: "red",
            size: "medium",
            shape: "square"
        }
    }, {
        id: 3,
        attributes: {
            color: "yellow",
            size: "small",
            shape: "circle"
        }
    }, {
        id: 4,
        attributes: {
            color: "yellow",
            size: "medium",
            shape: "square"
        }
    }, {
        id: 5,
        attributes: {
            color: "yellow",
            size: "large",
            shape: "square"
        }
    }, {
        id: 6,
        attributes: {
            color: "green",
            size: "medium",
            shape: "square"
        }
    }, {
        id: 7,
        attributes: {
            color: "green",
            size: "large",
            shape: "square"
        }
    }];

    $(function() {
        const allOptionsByAttrName = buildAllOptionsByAttrName();

        //create dropdowns
        for (let attrName in allOptionsByAttrName) {
            const dropdownId = attrName + "Dropdown";
            const options = allOptionsByAttrName[attrName];
            let html = "";
            html += attrName + ": ";
            html += buildDropdownHtml(dropdownId, options);
            html += "<br/>";
            $("#dropdowns").append(html);
        }

        //on dropdown changes, update options of all dropdowns
        for (let changedAttrName in allOptionsByAttrName) {
            $("#" + changedAttrName + "Dropdown").on('change', function() {
                //build pickedOptionByAttrName
                const pickedOptionByAttrName = {};
                for (let attrName in allOptionsByAttrName) {
                    const dropdown = $("#" + attrName + "Dropdown");
                    pickedOptionByAttrName[attrName] = dropdown.val();
                }

                refreshAvailableOptions(pickedOptionByAttrName);
            });
        }
    });

    function buildAllOptionsByAttrName() {
        const allOptionsByAttrName = {};
        for (let sku of skus) {
            for (let attrName in sku.attributes) {
                allOptionsByAttrName[attrName] = allOptionsByAttrName[attrName] || [];
                if (allOptionsByAttrName[attrName].indexOf(sku.attributes[attrName]) == -1) {
                    allOptionsByAttrName[attrName].push(sku.attributes[attrName]);
                }
            }
        }

        return allOptionsByAttrName;
    }

    function buildDropdownHtml(dropdownId, options) {
        let html = "";
        html += "<select id='" + dropdownId + "'>";
        html += "<option value=''>";
        html += "";
        html += "</option>";
        for (let option of options) {
            html += "<option value='" + option + "'>";
            html += option;
            html += "</option>";
        }
        html += "</select>";

        return html;
    }

    function refreshAvailableOptions(pickedOptionByAttrName) {
        for (let attrName in pickedOptionByAttrName) {
            //build availableOptions
            const dropdown = $("#" + attrName + "Dropdown");
            const options = $("#" + attrName + "Dropdown option");
            const availableOptions = buildAvailableOptions(pickedOptionByAttrName, attrName);
            availableOptions.push(""); //nothing picked option

            //show available options and hide others
            options.each(function() {
                const option = $(this);
                const optionIsAvailable = availableOptions.indexOf(option.val()) != -1;
                if (optionIsAvailable) {
                    option.show();
                } else {
                    option.hide();
                }
            });
        }
    }

    function buildAvailableOptions(pickedOptionByAttrName, attrNameToBuildFor) {
        //build availableSkus
        const availableSkus = skus.filter(function(sku) {
            let available = true;
            for (let attrName in pickedOptionByAttrName) {
                if (attrName != attrNameToBuildFor) {
                    const pickedOption = pickedOptionByAttrName[attrName];
                    if (pickedOption) {
                        available = available && sku.attributes[attrName] == pickedOption;
                    }
                }
            }

            return available;
        });

        //build availableOptions
        const availableOptions = [];
        for (let sku of availableSkus) {
            if (availableOptions.indexOf(sku.attributes[attrNameToBuildFor]) == -1) {
                availableOptions.push(sku.attributes[attrNameToBuildFor]);
            }
        }

        return availableOptions;
    }
</script>

<div id="dropdowns">
</div>

如果您不想动态创建下拉菜单,请在//create dropdowns下的for循环中注释掉,然后将<div id="dropdowns"></div>替换为以下内容:

<div id="dropdowns">
    color: 
    <select id="colorDropdown">
        <option value=""></option>
        <option value="red">red</option>
        <option value="yellow">yellow</option>
        <option value="green">green</option>
    </select><br/>
    size: 
    <select id="sizeDropdown">
        <option value=""></option>
        <option value="small">small</option>
        <option value="medium">medium</option>
        <option value="large">large</option>
    </select><br/>
    shape:
    <select id="shapeDropdown">
        <option value=""></option>
        <option value="circle">circle</option>
        <option value="square">square</option>
    </select><br>
</div>

答案 4 :(得分:0)

如果您可以在PHP中标识其他类别的哪些选项可用于某些下拉类别值选择。您可以将每个选项的增量选项标记为已排除,以使其保持禁用状态:

JS:

$(document).ready(function () {
    $(".dropdown").change(function(){
        var val = $(this).val();
        var id = $(this).attr('id');

        $.get('get_options.php', {category: id, value:val}, function(data) {

            $(".dropdown:not(#"+id+")").each(function() {

                var cat = $(this).attr('id');
                $(this).find('option').each(function() {

                    var cat_val = $(this).val();
                    var options = data[cat];
                    var exluded = $(this).attr('exlude');

                    if ($.inArray(cat_val, options) !== -1) {

                        $(this).attr('exlude', exluded-1);
                        if(exluded == 1) {
                            $(this).prop('disabled', false);
                        }
                    } else {

                        $(this).attr('exlude', exluded+1);
                        $(this).prop('disabled', true);
                    }
                });
            });
        }, 'json');
    });
});

HTML:

<div id="dropdowns">
color: 
<select id="color" class="dropdown">
    <option value=""></option>
    <option value="red">red</option>
    <option value="yellow">yellow</option>
    <option value="green">green</option>
</select><br/>
size: 
<select id="size" class="dropdown">
    <option value=""></option>
    <option value="small">small</option>
    <option value="medium">medium</option>
    <option value="large">large</option>
</select><br/>
shape:
<select id="shape" class="dropdown">
    <option value=""></option>
    <option value="circle">circle</option>
    <option value="square">square</option>
    <option value="triangle">triangle</option>
</select><br>

**样本数据:**例如,如果用户首先选择颜色,则返回其他下拉类别的所有可用选项。

{"size": ["small","large"],"shape":["circle"]}

或在php中:

$data = array(
    'size' => array(
        'small', 'large'
    ),
    'shape' => array(
        'circle'
    ),
);

echo json_encode($data);