将optgroups嵌套在dropdownlist / select中

我已经创build了一个客户的c#DropDownList控件,可以渲染出它的内容是optgroups(不是从头开始,我编辑了一些在互联网上find的代码,虽然我确实明白它在做什么),它工作正常。

但是,现在我遇到了一个情况,那就是我需要在下拉菜单中有两个缩进级别

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

但是,据我所知,这只会像正常的select组一样呈现。

有没有办法产生这种嵌套的行为?

这里的HTML规范是真的打破了。 它应该允许嵌套的optgroups,并build议用户代理将它们呈现为嵌套菜单。 相反, 只允许一个optgroup级别 。 但是,他们不得不在这个问题上说:

注意。 实现者被告知未来版本的HTML可以扩展分组机制以允许嵌套组(即,OPTGROUP元素可以嵌套)。 这将使作者代表更丰富的select层次。

并且用户代理可以开始使用子菜单来呈现optgoups,而不是像在他们现在那样在optgroup中的第一个选项元素之前显示标题。

这是很好,但如果你添加选项女巫是不是在optgroup它得到越野车。

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

如果你使用css并立即closuresoptgroup会好得多:

 <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"> AB1 </option> <option style="padding-left:15px"> A.2 </option> <option> A </option> </select> 

好的,如果有人读到这个:最好的select是添加四个&nbsp; 在每个额外的缩进级别,它似乎!

所以:

 <select> <optgroup label="Level One"> <option> A.1 </option> <optgroup label="&nbsp;&nbsp;&nbsp;&nbsp;Level Two"> <option>&nbsp;&nbsp;&nbsp;&nbsp; AB1 </option> </optgroup> <option> A.2 </option> </optgroup> </select> 
 <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> 

我认为,如果你有一些结构复杂的东西,你可能会考虑一些不同于一个下拉框的东西。

我知道这是相当一段时间,但是我有一点额外的补充:

这在HTML5或任何以前的规范中是不可能的,HTML5.1中也没有提出。 我已经向public-html-comments邮件列表发出了一个请求,但是我们会看看是否有任何内容。

无论如何,尽pipe使用<select>这是不可能的,但是您可以通过以下HTML获得类似的效果,以及一些可爱的CSS:

 <ul> <li> <input type="radio" name="location" value="0" id="loc_0" /> <label for="loc_0">United States</label> <ul> <li> Northeast <ul> <li> <input type="radio" name="location" value="1" id="loc_1" /> <label for="loc_1">New Hampshire</label> </li> <li> <input type="radio" name="location" value="2" id="loc_2" /> <label for="loc_2">Vermont</label> </li> <li> <input type="radio" name="location" value="3" id="loc_3" /> <label for="loc_3">Maine</label> </li> </ul> </li> <li> Southeast <ul> <li> <input type="radio" name="location" value="4" id="loc_4" /> <label for="loc_4">Georgia</label> </li> <li> <input type="radio" name="location" value="5" id="loc_5" /> <label for="loc_5">Alabama</label> </li> </ul> </li> </ul> </li> <li> <input type="radio" name="location" value="6" id="loc_6" /> <label for="loc_6">Canada</label> <ul> <li> <input type="radio" name="location" value="7" id="loc_7" /> <label for="loc_7">Ontario</label> </li> <li> <input type="radio" name="location" value="8" id="loc_8" /> <label for="loc_8">Quebec</label> </li> <li> <input type="radio" name="location" value="9" id="loc_9" /> <label for="loc_9">Manitoba</label> </li> </ul> </li> </ul> 

作为一个额外的好处,这也意味着你可以允许自己select<optgroups> 。 例如,如果您有嵌套的类别,其中的类别非常详细,并且您希望允许用户在层次结构中select较高的类别,这可能会非常有用。

这将在没有JavaScript的情况下工作,但是您可能希望添加一些隐藏单选button,然后更改所选项目的背景颜色。

