85

我创建了一个客户 c# DropDownList 控件,它可以渲染出它的内容是 optgroups (不是从头开始,我编辑了一些在互联网上找到的代码,尽管我确实理解它在做什么),它工作正常。

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

<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>

但是,在上面的示例代码段中,它呈现Level Two的缩进量与Level One.

有没有办法产生我正在寻找的嵌套 optgroup 行为?

4

12 回答 12

71

这里的 HTML 规范真的很糟糕。它应该允许嵌套的 optgroups 并建议用户代理将它们呈现为嵌套菜单。相反,只允许一个 optgroup 级别。但是,他们确实必须就这个问题说以下内容:

笔记。建议实施者未来版本的 HTML 可能会扩展分组机制以允许嵌套组(即 OPTGROUP 元素可以嵌套)。这将允许作者代表更丰富的选择层次。

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

于 2010-08-05T11:59:10.390 回答
53

这很好,但是如果您添加不在 optgroup 中的选项,它就会出错。

<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>

如果您使用 css 并立即关闭 optgroup 会更好:

<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>

于 2010-10-04T11:52:42.623 回答
49

好吧,如果有人读过这个:最好的选择是&nbsp;在每个额外的缩进级别添加四个 s,看起来!

所以:

<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>

于 2009-06-24T11:29:41.750 回答
17

<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>

于 2015-05-24T02:35:41.727 回答
6

我真的很喜欢这篇文章中上面的断箭解决方案。我刚刚对其进行了一些改进/更改,以便可以切换所谓的标签并且不被视为选项。我使用了一小块 jQuery,但是这可以在没有 jQuery 的情况下完成。

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

这是修改后的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 中运行示例

于 2016-12-07T17:41:45.880 回答
5

我认为如果你有一些结构化和复杂的东西,你可能会考虑除了一个下拉框之外的东西。

于 2009-06-24T11:36:12.470 回答
4

我知道这是很久以前的事了,但是我还有一点要补充的:

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

无论如何,虽然这还不能使用<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>

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

这一切都可以在没有 JavaScript 的情况下工作,但是您可能希望添加一些来隐藏单选按钮,然后更改所选项目的背景颜色或其他内容。

请记住,这远不是一个完美的解决方案,但如果您绝对需要具有合理跨浏览器兼容性的嵌套选择,这可能与您将获得的一样接近。

于 2013-06-09T08:21:21.877 回答
3

我需要干净和轻量级的解决方案(所以没有 jQuery 等),它看起来与纯 HTML 完全一样,在仅预设纯 HTML 时也能继续工作(所以 javascript 只会增强它),并且允许通过开头字母进行搜索(包括国家 UTF-8 字母)如果可能的话,它不会增加额外的重量。它还必须在非常慢的浏览器上快速运行(想想 rPi - 所以最好在页面加载后不执行 javascript)。

在 Firefox 中它使用 CSS 标识,因此允许按字母搜索,而在其他浏览器中它将使用&nbsp;前置(但它不支持按字母快速搜索)。无论如何,我对结果很满意。

你可以在这里尝试一下

它是这样的:

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();
  }
}  
于 2015-03-13T18:16:52.370 回答
3

我写了一个漂亮的嵌套选择。也许它会帮助你。

https://jsfiddle.net/nomorepls/tg13w5r7/1/

function on_change_select(e) {
  alert(e.value, e.title, e.option, e.select);
}

