Areeb
Areeb

Reputation: 48

Want to make something like conditional select options in a HTML Form

I want to make a form in which the next field appears based on input done in the previous field by the user.

eg: If The user selects Beverages then below it show a fieldset with different beverages with checkbox, and if user select snacks then below it show fieldset with snacks items

I was trying it to do like this:

<select id="first-choice">
  <option selected value="base">Please Select</option>
  <option value="beverages">Beverages</option>
  <option value="snacks">Snacks</option>
</select>

<br>

<select id="second-choice">
  <option>Please choose from above</option>
</select>

JSON Data

{
  "beverages": "Coffee,Coke",
  "snacks": "Chips,Cookies"
}

Jquery

$("#first-choice").change(function() {

    var $dropdown = $(this);

    $.getJSON("jsondata/data.json", function(data) {
    
        var key = $dropdown.val();
        var vals = [];
                            
        switch(key) {
            case 'beverages':
                vals = data.beverages.split(",");
                break;
            case 'snacks':
                vals = data.snacks.split(",");
                break;
            case 'base':
                vals = ['Please choose from above'];
        }
        
        var $secondChoice = $("#second-choice");
        $secondChoice.empty();
        $.each(vals, function(index, value) {
            $secondChoice.append("<option>" + value + "</option>");
        });

    });
});

I not only want append select option, I actually want to make a new division with text fields and file uploads etc

just guide me how to do it in a compact/dynamic way

Upvotes: 2

Views: 4565

Answers (1)

By all means keep the JS that fills the selector elements (but make your options map a thing that's known before the user even gets to pick anything: don't rely on a network transfer for that!), but don't try to get the whole "conditional showing" to work in JS: CSS can already do that, and it'll do it better. You just need to remember to use the correct HTML markup:

// Load this script via src="..." with `async` and `defer` attributes
// so that it'll run before the user gets to interact with the page,
// after the DOM has been constructed. A feature that's been universal
// since IE10, so there's no reason to still put scripts at the end of
// the page, or listening for DOMContentLoaded/ready events.

const first = document.getElementsByName('first-value')[0];
const second = document.getElementsByName('second-value')[0];
const initial = second.innerHTML;

// Either hard code this, or get it on page load, just make sure
// it's already available before users start picking values!
const optionMap = {
  a: ['i', 'j', 'k'],
  b: ['u', 'v', 'w'],
  c: ['x', 'y', 'z'],
};

function addOption(selectElement, text) {
  let option = document.createElement('option');
  option.value = text;
  option.textContent = text;
  selectElement.append(option);
}

// Fill the first selector
Object.keys(optionMap).forEach(text => addOption(first, text));

// And only fill the second selector when we know the first value
first.addEventListener('change', evt => {
  second.innerHTML = initial;
  optionMap[evt.target.value].forEach(text => addOption(second, text));
});
select:not(:valid) {
  border: 1px solid red;
}

select:not(:valid) + .followup {
  display: none;
}
<select required name="first-value">
  <option disabled selected>please select one</option>
</select>
 
<select required class="followup" name="second-value">
  <option disabled selected>please select one more</option>
</select>

The trick here is to make sure you have an option that is both disabled and selected. The latter because <select> elements always have an option selected, but any option marked as disabled does not count as a valid choice (this lets you for instance put labels in a selector element).

So, we make a first <option> that is simply a label, but also make sure the selector always starts with that option selected. As it's disabled, that makes the selector invalid as far as form posting is concerned, so we can use the CSS :valid pseudo class to do all kinds of useful things, like hiding any adjacent element until the main select element is valid.

And of course you can still "fill" the second selector using JS, with an event listener on the first selector so that its change triggers some JS that appends a bunch of option elements to the second one, but this is really something you want to do without a network request: have your code already know which primary values map to which arrays of secondary values by doing a fetch for the full mapping on pageload, or even hardcoded it (e.g. during your site building step, or even manually)

Upvotes: 4

Related Questions