Nesting optgroups in a dropdownlist/select

133,450

Solution 1

Ok, if anyone ever reads this: the best option is to add four  s at each extra level of indentation, it would seem!

so:

<select>
 <optgroup label="Level One">
  <option> A.1 </option>
  <optgroup label="&nbsp;&nbsp;&nbsp;&nbsp;Level Two">
   <option>&nbsp;&nbsp;&nbsp;&nbsp; A.B.1 </option>
  </optgroup>
  <option> A.2 </option>
 </optgroup>
</select>

Solution 2

The HTML spec here is really broken. It should allow nested optgroups and recommend user agents render them as nested menus. Instead, only one optgroup level is allowed. However, they do have to say the following on the subject:

Note. Implementors are advised that future versions of HTML may extend the grouping mechanism to allow for nested groups (i.e., OPTGROUP elements may nest). This will allow authors to represent a richer hierarchy of choices.

And user agents could start using submenus to render optgoups instead of displaying titles before the first option element in an optgroup as they do now.

Solution 3

This is just fine but if you add option which is not in optgroup it gets buggy.

<select>
  <optgroup label="Level One">
    <option> A.1 </option>
    <optgroup label="&nbsp;&nbsp;&nbsp;&nbsp;Level Two">
      <option>&nbsp;&nbsp;&nbsp;&nbsp; A.B.1 </option>
    </optgroup>
    <option> A.2 </option>
  </optgroup>
  <option> A </option>
</select>

Would be much better if you used css and close optgroup right away :

<select>
  <optgroup label="Level One"></optgroup>
  <option style="padding-left:15px"> A.1 </option>
  <optgroup label="Level Two" style="padding-left:15px"></optgroup>
  <option style="padding-left:30px"> A.B.1 </option>
  <option style="padding-left:15px"> A.2 </option>
  <option> A </option>
</select>

Solution 4

<style>
  .NestedSelect{display: inline-block; height: 100px; border: 1px Black solid; overflow-y: scroll;}
  .NestedSelect label{display: block; cursor: pointer;}
  .NestedSelect label:hover{background-color: #0092ff; color: White;}
  .NestedSelect input[type="radio"]{display: none;}
  .NestedSelect input[type="radio"] + span{display: block; padding-left: 0px; padding-right: 5px;}
  .NestedSelect input[type="radio"]:checked + span{background-color: Black; color: White;}
  .NestedSelect div{margin-left: 15px; border-left: 1px Black solid;}
  .NestedSelect label > span:before{content: '- ';}
</style>

<div class="NestedSelect">
  <label><input type="radio" name="MySelectInputName"><span>Fruit</span></label>
  <div>
    <label><input type="radio" name="MySelectInputName"><span>Apple</span></label>
    <label><input type="radio" name="MySelectInputName"><span>Banana</span></label>
    <label><input type="radio" name="MySelectInputName"><span>Orange</span></label>
  </div>

  <label><input type="radio" name="MySelectInputName"><span>Drink</span></label>
  <div>
    <label><input type="radio" name="MySelectInputName"><span>Water</span></label>

    <label><input type="radio" name="MySelectInputName"><span>Soft</span></label>
    <div>
      <label><input type="radio" name="MySelectInputName"><span>Cola</span></label>
      <label><input type="radio" name="MySelectInputName"><span>Soda</span></label>
      <label><input type="radio" name="MySelectInputName"><span>Lemonade</span></label>
    </div>

    <label><input type="radio" name="MySelectInputName"><span>Hard</span></label>
    <div>
      <label><input type="radio" name="MySelectInputName"><span>Bear</span></label>
      <label><input type="radio" name="MySelectInputName"><span>Whisky</span></label>
      <label><input type="radio" name="MySelectInputName"><span>Vodka</span></label>
      <label><input type="radio" name="MySelectInputName"><span>Gin</span></label>
    </div>
  </div>
</div>

Solution 5

I really like the Broken Arrow's solution above in this post. I have just improved/changed it a bit so that what was called labels can be toggled and are not considered options. I have used a small piece of jQuery, but this could be done without jQuery.

I have replaced intermediate labels (no leaf labels) with links, which call a function on click. This function is in charge of toggling the next div of the clicked link, so that it expands/collapses the options. This avoids the possibility of selecting an intermediate element in the hierarchy, which usually is something desired. Making a variant that allows to select intermediate elements should be easy.

This is the modified html:

<div class="NestedSelect">
    <a onclick="toggleDiv(this)">Fruit</a>
    <div>
        <label>
            <input type="radio" name="MySelectInputName"><span>Apple</span></label>
        <label>
            <input type="radio" name="MySelectInputName"><span>Banana</span></label>
        <label>
            <input type="radio" name="MySelectInputName"><span>Orange</span></label>
    </div>

    <a onclick="toggleDiv(this)">Drink</a>
    <div>
        <label>
            <input type="radio" name="MySelectInputName"><span>Water</span></label>

        <a onclick="toggleDiv(this)">Soft</a>
        <div>
            <label>
                <input type="radio" name="MySelectInputName"><span>Cola</span></label>
            <label>
                <input type="radio" name="MySelectInputName"><span>Soda</span></label>
            <label>
                <input type="radio" name="MySelectInputName"><span>Lemonade</span></label>
        </div>

        <a onclick="toggleDiv(this)">Hard</a>
        <div>
            <label>
                <input type="radio" name="MySelectInputName"><span>Bear</span></label>
            <label>
                <input type="radio" name="MySelectInputName"><span>Whisky</span></label>
            <label>
                <input type="radio" name="MySelectInputName"><span>Vodka</span></label>
            <label>
                <input type="radio" name="MySelectInputName"><span>Gin</span></label>
        </div>
    </div>
</div>

A small javascript/jQuery function:

function toggleDiv(element) {
    $(element).next('div').toggle('medium');
}

And the css:

.NestedSelect {
    display: inline-block;
    height: 100%;
    border: 1px Black solid;
    overflow-y: scroll;
}

.NestedSelect a:hover, .NestedSelect span:hover  {
    background-color: #0092ff;
    color: White;
    cursor: pointer;
}

.NestedSelect input[type="radio"] {
    display: none;
}

.NestedSelect input[type="radio"] + span {
    display: block;
    padding-left: 0px;
    padding-right: 5px;
}

.NestedSelect input[type="radio"]:checked + span {
    background-color: Black;
    color: White;
}

.NestedSelect div {
    display: none;
    margin-left: 15px;
    border-left: 1px black
    solid;
}

.NestedSelect label > span:before, .NestedSelect a:before{
    content: '- ';
}

.NestedSelect a {
    display: block;
}

Running sample in JSFiddle

Share:
133,450

Related videos on Youtube

Ed James
Author by

Ed James

Updated on October 30, 2021

Comments

  • Ed James
    Ed James over 2 years

    I have created a customer c# DropDownList control that can render out it's contents are optgroups (Not from scratch, I edited some code found on the internet, although I do understand exactly what it's doing), and it works fine.

    However, I have now come across a situation where I need to have two levels of indentation in my dropdown, i.e.

    <select>
      <optgroup label="Level One">
        <option> A.1 </option>
        <optgroup label="Level Two">
          <option> A.B.1 </option>
        </optgroup>
        <option> A.2 </option>
      </optgroup>
    </select>

    However, in the example snippet above, it is rendering as if Level Two was at the same amount of indentation as Level One.

    Is there a way to produce the nested optgroup behavior I am looking for?

  • Ed James
    Ed James about 15 years
    Not my design, I just implement things, although I agree that it's reasonably insane. Part of the joy of being brought in to work on a pre-agreed design.
  • Xeoncross
    Xeoncross over 13 years
    Perfect, much easier since you can do style="padding-left:'. (15 * $level). 'px" in a loop over a tree of items.
  • Markus
    Markus about 13 years
    unfortunately this does not work in safari, since padding in <option> is not rendered
  • Geoffrey Bachelet
    Geoffrey Bachelet about 13 years
    By doing that you literraly kill your select's usability, by not allowing users to press the first letter of an item to quick-scroll to that letter.
  • Potherca
    Potherca about 13 years
    CSS to the rescue: you can remove the padding-left and use text-indent instead, adding the same amount to your select box width (source: stackoverflow.com/questions/2966855/…)
  • inf3rno
    inf3rno over 12 years
    That's true, you cannot create a multiple level nested tree with option groups, neither with html 5, also the note is false.
  • Jake Rayson
    Jake Rayson almost 12 years
    Paradoxically, Geoffrey, it simultaneously improves usability by providing a visual hierarchy. Funny old world.
  • Sweepster
    Sweepster over 11 years
    Anyone know if they plan to visit this in HTML5? Seems like a massive oversight.
  • Ryan
    Ryan over 10 years
    Another reason this is not ideal is a selected option with a lot of indent looks unusual when the select is not active because of all the padding
  • mpen
    mpen over 10 years
    Inspect the DOM... the optgroups are not actually nested. At least in Firefox. It closes the first optgroup when it sees the 2nd.
  • Dan Atkinson
    Dan Atkinson over 10 years
    According to the HTML5 spec (dev.w3.org/html5/markup/optgroup.html#optgroup) the only permitted parent of optgroup is select, which suggest that no, this is not supported in HTML5.
  • Pat
    Pat about 10 years
    Can you add some sample CSS prettiness for those of us who do not know CSS as well as maybe we should?
  • Nj Subedi
    Nj Subedi almost 10 years
    I would consider this as a brilliant solution.
  • dishwasherWithProgrammingSkill
    dishwasherWithProgrammingSkill almost 10 years
    I ran across the same issue in chrmoe as @Mark has mentioned. The optgroups does not actually get nested. stackoverflow.com/questions/25726335/…
  • DarkSide
    DarkSide over 9 years
    And Google Chrome doesn't render HTML codes inside <option></option> tags. As result you'll simply see &nbsp; as text in Google Chrome
  • Ed James
    Ed James over 9 years
    @DarkSide This is a very very old question / answer pair so I was happy to give that the benefit of the doubt but I just tried it in Chrome 38.0.2125.104 and nope, works as expected.
  • DarkSide
    DarkSide over 9 years
    @EdWoodcock yeah, sorry, in Chrome it works too! My mistake was that I entered &nbsp; in Chromes console directly and in that case Chrome automatically converts special symbols to text.
  • Cerbrus
    Cerbrus over 9 years
    This is nothing more than a nested list. This completely misses the point of the question, which is to have some kind of collapsible list.
  • James Billingham
    James Billingham over 9 years
    @Cerbrus Maybe you should read the question again. At no point does he state that the sections need to be collapsible. The specific problem he was looking to solve was a method of having nested <optgroup>s which indent. Obviously one can make HTML render like a dropdown with some very simple CSS and/or JS, but OP was not looking for help with that.
  • Broken Arrow
    Broken Arrow about 9 years
    This will automatically work for UNLIMITED nesting level without adding/modifying any CSS/Style attribute.
  • Snæbjørn
    Snæbjørn about 9 years
    Anyone know if nested optgroups are on the roadmap?
  • NetVicious
    NetVicious almost 9 years
    Please, use paddings instead &nbsp;. Thats really an ugly solution when you have other answers with padding style.
  • Ed James
    Ed James almost 9 years
    @NetVicious Unless someone fixed padding in WebKit browsers (they may have, I haven't looked) it's unfortunately not a decent solution. text-indent may be possible, from a comment on another answer, but I've not looked at this in years and so will leave this answer marked correct until someone provides a better cross-browser solution.
  • Donny van V
    Donny van V over 8 years
    Awesome mate! Looks great! :D
  • Rasel
    Rasel almost 8 years
    Awesome answer. SK joy vai
  • Suncat2000
    Suncat2000 almost 8 years
    That's not buggy, it's defined that way. Option A is not part of a group.
  • Suncat2000
    Suncat2000 almost 8 years
    Adding CSS classes to control the indenting would solve the OP's question without impairing the functionality of the select. Good idea, as optgroups can't be nested.
  • Suncat2000
    Suncat2000 almost 8 years
    This is a decent answer for how to present things visually, but a list tag is not the equivalent of a select. You would need more CSS and Javascript code to make it behave like one.
  • Andrew Hows
    Andrew Hows about 6 years
    A better option is &ensp; (&#8194;) These don't render when the dropbox is in the closed state, but do when it's open. And they work they keyboard navigation, at least in the current chrome version. Tip from @evilkos on stackoverflow.com/questions/1146789/…
  • Andrew Hows
    Andrew Hows about 6 years
    And no, padding/margin still don't apply to <option> elements. As far as I'm aware, this is according to the specification, and is not a bug.
  • BallpointBen
    BallpointBen over 3 years
    @GeoffreyBachelet Safari seems to ignore leading whitespace in options
  • HoldOffHunger
    HoldOffHunger over 3 years
    Geoffrey's comment up above is way outdated. Obviously a browser can be coded to ignore space when typing in a select dropdown -- that is exactly what Chrome is doing right now as I test this.
  • HoldOffHunger
    HoldOffHunger over 3 years
    Why do you have code for margin-left: 1em; when it's not doing anything and you end up using &nbsp;s anyway just to make the margin appear? Your CSS code here is doing nothing at all. Demo of Left Margins Not Working
  • Matija Nalis
    Matija Nalis over 3 years
    @HoldOffHunger note that the answer above is from 2015 and has special isFirefox() handling - back then firefox didn't need &nbsp; (which was breaking search-as-you-type functionality of dropdown lists) and it was honoring margins in dropdown lists back then (sadly, at least as with Firefox 78.5, it no longer works that way)