TransWikia.com

Expanding Object to Create Nested Conditional Dropdowns

Stack Overflow Asked by James on September 3, 2020

I have some javascript that displays dropdowns based on the selection of the previous dropdown. Right now, the object has two types (car brands), which upon it’s selection will display models that relate the respective car brands.

However, I need to extend it one level further, so that selecting, for example, "Audi > r8" will show options X, Y, and Z, while selecting "Audi > a4" will show options L, M, and N in the third dropdown (which is currently empty). Here’s the demo of my code.

var data = {
  "audi": ["a4","a5","r8"],
  "volkswagen": ["Bug","Jetta","Golf"]
};

var make = document.querySelector("#cars");
var model = document.querySelector("#model");
var software = document.querySelector("#software");
var link = document.querySelector("#link");

make.addEventListener("change",function(){
  if(make.value != ""){
    var models = data[make.value];
    var length = model.options.length;
    for (i = length-1; i >= 0; i--) {model.options[i] = null;}
    link.innerHTML = "";
    
    let opt = document.createElement("option");
    opt.text = "Select " + make.value + " Model";
    opt.value = "";
    model.appendChild(opt);
    
    models.forEach(function(k,v){
    let opt = document.createElement("option");
    opt.text = k;
    opt.value = k;
      model.appendChild(opt);
  });
  }
});

model.addEventListener("change",function(){
  if(model.value != ""){
     link.innerHTML = '<a href="www.car.com/' + make.value + '/' + model.value + '"</a>' + '<h4>' + make.value + '/' + model.value + '</h4>';
  }
});
<form>
    <select name="cars" id="cars">
      <option value="" disabled selected>Select your Car</option>
        <option value="audi">Audi</option>
        <option value="volkswagen">Volkswagen</option>
    </select>
    <select name="model" id="model"></select>
    <select name="software" id="software"></select>
  </form>
  <div id="link">
  
  </div>

My trouble is, given my unfamiliarity with JS objects, I can’t add additional elements to the car models within the array. How would I successfully embed another object within my data variable for each car model, and get it to display in the dropdown?

2 Answers

You could change your lists to objects so that you can have lists inside of them. like this:

const data = {
    audi: {
        a4: ["L", "M", "N"],
        a5: ["L", "M", "N"],
        r8: ["L", "M", "N"],
    },
    volkswagen: {
        Bug: ["L", "M", "N"],
        Jetta: ["L", "M", "S"],
        Golf: ["L", "M", "N"],
    }
};

I changed the variable to a constant because I don't see anywhere in your code a reassignment of it (I also did this with other variables in the rest of the code).

With that structure in mind, your code could look something like this:

const make = document.querySelector("#cars");
const model = document.querySelector("#model");
const software = document.querySelector("#software");
const link = document.querySelector("#link");

function clearChildren(element) {
    while (element.firstChild) {
        element.removeChild(element.firstChild);
    }
}

make.addEventListener("change", function () {
    if (make.value != "") {
        const models = data[make.value];

        clearChildren(model);
        clearChildren(software);

        link.innerHTML = "";

        const opt = document.createElement("option");
        opt.text = "Select " + make.value + " Model";
        opt.value = "";
        model.appendChild(opt);

        Object.keys(models).forEach(k => {
            const opt = document.createElement("option");
            opt.text = k;
            opt.value = k;
            model.appendChild(opt);
        });
    }
});

model.addEventListener("change", function () {
    if (model.value != "") {
        const options = data[make.value][model.value];

        clearChildren(software);

        link.innerHTML = "";

        const opt = document.createElement("option");
        opt.text = "Select the software for" + model.value;
        opt.value = "";
        software.appendChild(opt);



        options.forEach(k => {
            const opt = document.createElement("option");
            opt.text = k;
            opt.value = k;
            software.appendChild(opt);
        });
    }
});


software.addEventListener("change", function () {
    if (model.value != "") {
        // I don't know how to format the link here
    }
});

I also changed some of your anonymous functions to the arrow syntax, I did it only because I think they look better.

Correct answer by Felipe Trost on September 3, 2020

There's different ways you could do this, but this is the way that first came to me. You can expand you current data object by converting the array to an object and then use the keys as Makes and the values of that object as an array of your end value, such that:

var data = {
  "audi": {
    "a4": ['x', 'y', 'z'],
    "a5": ['x', 'y', 'z'],
    "r8": ['x', 'y', 'z']
  },
  "volkswagen": {
    "Bug": ['x', 'y', 'z'],
    "Jetta": ['x', 'y', 'z'],
    "Golf": ['x', 'y', 'z']
   }
};

You will then have to adjust how you access the Models so instead of:

var models = data[make.value]

You can access the models by:

var models = Object.keys(data[make.value])

To then access the software array:

var softwares = data[make.value][model.value]

You can then implement similar code you used within your make-change event listener inside your model-change event listener to adjust the new options needed. Then, finally I'm guessing you'll also need to add a software change event listener as well.

Answered by Justin Levine on September 3, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP