Cascade dropdown using JS not working

2019-05-18 05:17发布

I have 3 dropdown, the values filled dynamically with mysql. Now, I'm trying to cascade the 3 dropdown, but JS script is not working.

What I'm trying to do's:

Case 1: If the user choose a value from dropdown #1, the value of dropdown #2 depends on dropdown #1, the value of dropdown #3 depends on dropdown #2. E.g. State - City - Avenue

Case 2: The user can choose from 3 dropdown.

Case 3 and 4: If the user choose from dropdown #2, the values of dropdown #3 depends in dropdown #2 (but choosing a value in dropdown #3 is optional, if the dropdown #2 have already)

Form:

<form action='' method='post' id='loc'>
<select name="state" id="filter_region" class="state">
    <option name="default" class="default" value="State" readonly>State</option>
    <?php
    foreach($result_state as $option){
        if(isset($_POST['state']) && $_POST['state'] == $option->state)
            echo '<option name="state" class="filter_by" selected value="'. $option->state .'">'. $option->state .'</option>';
        else    
         echo '<option name="state" class="filter_by" value="'. $option->state .'">'. $option->state .'</option>';
     };
    ?>
</select>

<select name="city" id="filter_city" class="city">
    <option name="default" class="default" value="City" readonly>City</option>
    <?php
    foreach($result_city as $option){
        if(isset($_POST['city']) && $_POST['city'] == $option->city)
            echo '<option name="city" class="filter_by" selected value="'. $option->city .'">'. $option->city .'</option>';
        else    
         echo '<option name="city" class="filter_by" value="'. $option->city .'">'. $option->city .'</option>';
     };
    ?>
</select>
<select name="avenue" id="filter_mall" class="avenue">
    <option name="default" class="default" value="Avenue" readonly>Avenue</option>
    <?php 
    foreach($result_avenue as $option){
        if(isset($_POST['avenue']) && $_POST['avenue'] == $option->avenue)
            echo '<option name="avenue" class="default" selected value="'. $option->avenue .'">'. $option->avenue .'</option>';
        else    
         echo '<option name="avenue" class="filter_by" value="'. $option->avenue .'">'. $option->avenue .'</option>';
     };
    ?>
</select>
<input type="submit" value="submit" class="submit"/>
</form>

JS:

function cascadeSelect(parent, child){
    var childOptions = child.find('option:not(.default)');
    child.data('options',childOptions);

    parent.change(function(){
        childOptions.remove();
        child
            .append(child.data('options').filter('.filter_by' + this.value))
            .change();
    })

    childOptions.not('.default, .filter_by' + parent.val()).remove();
}

$(function(){
    cascadeForm = $('.loc');
    state= cascadeForm.find('.state');
    city= cascadeForm.find('.city');
    avenue= cascadeForm.find('.avenue');

    cascadeSelect(state, city);
    cascadeSelect(city, avenue);
});

1条回答
倾城 Initia
2楼-- · 2019-05-18 05:22

I had created cascading parent-child select-boxes using AngularJS, but since you are using NativeJS, I tried to re-create using JS. The pre-requisite of my solution is a well-defined JSON based on which the select-boxes will be created. You'll have to create the JSON on server side or manually or wherever you are creating the select-box data. Below is the JSON format.:

Every select-box is a named object with following properties:

  1. Parent Attribute: Name of object which is the parent of this select-box object.
  2. Options: Array of option objects, where each object contains: (a) Option Value (b) Parent Option Value - The parent select-box value with which the current value is mapped. (c) Option ID.

  3. Selected Option: An object with two properties: (a) Currently selected value (b) ID of currently selected value.

enter image description here

Here is the working Plunker of the solution. Hope it helps.

----- EDIT: -----

On request of @User014019

Here is the another version of this drop-down solution where all the options in all select-boxes are visible initially, and parent-child relationships are set when user select a particular value.


Below is the code:

// reads the data and creates the DOM elements (select-boxes and their relevant options)
function initSelect(data) {
  var select, option, input, filteredOptions;
  for (var key in data) {
    select = document.createElement("select");
    select.name = key;
    container.appendChild(select);
    filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
    input = document.querySelector('select[name="' + key + '"');
    input.setAttribute("onchange", "updateSelect(this)");
    for (var i = 0; i < filteredOptions.length; i++) {
      option = document.createElement("option");
      option.value = filteredOptions[i].value;
      option.innerHTML = filteredOptions[i].value;
      input.appendChild(option);
    }
    input.options.selectedIndex = getSelectedIndex(filteredOptions, data[key]);
    input.setAttribute('model', data[key].selectedOption.value);
  }
}


// this function will be called on change of select-box
function updateSelect(element) {
  var input, option;
  setSelectedOption(element);
  for (var key in data) {
    filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
    input = document.querySelector('select[name="' + key + '"');
    while (input.firstChild) {
      input.removeChild(input.firstChild);
    }
    for (var i = 0; i < filteredOptions.length; i++) {
      option = document.createElement("option");
      option.value = filteredOptions[i].value;
      option.innerHTML = filteredOptions[i].value;
      input.appendChild(option);
    }
    input.options.selectedIndex = getSelectedIndex(filteredOptions, data[key]);
    input.setAttribute('model', data[key].selectedOption.value);
  }
}

// set the selected-option of select-box when it's changed
function setSelectedOption(element) {
  var inputName = element.getAttribute("name");
  var inputValue = getSelectedText(element);
  var inputItem = data[inputName];
  var selectedOption, filteredOptions;

  // setting selected option of changed select-box
  for (var i = 0; i < inputItem.availableOptions.length; i++) {
    if (inputValue === inputItem.availableOptions[i].value) {
      inputItem.selectedOption = inputItem.availableOptions[i];
      break;
    }
  }
  // setting child object selected option now
  for (var key in data) {
    if (data[key].parent === inputName) {
      filteredOptions = optionFilter(data[key].availableOptions, data[data[key].parent], data[key]);
      data[key].selectedOption = filteredOptions[0];
    }
  }
}

// get the text of select-box
function getSelectedText(element) {
  if (element.selectedIndex == -1) {
    return null;
  }
  return element.options[element.selectedIndex].text;
}

function getSelectedIndex(options, self) {
  var index;
  for (var i = 0; i < options.length; i++) {
    if (self.selectedOption.value === options[i].value) {
      index = i;
      break;
    }
  }
  return index;
}

// get filtered options based on parent's selected value
function optionFilter(items, parent, self) {
  var result = [];
  if (typeof parent !== "undefined") {
    for (var i = 0; i < items.length; i++) {
      if (typeof parent.selectedOption !== "undefined") {
        if (parent.selectedOption !== null && items[i].parentValue === parent.selectedOption.value) {
          result.push(items[i]);
        }
      }
    }
    if (typeof self.selectedOption === "undefined") {
      self.selectedOption = null;
    }
    if (self.selectedOption === null) {
      self.selectedOption = result[0];
    }
    return result;
  } else {
    return items;
  }
}
查看更多
登录 后发表回答