Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tree-like Select in HTML

I'm trying to create a tree-like <select> using HTML and CSS.

To maintain accessibility I'd like to avoid javascript if possible. I'd also like to avoid using &nbsp; instead of padding, as this prevents pressing letter keys to jump to items.

What I have so far is this:

<select>
    <optgroup label="fluffy" style="padding-left: 10px;"></optgroup>
        <optgroup label="kitties" style="padding-left: 20px;"></optgroup>
            <option value="1" style="padding-left: 30px;">Fluffykins</option>
            <option value="2" style="padding-left: 30px;">Mr Pooky</option>
        <optgroup label="puppies" style="padding-left: 20px;"></optgroup>
            <option value="3" style="padding-left: 30px;">Doggins</option>

    <optgroup label="not fluffy" style="padding-left: 10px;"></optgroup>
        <optgroup label="snakes" style="padding-left: 20px;"></optgroup>
            <option value="4" style="padding-left: 30px;">Fingers</option>
        <optgroup label="crabs" style="padding-left: 20px;"></optgroup>
            <option value="5" style="padding-left: 30px;">Lucky (AKA Citizen Snips)</option>
</select>

This works fine in Firefox, but IE ignores the padding, rendering it as a flat list (quite hard to use) and Chrome doesn't render the <optgroup>s, which are technically not valid as an <optgroup> is supposed to contain at least on <option>.

Unfortunately <optgroup>s can't be nested.

This is how Firefox renders it

like image 500
Greg Avatar asked Aug 05 '09 09:08

Greg


People also ask

How do you create a hierarchy in HTML?

You can use unordered lists to create a hierarchy (the <ul> and <li> tags). This can give you a tree structure which is semantically valid, and on top of that you can do whatever you like to express it in interesting ways, either through CSS or Javascript.


2 Answers

With the SELECT element it will not work. You can create a custom SELECT based on this:

css:

    * {
        margin: 0;
        padding: 0;
    }

    .select, .select-tree {
        border: 1px solid black;
        width: 200px;
        overflow: hidden;
        list-style-type: none;
        margin: 10px;
    }

    .select .group-label {
        background-color: white;
    }

    .select .option-label {
        background-color: white;
        display: block;
    }

    .select .hidden-checkbox {
        display: none;
    }

    .select .hidden-checkbox:checked + .option-label,
    .select-tree .hidden-checkbox:checked ~ .group-children,
    .select-tree .hidden-checkbox:checked ~ .group-children * {
        background-color: lightblue;
    }

    .select-tree .group-children {
        list-style-type: none;
        padding-left: 20px;
    }
    .select-tree .option-label {
        padding-left: 5px;
    }

    .select-list .level-0 {
        padding-left: 10px;
    }

    .select-list .level-1 {
        padding-left: 20px;
    }

    .select-list .level-2 {
        padding-left: 30px;
    }

html:

select many from list (groups not selectable):

<ol class="select select-list select-many">
    <li>
        <input name="list1[1]" type="checkbox" id="check1" class="hidden-checkbox"/>
        <label for="check1" class="option-label level-0">option 1</label>
    </li>
    <li>
        <span class="group-label level-0">group 1</span>
    </li>
    <li>
        <input name="list1[2]" type="checkbox" id="check2" class="hidden-checkbox"/>
        <label for="check2" class="option-label level-1">option 2</label>
    </li>
    <li>
        <span class="group-label level-1">group 2</span>
    </li>
    <li>
        <input name="list1[3]" type="checkbox" id="check3" class="hidden-checkbox"/>
        <label for="check3" class="option-label level-2">option 3</label>
    </li>
    <li>
        <input name="list1[4]" type="checkbox" id="check4" class="hidden-checkbox"/>
        <label for="check4" class="option-label level-2">option 4</label>
    </li>
    <li>
        <input name="list1[5]" type="checkbox" id="check5" class="hidden-checkbox"/>
        <label for="check5" class="option-label level-0">option 5</label>
    </li>
</ol>

select one from list (groups not selectable):

<ol class="select select-list select-one">
    <li>
        <input name="list2" type="radio" id="check6" class="hidden-checkbox"/>
        <label for="check6" class="option-label level-0">option 1</label>
    </li>
    <li>
        <span class="group-label level-0">group 1</span>
    </li>
    <li>
        <input name="list2" type="radio" id="check7" class="hidden-checkbox"/>
        <label for="check7" class="option-label level-1">option 2</label>
    </li>
    <li>
        <span class="group-label level-1">group 2</span>
    </li>
    <li>
        <input name="list2" type="radio" id="check8" class="hidden-checkbox"/>
        <label for="check8" class="option-label level-2">option 3</label>
    </li>
    <li>
        <input name="list2" type="radio" id="check9" class="hidden-checkbox"/>
        <label for="check9" class="option-label level-2">option 4</label>
    </li>
    <li>
        <input name="list2" type="radio" id="check10" class="hidden-checkbox"/>
        <label for="check10" class="option-label level-0">option 5</label>
    </li>
</ol>

select one from tree (groups selectable):

<ol class="select select-tree select-one">
    <li>
        <input name="tree1" type="radio" id="check11" class="hidden-checkbox"/>
        <label for="check11" class="option-label">option 1</label>
    </li>
    <li>
        <input name="tree1" type="radio" id="check12" class="hidden-checkbox"/>
        <label for="check12" class="option-label group-label">group 1</label>
        <ol class="group-children">
            <li>
                <input name="tree1" type="radio" id="check13" class="hidden-checkbox"/>
                <label for="check13" class="option-label">option 2</label>
            </li>
            <li>
                <input name="tree1" type="radio" id="check14" class="hidden-checkbox"/>
                <label for="check14" class="option-label group-label">group 2</label>
                <ol class="group-children">
                    <li>
                        <input name="tree1" type="radio" id="check15" class="hidden-checkbox"/>
                        <label for="check15" class="option-label">option 3</label>
                    </li>
                    <li>
                        <input name="tree1" type="radio" id="check16" class="hidden-checkbox"/>
                        <label for="check16" class="option-label">option 4</label>
                    </li>
                </ol>
            </li>
        </ol>
    </li>
    <li>
        <input name="tree1" type="radio" id="check17" class="hidden-checkbox"/>
        <label for="check17" class="option-label">option 5</label>
    </li>
</ol>

IE9+ only (and you have to give the doctype for msie)... I think you cannot do it without "level-x" css classes, because by nested divs the padding would offset the colored background of the label too...

By IE8- you have to use javascript to colorize the labels...

like image 171
inf3rno Avatar answered Oct 21 '22 13:10

inf3rno


As you've noted, you can't nest one OPTGROUP within another. But you do have to enclose them. This will achieve at least the base level of indenting you're not already seeing.

<optgroup label="fluffy" style="padding-left: 10px;">
  <optgroup label="&nbsp;&nbsp;&nbsp;kitties" style="padding-left: 20px;">
     <option value="1" style="padding-left: 30px;">Fluffykins</option>
     <option value="2" style="padding-left: 30px;">Mr Pooky</option>
  </optgroup>
  <optgroup label="&nbsp;&nbsp;&nbsp;puppies" style="padding-left: 20px;">
     <option value="3" style="padding-left: 30px;">Doggins</option>
  </optgroup>
</optgroup>

Since you can't jump to the OPTGROUP headings with the keyboard anyway (and only to the actual OPTION), there should no problem padding the label out with &nbsp; to work across the cross-browser issues on padding.

like image 1
random Avatar answered Oct 21 '22 13:10

random