$(document).ready(() => {
  // NESTED SELECT

  $(document).on('click', '.nested-cell', function() {
    $(this).next('div').toggle('medium');
  });

  $(document).on('change', 'input[name="nested-select-hidden-radio"]', function() {
    const parent = $(this).closest(".nested-select");
    const value = $(this).attr('value');
    const title = $(this).attr('title');
    const executer = parent.attr('executer');
    if (executer) {
      const event = new Object();
      event.value = value;
      event.title = title;
      event.option = $(this);
      event.select = parent;
      window[executer].apply(null, [event]);
    }
    parent.attr('value', value);
    parent.parent().slideToggle();
    const button = parent.parent().prev();
    button.toggleClass('active');
    button.addClass('selected');
    button.children('.nested-select-title').html(title);
  });

  $(document).on('click', '.nested-select-button', function() {
    const button = $(this);
    let select = button.parent().children('.nested-select-wrapper');

    if (!button.hasClass('active')) {
      select = select.detach();
      if (button.height() + button.offset().top + $(window).height() * 0.4 > $(window).height()) {
        select.insertBefore(button);
        select.css('margin-top', '-44vh');
        select.css('top', '0');
      } else {
        select.insertAfter(button);
        select.css('margin-top', '');
        select.css('top', '40px');
      }
    }
    select.slideToggle();
    button.toggleClass('active');
  });
});
.container {
  width: 200px;
  position: relative;
  top: 0;
  left: 0;
  right: 0;
  height: auto;
}

.nested-select-box {
  font-family: Arial, Helvetica, sans-serif;
  display: block;
  position: relative;
  width: 100%;
  height: fit-content;
  cursor: pointer;
  color: #2196f3;
  height: 40px;
  font-size: small;
  /* z-index: 2000; */
}

.nested-select-box .nested-select-button {
  border: 1px solid #2196f3;
  position: absolute;
  width: calc(100% - 20px);
  padding: 0 10px;
  min-height: 40px;
  word-wrap: break-word;
  margin: 0 auto;
  overflow: hidden;
}

.nested-select-box.danger .nested-select-button {
  border: 1px solid rgba(250, 33, 33, 0.678);
}

.nested-select-box .nested-select-button .nested-select-title {
  padding-right: 25px;
  padding-left: 25px;
  width: calc(100% - 50px);
  margin: auto;
  height: fit-content;
  text-align: center;
  vertical-align: middle;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
}

.nested-select-box .nested-select-button.selected .nested-select-title {
  bottom: unset;
  top: 5px;
}

.nested-select-box .nested-select-button .nested-select-title-icon {
  position: absolute;
  height: 20px;
  width: 20px;
  top: 10px;
  bottom: 10px;
  right: 7px;
  transition: all 0.5s ease 0s;
}

.nested-select-box .nested-select-button.active .nested-select-title-icon {
  -moz-transform: scale(-1, -1);
  -o-transform: scale(-1, -1);
  -webkit-transform: scale(-1, -1);
  transform: scale(-1, -1);
}

.nested-select-box .nested-select-button .nested-select-title-icon::before,
.nested-select-box .nested-select-button .nested-select-title-icon::after {
  content: "";
  background-color: #2196f3;
  position: absolute;
  width: 70%;
  height: 2px;
  transition: all 0.5s ease 0s;
  top: 9px;
}

.nested-select-box .nested-select-button .nested-select-title-icon::before {
  transform: rotate(45deg);
  left: -1.6px;
}

.nested-select-box .nested-select-button .nested-select-title-icon::after {
  transform: rotate(-45deg);
  left: 7px;
}

.nested-select-box .nested-select-wrapper {
  width: 100%;
  top: 40px;
  position: relative;
  border: 1px solid #2196f3;
  background: #ffffff;
  z-index: 2005;
  opacity: 1;
}

.nested-select {
  font-family: Arial, Helvetica, sans-serif;
  display: inline-block;
  overflow-y: scroll;
  max-height: 40vh;
  width: calc(100% - 10px);
  padding: 5px;
  -ms-overflow-style: none;
  scrollbar-width: none;
}

.nested-select::-webkit-scrollbar {
  display: none;
}

.nested-select a,
.nested-select span {
  padding: 0 5px;
  border-radius: 3px;
  cursor: pointer;
  text-align: start;
}

.nested-select a:hover {
  background-color: #62b2f3;
  color: #ffffff;
}

.nested-select span:hover {
  background-color: #c4c4c4;
  color: #ffffff;
}

.nested-select input[type="radio"] {
  display: none;
}

