Вложенность optgroups в раскрывающемся списке / select

Я создал элемент управления customer 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, которое я ищу?

8 ответов


спецификация HTML здесь действительно сломана. Он должен разрешать вложенные optgroups и рекомендовать агентам пользователей отображать их как вложенные меню. Вместо допускается только один уровень optgroup. Тем не менее, они должны сказать следующее по этому вопросу:

Примечание. Реализаторам рекомендуется, что будущие версии HTML могут расширить механизм группировки, чтобы разрешить вложенные группы (т. е. элементы OPTGROUP могут гнездиться). Это позволит авторам представить более богатую иерархию выбор.

и пользовательские агенты могут начать использовать подменю для отображения optgoups вместо отображения заголовков перед первым элементом option в optgroup, как они это делают сейчас.


Это просто отлично, но если вы добавляете опцию witch не в 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>

Ok, если кто-нибудь когда-нибудь читает это: лучший вариант-добавить четыре &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>

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

Я думаю, если у вас есть что-то структурированное и сложное, вы можете рассмотреть что-то другое, чем один раскрывающийся список.


мне очень нравится решение сломанной стрелки выше в этом посте. Я только что улучшил / изменил его немного, чтобы то, что называлось ярлыками, можно было переключать и не рассматривать варианты. Я использовал небольшой кусок 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


мне нужно чистое и легкое решение (так что нет 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 " и т.д. обозначают уровень identation):

<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();
  }
}  

Я знаю, что это было довольно давно, однако у меня есть немного больше, чтобы добавить:

это невозможно в HTML5 или каких-либо предыдущих спецификациях, а также не предлагается в HTML5.1 пока. Я сделал запрос в public-html-comments список рассылки, но мы посмотрим, что из этого выйдет.

независимо, в то время как это невозможно с помощью <select> тем не менее, вы можете достичь аналогичного эффекта со следующим HTML, плюс некоторые CSS для prettiness:

<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, однако вы можете добавить некоторые, чтобы скрыть переключатели, а затем изменить цвет фона выбранный предмет или что-то в этом роде.

имейте в виду, что это далеко не идеальное решение, но если вам абсолютно нужен вложенный выбор с разумной кросс-браузерной совместимостью, это, вероятно, так близко, как вы собираетесь получить.