Combobox
Combobox позволяет выбрать один пункт из длинного списка: человек печатает, и список фильтруется. Значение всегда один из ваших пунктов; введённый текст только помогает его найти.
<tk-field label="Country">
<tk-combobox name="country" placeholder="Start typing a country">
<option value="dk">Denmark</option>
<option value="de">Germany</option>
<option value="nl">Netherlands</option>
<option value="gb">United Kingdom</option>
</tk-combobox>
</tk-field>Поместите combobox в aria-label. Placeholder не заменяет имя: он
пропадает, как только человек начинает печатать.
Анатомия
| Часть | Что это |
|---|---|
tk-combobox |
сам элемент; одна остановка Tab |
| поле | текстовое поле: показывает выбранный пункт или то, что человек печатает; занимает ширину контейнера |
| кнопка очистки | видна, пока в поле есть текст; стирает текст и значение |
| шеврон | переворачивается, пока меню открыто; во время загрузки пунктов его место занимает спиннер |
| меню | подходящие пункты; ширина как у поля; открывается под полем, а у нижнего края экрана над ним |
| пункт | одна строка; выбранный отмечает галочка |
| заголовок группы | необязательный заголовок над несколькими пунктами; скрыт, если ни один из них не подошёл |
| строка сообщения | «No results found», подсказка о загрузке или ошибка загрузки; её нельзя выбрать |
В меню видно примерно пять с половиной строк, дальше оно прокручивается.
Размеры
<tk-combobox aria-label="City" placeholder="Start typing a city"> <option>Amsterdam</option> </tk-combobox> <tk-combobox size="large" aria-label="Airport" placeholder="Start typing an airport"> <option>Schiphol</option> </tk-combobox>
size |
Высота | Padding-inline | Текст | Строка пункта |
|---|---|---|---|---|
medium (по умолчанию) |
32px | space-8 |
Body Small (14/20) | 32px |
large |
36px | space-10 |
Body Medium (16/24) | 36px |
Высоты совпадают с Select, Input и Button. Длинные пункты обрезаются многоточием.
Состояния
<tk-combobox aria-label="Office" value="lis" disabled> <option value="lis">Lisbon</option> </tk-combobox> <tk-combobox aria-label="Team" value="core" readonly> <option value="core">Core platform</option> </tk-combobox>
При наведении поле не меняется. При фокусе с клавиатуры его рамка окрашивается и становится толще; кольца фокуса нет.
disabled: на поле нельзя перейти, его нельзя открыть, значение не отправляется.readonly: значение видно и отправляется. Поле можно выделить и скопировать текст, но нельзя изменить или открыть меню. Кнопка очистки и шеврон скрыты.- Ошибка: задайте
errorна Field. Под полем появится сообщение, рамка станет красной, даже в фокусе.
Фильтрация
- Ввод открывает меню и фильтрует его. Регистр не важен, совпадение ищется в любом месте названия: «kingdom» найдёт «United Kingdom». Описания и заголовки групп не учитываются.
- Текст — это поиск, а не значение. Значение меняется, только когда человек выбирает пункт. Если он уходит, ничего не выбрав, в поле возвращается выбранный пункт.
- Клик открывает весь список, так что выбрать можно и без ввода.
- Если ничего не нашлось, меню показывает «No results found».
Источники данных
| Источник | Как задать | Где фильтруется |
|---|---|---|
дочерние <option> (по умолчанию) |
разметкой, группы через <optgroup> |
в браузере |
свойство options |
массив { value, label, description?, disabled?, group? } |
в браузере |
свойство loadOptions |
асинхронная функция, которая возвращает пункты для запроса | на вашем сервере |
loadOptions нужен, только если список лежит на сервере или слишком велик, чтобы
отдавать его целиком:
combobox.loadOptions = async (query, { signal }) => {
const res = await fetch(`/api/cities?q=${encodeURIComponent(query)}`, { signal });
return (await res.json()).map((c) => ({ value: c.id, label: c.name }));
};- Запрос уходит через
debounceмс (250) после последнего нажатия. min-charsоткладывает запросы, пока не введено достаточно символов; меню пишет «Type 2 or more characters.» Значение по умолчанию, 0, загружает весь список по клику.- Во время загрузки вместо шеврона крутится спиннер. Если промис отклонён, меню показывает «Couldn't load options.» и кнопку Try again; Enter тоже повторяет запрос.
- Показываются только результаты последнего запроса. Передайте
signalвfetch, чтобы отменять старые. - Возвращайте уже отфильтрованные пункты, каждый в одну строку.
Описания пунктов
<tk-combobox aria-label="Plan" placeholder="Start typing a plan"> <option value="free" data-description="For trying things out">Free</option> <option value="pro" data-description="Adds version history">Pro</option> </tk-combobox>
data-description (или description в options) добавляет короткую вторую строку.
Пишите её, чтобы помочь с выбором, и не описывайте каждый пункт. Скринридер читает её
после названия.
Создание своих значений
<tk-field label="Tag">
<tk-combobox creatable placeholder="Start typing a tag">
<option value="design">Design</option>
<option value="research">Research</option>
</tk-combobox>
</tk-field>По умолчанию можно выбрать только ваши пункты. С creatable запрос без совпадений
добавляет строку Create "…". Выбор этой строки или Enter на введённом тексте делает
текст значением и добавляет его в список. Подходит для открытых наборов вроде тегов или
навыков.
- При точном совпадении без учёта регистра предлагается существующий пункт, а не копия.
- Уход из поля по Tab, Esc или клику снаружи ничего не создаёт.
- Работает с любым источником, но не во время загрузки и не ниже
min-chars.
Когда использовать
- Combobox нужен для длинного списка: примерно от 15 пунктов или без чёткого конца, например города или товары.
- Для списка от 4 пунктов до одного экрана берите
Select . Select проще; combobox нужен, только когда select уже не подходит. - Для стран берите
Country select . Список, названия и флаги в нём уже есть. - Если можно выбрать несколько, нужен
Multiselect . - Если подходит любой текст, нужен
Input . - Не используйте combobox для поиска. Он задаёт значение формы и не показывает результаты.
Текст
- Подпись: назовите, что вы спрашиваете, с заглавной буквы только в первом слове, без двоеточия.
- Placeholder: «Start typing a…». Он пропадает при вводе, поэтому не пишите в нём ничего нужного.
- Пункты: каждый в одну короткую строку. Ставьте первым слово, которое будут печатать: «Congo (Democratic Republic)», а не «Democratic Republic of the Congo».
- Не начинайте пункты с иконки или эмодзи. Они мешают фильтрации.
- Ошибка: скажите, как её исправить, словами подписи: «Select a country from the list».
Подробнее в
Можно / нельзя
| ✅ Можно | ❌ Нельзя |
|---|---|
| Брать combobox для длинных списков | Брать его там, где хватит select или radio group |
| Открывать весь список по клику | Требовать ввод, прежде чем что-то показать |
| Ставить первым слово, по которому пункты различаются | Начинать пункты с иконок или эмодзи |
| Задавать им значение формы | Делать из него строку поиска |
| Давать подпись через Field | Использовать placeholder вместо подписи |
Связанные
Field : подпись, подсказка и ошибка вокруг combobox.Select : один пункт из списка покороче.Country select : combobox со встроенным списком стран.Multiselect : несколько пунктов, с той же фильтрацией.Input : свободный текст.Select family : как они соотносятся.
API
Атрибуты
| Атрибут | Тип | По умолчанию | Что делает |
|---|---|---|---|
value |
string | '' |
значение выбранного пункта; '' — ничего не выбрано. Ввод его не меняет |
placeholder |
string | — | подсказка, пока поле пустое |
name |
string | — | имя поля в форме |
required |
boolean | false |
пустое значение не даёт отправить форму; видимой метки нет (звёздочку добавляет Field) |
disabled |
boolean | false |
нельзя перейти и открыть, не отправляется с формой |
readonly |
boolean | false |
можно перейти, значение отправляется, но изменить и открыть нельзя |
size |
medium | large |
medium |
высота 32 / 36px |
creatable |
boolean | false |
позволяет добавить значение, которого нет в списке |
min-chars |
number | 0 |
с loadOptions: сколько символов нужно до загрузки |
debounce |
number | 250 |
с loadOptions: сколько миллисекунд ждать после ввода |
Атрибута autocomplete нет: автозаполнение браузера выключено, чтобы его меню не
перекрывало меню combobox.
Свойства
| Свойство | Тип | Что делает |
|---|---|---|
options |
{ value, label, description?, disabled?, group? }[] |
пункты из JavaScript; заменяют дочерние <option> |
loadOptions |
(query, { signal }) => Promise<options> |
загружает пункты под запрос; важнее options и дочерних элементов |
Слоты
| Слот | Содержимое |
|---|---|
| по умолчанию | элементы <option> и <optgroup>; их изменения подхватываются |
<option> принимает value, selected, disabled и data-description. <optgroup>
принимает label.
События
change срабатывает, когда человек выбирает пункт или очищает поле. Оно не срабатывает
при вводе, при движении по меню и когда вы задаёте value из кода.
В форме значение отправляется под именем name, сброс формы возвращает начальное
значение, а Enter в закрытом поле отправляет форму.
CSS
::part(field): поле.::part(listbox): меню.
Токены
Поле, меню, пункты, ошибка и фокус используют
| Часть | Токены |
|---|---|
| введённый или выбранный текст | text/default; placeholder text/placeholder |
| кнопка очистки | icon/default, icon-size-16; наведение state/hover, нажатие state/press; 24×24px, radius-full |
| строка сообщения | text/secondary; ошибка загрузки text/error, Try again text/accent |
строка Create "…" |
text/secondary; введённый текст text/default |
Доступность
Уже встроено:
- Скринридер объявляет редактируемый combobox с подсказками и объясняет, как ими пользоваться.
- Скрытое статусное сообщение по ходу ввода говорит, сколько пунктов подходит («7 results available.», «No results.»), а также сообщает о загрузке и ошибках.
- Пока меню открыто, фокус остаётся в поле. Подсвеченный пункт читается вместе с позицией, например «2 of 5».
- Ничего не выбирается, пока человек сам не выберет. Esc и клик снаружи ничего не меняют.
- Кнопка очистки не остановка Tab. Чтобы очистить поле с клавиатуры, удалите текст и нажмите Enter или Tab.
Ваша часть:
- Дайте каждому combobox имя: подпись Field или
aria-label. - Оставьте весь список доступным по клику; с
loadOptionsпо возможности держитеmin-charsравным 0. - Пишите ошибки по правилам из раздела «Текст».
| Клавиша | Меню закрыто | Меню открыто |
|---|---|---|
буквы, Space |
ввод; меню открывается и фильтруется | ввод; список фильтруется |
Down / Up |
открывает весь список и подсвечивает выбранный или первый пункт | двигает подсветку, пропуская отключённые пункты; на краях останавливается |
Alt + Down |
открывает без подсветки | — |
Alt + Up |
— | выбирает подсвеченный пункт и закрывает меню |
Enter |
отправляет форму | выбирает подсвеченный пункт; если подсветки нет, ничего не делает |
Esc |
— | закрывает меню и возвращает текст; окружающий диалог остаётся открытым |
Tab |
переход к следующему элементу | выбирает подсвеченный пункт или возвращает текст и переходит дальше |
Home / End, Left / Right |
двигают курсор в тексте | двигают курсор в тексте |
Пределы контраста
- Рамка поля: 1.33:1 в светлой теме и 2.00:1 в тёмной, ниже минимума 3:1; 4.22:1 в высококонтрастной. Всегда показывайте подпись.
- Placeholder: 3.26:1 в светлой теме, ниже минимума 4.5:1 для текста. Не пишите в нём ничего нужного.