.nested-select input[type="radio"]+span {
  display: block;
}

.nested-select input[type="radio"]:checked+span {
  background-color: #2196f3;
  color: #ffffff;
}

.nested-select div {
  margin-left: 15px;
}

.nested-select label>span:before,
.nested-select a:before {
  content: "\2022";
  margin-right: 5px;
}

.nested-select a {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="nested-select-box w-100">
    <div class="nested-select-button">
      <p class="nested-select-title">
        Account
      </p>
      <span class="nested-select-title-icon"></span>
    </div>
    <div class="nested-select-wrapper" style="display: none;">
      <div class="nested-select" executer="on_change_select">

        <label>
        <input title="Accounting and legal services" value="1565142000000891539" type="radio" name="nested-select-hidden-radio">
        <span>Accounting and legal services</span>
      </label>



        <label>
        <input title="Advertising agencies" value="1565142000000891341" type="radio" name="nested-select-hidden-radio">
        <span>Advertising agencies</span>
      </label>



        <a class="nested-cell">Advertising And Marketing</a>
        <div>



          <label>
          <input title="Advertising agencies" value="1565142000000891341" type="radio" name="nested-select-hidden-radio">
          <span>Advertising agencies</span>
        </label>



          <a class="nested-cell">Adwords - traffic</a>
          <div>



            <label>
            <input title="Adwords - traffic: Charters and general search" value="1565142000003929177" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Charters and general search</span>
          </label>



            <label>
            <input title="Adwords - traffic: Distance course" value="1565142000007821291" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Distance course</span>
          </label>



            <label>
            <input title="Adwords - traffic: Events" value="1565142000003929189" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Events</span>
          </label>



            <label>
            <input title="Adwords - traffic: Practices" value="1565142000003929165" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Practices</span>
          </label>



            <label>
            <input title="Adwords - traffic: Sailing tours" value="1565142000003929183" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Sailing tours</span>
          </label>



            <label>
            <input title="Adwords - traffic: Theoretical courses" value="1565142000003929171" type="radio" name="nested-select-hidden-radio">
            <span>Adwords - traffic: Theoretical courses</span>
          </label>



          </div>



          <label>
          <input title="Branded products" value="1565142000000891533" type="radio" name="nested-select-hidden-radio">
          <span>Branded products</span>
        </label>



          <label>
          <input title="Business cards" value="1565142000005438323" type="radio" name="nested-select-hidden-radio">
          <span>Business cards</span>
        </label>



          <a class="nested-cell">Facebook, Instagram - traffic</a>
          <div>



            <label>
            <input title="Facebook, Instagram - traffic: Charters and general search" value="1565142000003929145" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Charters and general search</span>
          </label>



            <label>
            <input title="Facebook, Instagram - traffic: Distance course" value="1565142000007821285" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Distance course</span>
          </label>



            <label>
            <input title="Facebook, Instagram - traffic: Events" value="1565142000003929157" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Events</span>
          </label>



            <label>
            <input title="Facebook, Instagram - traffic: Practices" value="1565142000003929133" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Practices</span>
          </label>



            <label>
            <input title="Facebook, Instagram - traffic: Sailing tours" value="1565142000003929151" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Sailing tours</span>
          </label>



            <label>
            <input title="Facebook, Instagram - traffic: Theoretical courses" value="1565142000003929139" type="radio" name="nested-select-hidden-radio">
            <span>Facebook, Instagram - traffic: Theoretical courses</span>
          </label>



          </div>



          <label>
          <input title="Offline Advertising (posters, banners, partnerships)" value="1565142000000891377" type="radio" name="nested-select-hidden-radio">
          <span>Offline Advertising (posters, banners, partnerships)</span>
        </label>



          <label>
          <input title="Photos, video etc." value="1565142000000891371" type="radio" name="nested-select-hidden-radio">
          <span>Photos, video etc.</span>
        </label>



          <label>
          <input title="Prize fund" value="1565142000001404931" type="radio" name="nested-select-hidden-radio">
          <span>Prize fund</span>
        </label>



          <label>
          <input title="SEO" value="1565142000000891365" type="radio" name="nested-select-hidden-radio">
          <span>SEO</span>
        </label>



          <label>
          <input title="SMM Content creation (texts, copywriting)" value="1565142000000891389" type="radio" name="nested-select-hidden-radio">
          <span>SMM Content creation (texts, copywriting)</span>
        </label>



          <a class="nested-cell">YouTube</a>
          <div>



            <label>
            <input title="YouTube: travel expenses" value="1565142000008100163" type="radio" name="nested-select-hidden-radio">
            <span>YouTube: travel expenses</span>
          </label>



            <label>
            <input title="Youtube: video editing" value="1565142000008100157" type="radio" name="nested-select-hidden-radio">
            <span>Youtube: video editing</span>
          </label>



          </div>



        </div>

      </div>
    </div>
  </div>
</div>

于 2020-07-09T12:31:15.303 回答
1

似乎该标准的最初发明者想要一个基于组的选择列表而不是分层列表。而且,不幸的是,由于破坏向下兼容性的高风险,未来的 HTML 标准可能不会改变这一点。

在可接受的情况下,将分层列表转换为以段命名的组列表可能是一种解决方法:

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

如果可以接受更改顺序,请考虑合并具有相同组名的项目以获得更好的外观:

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

如果两者都不可接受并且确实需要分层选择列表,则可能需要实现带有复选框的模式对话框(可能还需要一些 JavaScript 键盘陷阱,用于使用 Shift 或 Ctrl 键进行多选)。

于 2021-07-24T14:34:53.833 回答
0

嵌套手风琴选择

我之所以采用这种方法,是因为我找不到我要搜索的内容。嵌套的手风琴选择。它的 CSS 非常简单,可以改进。您唯一需要的是一个带有要添加到选择中的键和值的对象。Keys将是子组,键values(数组和单个元素)将是可选项目。

一旦你有了你的数组,你唯一需要做的就是调用

initAccordeon(obj);

将您的数据对象作为参数,嵌套的手风琴将出现:

const obj = {
  Cars: {
    SwedishCars: [
      "Volvo",
      "Saab"
    ],
    GermanCars: [
      "Mercedes",
      {
        Audi: [
          "Audi A3", 
          "Audi A4", 
          "Audi A5" 
        ]
      }
    ]
  },
  Food: {
    Fruits: [
      "Orange",
      "Apple",
      "Banana"
    ],
    SaltyFoods: [
      "Pretzels",
      "Burger",
      "Noodles"
    ],
    Drinks: "Water"
  }
};

initAccordeon(obj);   // <--------------------------- Call initialization

function accordeonAddEvents() {
  Array.from(document.getElementsByClassName("accordeon-header")).forEach(function(header) {
    if (header.getAttribute("listener") !== "true") {
      header.addEventListener("click", function() {
        this.parentNode.getElementsByClassName("accordeon-body")[0].classList.toggle("hide");
      });
      header.setAttribute("listener", "true");
    }
  });

  Array.from(document.getElementsByClassName("button-group")).forEach(function(but) {
    if (but.getAttribute("listener") !== "true") {
      but.addEventListener("click", function() {
        if (this.getAttribute("depth") === "-1") {
          let header = this;
          while ((header = header.parentElement) && header.className !== "accordeon");
          header.getElementsByClassName("accordeon-header")[0].innerHTML = this.innerHTML;
          return;
        }
        const groups = Array.from(this.parentNode.getElementsByClassName("accordeon-group"));
        groups.forEach(g => {
          if (g.getAttribute("uuid") === this.getAttribute("uuid") &&
            g.getAttribute("depth") === this.getAttribute("depth")) {
            g.classList.toggle("hide");
          }
        });
      });
      but.setAttribute("listener", "true");
    }
  });
}

function initAccordeon(data) {
  accordeons = Array.from(document.getElementsByClassName("accordeon-body"));
  accordeons.forEach(acc => {
    acc.innerHTML = "";
    const route = (subObj, keyIndex = 0, parent = acc, depth = 0) => {
      const keys = Object.keys(subObj);
      if (typeof subObj === 'object' && !Array.isArray(subObj) && keys.length > 0) {
        while (keyIndex < keys.length) {
          var but = document.createElement("button");
          but.className = "button-group";
          but.setAttribute("uuid", keyIndex);
          but.setAttribute("depth", depth);
          but.innerHTML = keys[keyIndex];
          var group = document.createElement("div");
          group.className = "accordeon-group hide";
          group.setAttribute("uuid", keyIndex);
          group.setAttribute("depth", depth);
          route(subObj[keys[keyIndex]], 0, group, depth + 1);
          keyIndex++;
          parent.append(but);
          parent.append(group);
        }
      } else {
        if (!Array.isArray(subObj)) subObj = [subObj];
        subObj.forEach((e, i) => {
          if (typeof e === 'object') {
              route(e, 0, parent, depth);
          } else {
              var but = document.createElement("button");
              but.className = "button-group";
              but.setAttribute("uuid", i);
              but.setAttribute("depth", "-1");
              but.innerHTML = e;
              parent.append(but);
          }
        });
      }
    };
    route(data);
  });
  accordeonAddEvents();
}
.accordeon {
  width: 460px;
  height: auto;
  min-height: 340px;
  font-size: 20px;
  cursor: pointer;
  user-select: none;
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -o-user-select: none;
  display: block;
  position: relative;
  z-index: 10;
}

.accordeon-header {
  display: inline-block;
  width: 450px;
  border: solid 0.1vw black;
  border-radius: 0.2vw;
  background-color: white;
  padding-left: 10px;
  color: black;
}

.accordeon-header:hover {
  opacity: 0.7;
}

.accordeon-body {
  display: block;
  position: absolute;
}

.button-group {
  display: block;
  cursor: pointer;
  width: 460px;
  text-align: left;
  font-size: 20px;
  font-weight: bold;
}

.accordeon-group {
  padding-left: 20px;
}

.accordeon-group .button-group {
  width: 100%;
}

.button-group[depth="-1"] {
  color: green;
}

.hide {
  display: none;
}
<div class="accordeon">
  <span class="accordeon-header">Select something</span>
  <div class="accordeon-body hide">
      
  </div>
</div>

于 2021-07-29T15:44:48.203 回答
0

截至 2021 年底,仍然没有嵌套<optgroup>.

我制作了一个简单的 SCSS,它根本不需要 JS。For <select size>and <select multiple>to align 选项基于<option class="depth-[0-100]">.

仅在<optgroup>不应该再使用的地方有所不同,而不是他们使用<option class="depth-n" disabled label="..." />

<select size="10">
  <!-- Behaves similar to optgroup if disabled -->
  <option class="depth-0" label="Root" disabled />
  
  <!-- Examples -->
  <option class="depth-1">1</option>
  <option class="depth-2" value="something">1.2</option>
  <option class="depth-20" value="Level 20">20</option>
  
  <option class="depth-0" label="Next item on Root" disabled />
  <option class="depth-1" label="Sub" disabled />
  <option class="depth-2" value="#fff">White</option>
</select>
/* can easily be adjusted to support even more */
@for $i from 0 through 100 { 
  select[size] option.depth-#{$i},
  select[multiple] option.depth-#{$i} {
      padding-left: calc(0.2em + calc(0.8em * #{$i}));
  }
  /**
   * The label on options is handled similar to <optgroup label="..."> 
   * (tested with chrome 87 and 95)

   * This styles a <option> similar to an option group
   * but it requires the attribute data-deppth and disabled
   */
  select[size] option.depth-#{$i}[label]:disabled, 
  select[multiple] option.depth-#{$i}[label]:disabled {
    font-weight: bold;
    color: initial;
  }
}

示例小提琴

于 2021-10-29T16:33:13.973 回答