Home > front end >  Multiple nested select dropdown list dependent on input element in JavaScript
Multiple nested select dropdown list dependent on input element in JavaScript

Time:06-30

I have input element as range for years. I can change value of year by buttons. I would like to create nested list in second select that depends on year from range input and on first select - id countrySelect.

<input type="range" id="yearRange" min="0" max="3" list="yearsDatalist" value="3"
onChange="changeYear(value)" onInput="changeYearLite(value)">
    <datalist id="yearsDatalist">
        <option value="0" label="1918"></option>
        <option value="1" label="1936"></option>
        <option value="2" label="1950"></option>
        <option value="3" label="1981"></option>
    </datalist>

Then I have two select elements. The first one is static - not necessary to change the values there:

<select id="countrySelect">
    <option value="0">Sweden</option>
    <option value="1">Germany</option>
    <option value="2">USA</option>
</select>

Second select is dependent on year and on values in first select with id=countrySelect

<select id="itemSelection"></select>

I used object for values like this in some different project:

const yearsFilterDataList = {
    "1918": {
        "0": "Choose model",
        "volvo": "Volvo",
        "mercedes": "Mercedes",
        "saab": "Saab"
    }, ...

CodePudding user response:

I would suggest using a template literal for string interpolation in a querySelector to find the label attribute on the selected option.

function changeYear (value) {
  let selectedOption = document.querySelector(`option[value="${value}"]`);
  let selectedYear = selectedOption.label;
  
  let data = yearsFilterDataList[selectedYear];
  // Do what you want with the car information
}

Next time try to provide and organize your code (HTML, CSS, JS) in a better way so people don't have to guess what your setup exactly is. You also didn't provide the full yearsFilterDataList object so the slider only populates the select form on the first tick.

But here is an assumed implementation:

const yearsFilterDataList = {
    "1918": {
        "0": "Choose model",
        "volvo": "Volvo",
        "mercedes": "Mercedes",
        "saab": "Saab"
    }
}

function changeYearLite (value) {
    // ?
}
    
function changeYear (value) {
  let selectedOption = document.querySelector(`option[value="${value}"]`);
  let selectedYear = selectedOption.label;
  
  let data = yearsFilterDataList[selectedYear];
  if (!data) return console.warn('no data found')
  let wrapper = document.getElementById('itemSelection');
  
  Object.keys(data).forEach((key, index) => {
      let option = document.createElement('option');
      option.value = index;
      option.textContent = data[key];
      wrapper.appendChild(option);
  })
}
<input type="range" id="yearRange" min="0" max="3" list="yearsDatalist" value="3"
onChange="changeYear(value)" onInput="changeYearLite(value)">
<datalist id="yearsDatalist">
  <option value="0" label="1918"></option>
  <option value="1" label="1936"></option>
  <option value="2" label="1950"></option>
  <option value="3" label="1981"></option>
</datalist>

<select id="countrySelect">
    <option value="0">Sweden</option>
    <option value="1">Germany</option>
    <option value="2">USA</option>
</select>

<select id="itemSelection"></select>

CodePudding user response:

I have created a fiddle where I have created an optgroup for each year-country pair, each optgroup has two elements, depending on the ids of the year and country (you can of course change the options of each optgroup, the lone reason for this kind of option definition was to ease my testing). Changing the value of yearRange or yearsDatalist triggers a call to changeOptions, which finds these elements, defines the id of the optgroup to show, then loops the optgroups and hides everything except the one that matches the id. The matching optgroup will be shown and, since any prior selection is logically invalidated, I update itemSelection.value to the value of the first child of the chosen optgroup.

Finally I call changeOptions anyway, in order to initialize a proper state even before any of the triggers are triggered.

function changeOptions() {
    let yearRange = document.getElementById("yearRange").value;
    let countrySelect = document.getElementById("countrySelect").value;
    let id = `options-${yearRange}-${countrySelect}`;
    let itemSelection = document.getElementById("itemSelection");
    for (let optgroup of itemSelection.children) {
        let match = optgroup.id === id;
        optgroup[(match ? "remove" : "set")   "Attribute"]("hidden", "hidden");
        if (match) {
            itemSelection.value = optgroup.children[0].value;
        }
    }
}

changeOptions();
<input type="range" id="yearRange" min="0" max="3" list="yearsDatalist" value="3"
onChange="changeOptions()" onInput="changeOptions()">
    <datalist id="yearsDatalist">
        <option value="0" label="1918"></option>
        <option value="1" label="1936"></option>
        <option value="2" label="1950"></option>
        <option value="3" label="1981"></option>
    </datalist>

<select id="countrySelect" onchange="changeOptions()">
    <option value="0">Sweden</option>
    <option value="1">Germany</option>
    <option value="2">USA</option>
</select>

<select id="itemSelection">
    <optgroup id="options-0-0">
        <option value="0-0-1">0-0-1</option>
        <option value="0-0-1">0-0-2</option>
    </optgroup>
    <optgroup id="options-0-1">
        <option value="0-1-1">0-1-1</option>
        <option value="0-1-1">0-1-2</option>
    </optgroup>
    <optgroup id="options-0-2">
        <option value="0-2-1">0-2-1</option>
        <option value="0-2-1">0-2-2</option>
    </optgroup>
    <optgroup id="options-0-3">
        <option value="0-3-1">0-3-1</option>
        <option value="0-3-1">0-3-2</option>
    </optgroup>
    <optgroup id="options-1-0">
        <option value="1-0-1">1-0-1</option>
        <option value="1-0-1">1-0-2</option>
    </optgroup>
    <optgroup id="options-1-1">
        <option value="1-1-1">1-1-1</option>
        <option value="1-1-1">1-1-2</option>
    </optgroup>
    <optgroup id="options-1-2">
        <option value="1-2-1">1-2-1</option>
        <option value="1-2-1">1-2-2</option>
    </optgroup>
    <optgroup id="options-1-3">
        <option value="1-3-1">1-3-1</option>
        <option value="1-3-1">1-3-2</option>
    </optgroup>
    <optgroup id="options-2-0">
        <option value="2-0-1">2-0-1</option>
        <option value="2-0-1">2-0-2</option>
    </optgroup>
    <optgroup id="options-2-1">
        <option value="2-1-1">2-1-1</option>
        <option value="2-1-1">2-1-2</option>
    </optgroup>
    <optgroup id="options-2-2">
        <option value="2-2-1">2-2-1</option>
        <option value="2-2-1">2-2-2</option>
    </optgroup>
    <optgroup id="options-2-3">
        <option value="2-3-1">2-3-1</option>
        <option value="2-3-1">2-3-2</option>
    </optgroup>
    <optgroup id="options-3-0">
        <option value="3-0-1">3-0-1</option>
        <option value="3-0-1">3-0-2</option>
    </optgroup>
    <optgroup id="options-3-1">
        <option value="3-1-1">3-1-1</option>
        <option value="3-1-1">3-1-2</option>
    </optgroup>
    <optgroup id="options-3-2">
        <option value="3-2-1">3-2-1</option>
        <option value="3-2-1">3-2-2</option>
    </optgroup>
    <optgroup id="options-3-3">
        <option value="3-3-1">3-3-1</option>
        <option value="3-3-1">3-3-2</option>
    </optgroup>
</select>

  • Related