E.W
E.W

Reputation: 47

How to create new dropdown's depending on the choice in the previous dropdown

I am trying to make a jQuery that allows new dropdowns to appear depending on the choice of an initial dropdown. Example:

Dropdown 1 has the options: Car, Airplane. If I select Car a new dropdown will appear with the options: BMW, Audi, Tesla.

If Airplane is selected the next drop down will instead show: Airbus, Boeing.

If I select BMW in the second dropdown (Car->BMW) a third dropdown will show options like M3, M5 and so on (this is just examples for you to understand my problem). Basically I just want new dropdowns to appear depending on the choice of the previous one.

I tried something on my own like this:

HTML:
<form id="test" name="testSearch">
<select class="test1">
  <option value="option">Car</option>
  <option value="country">Airplane</option>
</select>

jQuery:
$('form[name="testSearch"] select').click(function () {
        $('div[name=carForm]').append('<select id="carBrand">'+
        '<option>BMW</option>'+
         '<option>Audi</option>'+
         '<option>Tesla</option>'+

This is not however not nice, I saw that it is possible to create div's with dropdowns and then make them visible depending on what choice you make in the previous dropdown. I think that is the way to go?

Sorry for no jsfiddle btw. Help is really appreciated.

Upvotes: 0

Views: 123

Answers (1)

Tyler Roper
Tyler Roper

Reputation: 21672

I started writing you a vague answer, but then ended up expanding on it so much that I made a full working example. I'll explain it all below, with some additional documentation linked at the very bottom.

var dropdowns = [
  {
    type: "Vehicle",
    options: ["Car", "Airplane"]
  },
  {
    parentType: "Vehicle",
    parentOption: "Car",
    type: "Make",
    options: ["Audi", "BMW"]
  },
  {
    parentType: "Vehicle",
    parentOption: "Airplane",
    type: "Type",
    options: ["737", "747"]
  },
  {
    parentType: "Make",
    parentOption: "Audi",
    type: "Model",
    options: ["A4", "A8L"]
  },
  {
    parentType: "Make",
    parentOption: "BMW",
    type: "Model",
    options: ["M3", "M5"]
  },
  {
    parentType: "Make",
    type: "Color",
    options: ["White", "Black", "Red"]
  }
];

function refreshDropdowns(parentType, selection, $container) {
  var $newContainer = $("<div class='container' />");

  if ($container)
    $container.append($newContainer);
  else
    $("body").append($newContainer);

  dropdowns
    .filter(i => i.parentType === parentType && (i.parentOption === selection || !i.parentOption))
    .map(i => {
      var $newDropdown = $("<select />").data("type", i.type);
      var $options = i.options.map(option => $("<option>").val(option).text(option));
      var $placeholderOption = $("<option>").text(`Select ${i.type}...`);
      $newDropdown
        .append($placeholderOption)
        .append($options);
      return $newDropdown;
    })
    .forEach($i => {
      if ($container) $newContainer.append($i)
      else {
        var $rootContainer = $("<div class='container' />");
        $rootContainer.appendTo($newContainer).append($i);
      }
    });
}

$(document).on("change", "select", function() {
  var $container = $(this).closest(".container");
  var type = $(this).data("type");
  var selection = $(this).val();

  $container.find(".container").remove();
  refreshDropdowns(type, selection, $container);
});

refreshDropdowns();
select {
  display: block;
  width: 150px;
  padding: 8px 4px;
  margin: 5px 0;
  border-radius: 5px;
}

.root-container {
  padding: 10px;
  margin: 10px;
  border: 1px solid #ccc;
}
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


1. Create a structure/list for your dropdowns

In my example, each has a type, an optional parentType (anything without a parentType is assumed to be always visible), and an optional specific parentOption. For example, the Model dropdown with M3 and M5 requires "BMW" to be selected for the Make. If you look at the object, this should make more sense.

This is extremely beneficial because now you can control everything from here, rather than diving into the code each time you want to update things.

var dropdowns = [
  {
    type: "Vehicle",
    options: ["Car", "Airplane"]
  },
  {
    parentType: "Vehicle",
    parentOption: "Car",
    type: "Make",
    options: ["Audi", "BMW"]
  },
  {
    parentType: "Vehicle",
    parentOption: "Airplane",
    type: "Type",
    options: ["737", "747"]
  },
  {
    parentType: "Make",
    parentOption: "Audi",
    type: "Model",
    options: ["A4", "A8L"]
  },
  {
    parentType: "Make",
    parentOption: "BMW",
    type: "Model",
    options: ["M3", "M5"]
  },
  {
    parentType: "Make",
    type: "Color",
    options: ["White", "Black", "Red"]
  }
];

2. Use that structure to determine which dropdowns to add/remove when selections change

function refreshDropdowns(parentType, selection, $container) {
  //All our new dropdowns should go in a container together.
  //This makes it easier to remove all children later on.
  var $newContainer = $("<div class='container' />");
  if ($container)
    $container.append($newContainer);
  else
    $("body").append($newContainer);

  //From our dropdown list, find any that depend on this dropdown and/or option
  dropdowns
    .filter(i => i.parentType === parentType && (i.parentOption === selection || !i.parentOption))

    //Now that we know which dropdowns we need to create, let's create the actual
    //<select> and <option> elements
    .map(i => {
      var $newDropdown = $("<select />").data("type", i.type);
      var $options = i.options.map(option => $("<option>").val(option).text(option));
      var $placeholderOption = $("<option>").text(`Select ${i.type}...`);
      $newDropdown
        .append($placeholderOption)
        .append($options);
      return $newDropdown;
    })

    //Add them all to the container. For root-level dropdowns, we'll need separate containers.
    .forEach($i => {
      if ($container) $newContainer.append($i)
      else {
        var $rootContainer = $("<div class='container' />");
        $rootContainer.appendTo($newContainer).append($i);
      }
    });
}

3. Call our function whenever a dropdown changes

//We use event delegation here to trigger events for dynamic elements
$(document).on("change", "select", function() {
  var $container = $(this).closest(".container");
  var type = $(this).data("type");
  var selection = $(this).val();

  //Remove any dropdowns that depend on this one
  $container.find(".container").remove();
  refreshDropdowns(type, selection, $container);
});

4. Call our function once on page-load just to create the root/parentless dropdowns

refreshDropdowns();

.filter()

.map()

Upvotes: 1

Related Questions