请记住,这并不是一个完美的解决scheme,但是如果你绝对需要一个具有合理的跨浏览器兼容性的嵌套select,这可能就像你将要得到的一样。

我需要干净和轻量级的解决scheme(所以没有jQuery和类似),它将看起来完全像普通的HTML,也将继续工作时,只有纯HTML是预先设定的(所以JavaScript只会增强),这将允许search开始字母(包括国家的UTF-8字母),如果可能的话,它不会增加额外的重量。 它也必须在非常慢的浏览器上快速工作(想想rPi – 所以最好在页面加载后不执行JavaScript)。

在Firefox中,它使用CSS标识,从而允许通过字母search,而在其他浏览器中,它将使用&nbsp; 预先计划(但不支持通过字母快速search)。 无论如何,我对结果很满意。

你可以在这里尝试一下

它是这样的:

CSS:

 .i0 { } .i1 { margin-left: 1em; } .i2 { margin-left: 2em; } .i3 { margin-left: 3em; } .i4 { margin-left: 4em; } .i5 { margin-left: 5em; } 

HTML(类“i1”,“i2”等表示标识级别):

 <form action="/filter/" method="get"> <select name="gdje" id="gdje"> <option value=1 class="i0">Svugdje</option> <option value=177 class="i1">Bosna i Hercegovina</option> <option value=190 class="i2">Babin Do</option> <option value=258 class="i2">Banja Luka</option> <option value=181 class="i2">Tuzla</option> <option value=307 class="i1">Crna Gora</option> <option value=308 class="i2">Podgorica</option> <option value=2 SELECTED class="i1">Hrvatska</option> <option value=5 class="i2">Bjelovarsko-bilogorska županija</option> <option value=147 class="i3">Bjelovar</option> <option value=79 class="i3">Daruvar</option> <option value=94 class="i3">Garešnica</option> <option value=329 class="i3">Grubišno Polje</option> <option value=368 class="i3">Čazma</option> <option value=6 class="i2">Brodsko-posavska županija</option> <option value=342 class="i3">Gornji Bogićevci</option> <option value=158 class="i3">Klakar</option> <option value=140 class="i3">Nova Gradiška</option> </select> </form> <script> <!-- window.onload = loadFilter; // --> </script> 

JavaScript的:

 function loadFilter() { 'use strict'; // indents all options depending on "i" CSS class function add_nbsp() { var opt = document.getElementsByTagName("option"); for (var i = 0; i < opt.length; i++) { if (opt[i].className[0] === 'i') { opt[i].innerHTML = Array(3*opt[i].className[1]+1).join("&nbsp;") + opt[i].innerHTML; // this means "&nbsp;" x (3*$indent) } } } // detects browser navigator.sayswho= (function() { var ua= navigator.userAgent, tem, M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*([\d\.]+)/i) || []; if(/trident/i.test(M[1])){ tem= /\brv[ :]+(\d+(\.\d+)?)/g.exec(ua) || []; return 'IE '+(tem[1] || ''); } M= M[2]? [M[1], M[2]]:[navigator.appName, navigator.appVersion, '-?']; if((tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1]; return M.join(' '); })(); // quick detection if browser is firefox function isFirefox() { var ua= navigator.userAgent, M= ua.match(/firefox\//i); return M; } // indented select options support for non-firefox browsers if (!isFirefox()) { add_nbsp(); } } 

我真的很喜欢这篇文章中的Broken Arrow解决scheme 。 我刚刚改进/改变了一下,所以称为标签可以切换,不被视为选项。 我使用了一小段jQuery,但是这可以在没有jQuery的情况下完成。

我用链接replace了中间标签(没有叶子标签),在点击时调用一个function。 此function负责切换点击链接的下一个div,以便扩展/折叠选项。 这避免了在层次结构中select中间元素的可能性,这通常是需要的。 制作一个允许select中间元素的变体应该很容易。

这是修改后的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> 

一个小的JavaScript / jQuery函数:

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

和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; } 

在JSFiddle中运行示例