Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating dropdown based on previous dropdown selection

I have an ecommerce site that has products with multiple attributes (e.g. size, colour, etc,.)

On each product page there is a dropdown for each attribute with a class of 'attribute_price'.

I have also preloaded hidden inputs onto the page from my database with the pricing for each product with a class of 'hidden_attribute_value'.

So not every combination of size and colour is an option. For example, we might have 'small_red' or 'medium_red' but no 'large_red'

So if they select 'large' from the size dropdown menu 'red' should not be an option for the colour.

What I have so far is:

$("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

});

So I have a variable 'attribute_value' which would be something like 'red_' or 'blue_'.

I have an array called 'all_attribute_values' which has the ids of hidden inputs for all possible combinations. These would have values like 'small_red_' or 'small_blue'.

And I have an array called 'other_attribute_ids' which has the id of the other dropdown menus that haven't been selected.

So if an item in 'all_attribute_values' does not contain 'attribute_value' remove that option from 'other_attribute_ids'.

like image 362
Paddy Hallihan Avatar asked Oct 01 '18 16:10

Paddy Hallihan


People also ask

How do I change the options in a dropdown list dynamically?

To add a dropdown list dynamically, you would need to create the HTML <select> element, its label and optionally a <br> tag. In pure JavaScript, you can use the document. createElement() method to programmatically create a dropdown list. Then you can call the Node's appendChild() method or jQuery's .

What is cascading dropdown?

A cascading drop-down list is a series of dependent DropDownList controls in which one DropDownList control depends on the parent or previous DropDownList controls. The items in the DropDownList control are populated based on an item that is selected by the user from another DropDownList control.

How do you populate values in one HTML dropdown list with another using simple JavaScript?

ready(function () { var list1 = document. getElementById('firstList'); list1. options[0] = new Option('--Select--', ''); list1. options[1] = new Option('Snacks', 'Snacks'); list1.


3 Answers

I have created a sample html based on your usecase. Solution is likewise, but you should get inspiration for solving yours.

I have considered independent attributes, so the solution will scale to new attributes with different values. I have also considered that server response is not editable.

I have a quick link in jsfiddle to checkout the solution

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>
like image 91
Faiz Mohamed Haneef Avatar answered Sep 29 '22 12:09

Faiz Mohamed Haneef


this would work with any number of dropdown.

you can generate random attributes to test.

$(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>
like image 23
Ercan Peker Avatar answered Sep 29 '22 13:09

Ercan Peker


I don't know if I have understood exactly what you are trying to explain or not but here's what I understood you have a website with suppose dropdowns for say clothes having attributes size, price, color, brand and you have an array of objects containing all of these attributes for each object.

I will be explaining this in reactjs since i am more familiar with it than php

so for each dropdown you can have an onchange handler which invokes a function where you check the values of the other dropdowns. If You select size M then enable and populate the dropdown list after filtering clothes with size M. when that is done invoke a similar function for checking the other attributes.

at this time all of your dropdowns will be active, now if the user makes any changes to the first dropdown i.e. size you can either reset the other dropdowns to be reselected or just pass the new list depending on how you are handling

Here is a similar thing i had done for setting date

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>
like image 42
Nishith Avatar answered Sep 29 '22 13:09

Nishith