Выпадающий список в HTML: как сделать, оформить через CSS и добавить поиск

Выпадающий список HTML создаёт тег select с вложенными option. Рабочий код – в первом разделе, он запускается без скриптов и стилей.

Дальше вы узнаете, как создать выпадающий список нужного вида: оформите его через CSS, добавите поиск и выбор нескольких значений. Затем заберёте выбранное в JavaScript и на сервере в PHP.

Вадим Дворниковавтор материала

Как сделать выпадающий список в HTML за минуту

Чтобы сделать выпадающий список, оберните варианты в select. Каждый вариант – отдельный option. Вот полный пример из трёх вариантов.

<label for="color">Цвет:</label>
<select id="color" name="color">
  <option value="green">Зелёный</option>
  <option value="yellow">Жёлтый</option>
  <option value="black">Чёрный</option>
</select>

Вставьте разметку в любую страницу и откройте её в браузере. Появится поле со словом «Зелёный» и стрелкой, по щелчку раскроются 3 варианта. Если вы не указали selected, браузер выбирает первый доступный вариант.

Нужно больше вариантов? Добавляйте по строке option на каждый. Длинный список утомляет, поэтому при десятке вариантов и больше группируйте их или добавляйте поиск. Оба способа описаны ниже.

Раскрывающийся список HTML и выпадающий – одно и то же: это два названия элемента dropdown. В коде это всегда пара html select option: тег select держит список, а option описывает один вариант.

Из чего состоит select: атрибуты и значение варианта

Текст варианта и атрибут value – разные вещи. Пользователь видит текст между тегами. На сервер уходит value. Нет value – уйдёт текст варианта.

В первом примере «Зелёный» превращается в green. Значения на латинице проще проверять в коде, и они не ломаются при смене перевода. Поэтому в коде на сервере вы сравниваете значение с green, а не со словом «Зелёный».

Выбор из списка настраивают пять атрибутов:

  • name – имя поля, по нему значение приходит на сервер;
  • selected – вариант выбран при загрузке страницы;
  • disabled – вариант или весь список недоступен;
  • required – форма не отправится без выбора;
  • size – сколько строк видно сразу: при size="4" список превращается в блок из 4 строк.
<select id="delivery" name="delivery" required>
  <option value="">Выберите способ</option>
  <option value="courier">Курьер</option>
  <option value="pickup" selected>Самовывоз</option>
  <option value="post" disabled>Почта (временно недоступна)</option>
</select>

В этом примере при загрузке выбран «Самовывоз». Вариант «Почта» серый, его не выбрать. Пустой первый вариант нужен для required: об этом ещё раз в последнем разделе.

Атрибут id нужен не серверу, а подписи и скриптам. По нему label for находит список, а querySelector – его в коде. Пара id и name не обязана совпадать, но одинаковые значения упрощают чтение разметки.

Группировка вариантов: optgroup

Когда вариантов больше десятка и они делятся по смыслу, объедините их тегом optgroup. Список с группами читается быстрее: глаз находит заголовок, а не перебирает 30 строк подряд.

<select name="city">
  <optgroup label="Россия">
    <option value="msk">Москва</option>
    <option value="spb">Санкт-Петербург</option>
  </optgroup>
  <optgroup label="Беларусь">
    <option value="minsk">Минск</option>
  </optgroup>
</select>

Атрибут label задаёт заголовок группы. Заголовок выбрать нельзя, и в форму он не уходит. Если добавить disabled к optgroup, отключится вся группа сразу.

Группы не вкладываются друг в друга: optgroup внутри другого optgroup недопустим. Нужна иерархия из трёх уровней? Сделайте два связанных списка: второй заполняйте скриптом после выбора в первом.

Группировка полезна не всегда. Для пяти вариантов она только усложняет разметку. Для длинного справочника – стран, городов, категорий товаров – она заметно ускоряет поиск нужной строки глазами.

Выпадающий список с автодополнением: input и datalist

Нужен выбор из списка, где пользователь печатает и получает подсказки? Свяжите поле input с тегом datalist. Скрипты не нужны: браузер сам фильтрует варианты по введённым буквам.

<label for="city-input">Город:</label>
<input id="city-input" name="city" list="cities" autocomplete="off">
<datalist id="cities">
  <option value="Москва">
  <option value="Минск">
  <option value="Мурманск">
</datalist>

Связь держит атрибут list: его значение равно id списка. Главное отличие от select – в поле можно ввести текст, которого нет среди подсказок. Проверяйте такое значение на сервере.

Когда что выбирать? Если допустимы только заранее известные значения, берите select: он не даёт ввести лишнее. Если вариантов много, а свой текст допустим, берите datalist. Он подходит для города, тега или названия товара.

Дополнительная проверка на стороне браузера ловит опечатки до отправки формы. Сверьте введённое значение с вариантами и подсветите поле:

<script>
  const input = document.querySelector("#city-input");
  const options = Array.from(document.querySelectorAll("#cities option"), (o) => o.value);

  input.addEventListener("input", () => {
    const known = options.includes(input.value);
    input.setCustomValidity(known ? "" : "Выберите город из списка");
  });
</script>

Пустая строка в setCustomValidity снимает ошибку, непустая блокирует отправку формы. Скрипт стоит после разметки, поэтому оба элемента к этому моменту уже есть на странице. Проверка в браузере не заменяет серверную: запрос можно отправить в обход формы.

Поддержка неполная. По данным caniuse, в Safari на iOS она частичная, а в Chrome для Android полная. Проверьте вид подсказок на своих устройствах, прежде чем выкладывать форму.

Выбор нескольких значений: multiple

Атрибут multiple превращает список в поле выбора нескольких значений. На компьютере браузер рисует не раскрывающееся меню, а прокручиваемый блок. Высоту блока задаёт size: при size="4" видны 4 строки.

<select id="colors" name="colors[]" multiple size="4">
  <option value="red">Красный</option>
  <option value="green">Зелёный</option>
  <option value="blue">Синий</option>
  <option value="black">Чёрный</option>
</select>

На компьютере зажмите Ctrl (в macOS – Cmd) или Shift и щёлкайте по вариантам. Посетители не всегда об этом знают, поэтому добавьте подсказку рядом со списком.

Список select с атрибутом multiple: в блоке выделены несколько вариантов

Для трёх–пяти вариантов удобнее группа чекбоксов: их отмечают одним щелчком без подсказок. Список с multiple берите, когда вариантов много и блок с прокруткой экономит место на странице.

На телефоне окно вариантов рисует браузер или система, и вид отличается от компьютерного. Ваши стили туда могут не дойти. Проверьте список на реальном устройстве до выкладки.

Имя поля для нескольких значений заканчивайте квадратными скобками: name="colors[]". PHP тогда соберёт значения в массив. Без скобок останется только последнее значение – такое правило описано в FAQ на php.net. При выборе красного и синего придёт массив из двух элементов.

Выпадающий список HTML CSS: стилизация select

Привести список к макету можно за три шага.

  1. Уберите системный вид свойством appearance: none. MDN относит его к Baseline: оно работает во всех актуальных браузерах без вендорных префиксов.
  2. Нарисуйте свою стрелку фоном: встроенный SVG в background-image. Справа оставьте отступ padding-right, чтобы текст не заезжал на стрелку.
  3. Добавьте состояния :hover, :focus-visible и :disabled. Проверьте результат: стрелка должна стоять справа, а по Tab список должен получать видимую рамку.
.select {
  appearance: none;
  width: 240px;
  padding: 10px 40px 10px 12px;
  border: 1px solid #517b97;
  border-radius: 6px;
  background-color: #fff;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23333' stroke-width='2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  font: inherit;
  color: #222;
}

.select:hover {
  border-color: #2b5d82;
}

.select:focus-visible {
  outline: 2px solid #1a73e8;
  outline-offset: 2px;
}

.select:disabled {
  background-color: #f0f0f0;
  color: #888;
}

Повесьте класс на список: <select class="select">.

Разберём ключевые строки. Отступ справа в 40 пикселей оставляет место под стрелку. Строка background-position: right 12px center ставит стрелку в 12 пикселях от правого края и по центру высоты. Значение font: inherit берёт шрифт страницы вместо системного.

Стрелка – это SVG внутри адреса data:image/svg+xml. Символ решётки в цвете записан как %23: иначе браузер прочтёт его как начало якоря. Цвет стрелки меняйте в параметре stroke, размер – в width и height.

Псевдокласс :focus-visible тоже относится к Baseline. Не убирайте outline без замены: клавиатурный фокус исчезнет, и пользователь потеряет место на форме.

Если проект на Bootstrap, стилизацию можно не писать. В Bootstrap 5.3 штатный класс для списка – form-select, есть размеры form-select-lg и form-select-sm. В четвёртой версии для этого служил custom-select.

Почему option почти не стилизуется

Стилизовать можно сам select, но не окно с вариантами. MDN пишет об этом прямо: вы не управляете списком, который раскрывается по щелчку. Наследовать шрифт родителя можно, а отступы и цвета – нет.

Для самих option есть только узкая лазейка. Chromium может дополнительно применить color, background-color, font-family, font-variant и text-align. Результат зависит от браузера и системы, поэтому на одинаковый вид не рассчитывайте.

Будущее решение – значение appearance: base-select и псевдоэлемент ::picker(select). Они открывают окно вариантов для CSS. В Chrome они работают с версии 135, но MDN отмечает ограниченную доступность: это не Baseline. Про Firefox и Safari проверяйте таблицу совместимости MDN.

@supports (appearance: base-select) {
  select,
  ::picker(select) {
    appearance: base-select;
  }
}

Блок в @supports срабатывает только там, где значение известно. В остальных браузерах останется обычный вид из прошлого раздела.

Что делать, если дизайнер просит иконки, цветные метки или две строки в варианте? Есть три пути.

Первый: упростить макет и оставить нативный список. Второй: подождать, пока base-select станет доступен всем вашим посетителям. Третий: взять кастомный список или готовую библиотеку – о них следующие разделы.

Кастомный список на своей разметке: когда он оправдан

Свой список оправдан, когда в вариантах нужны иконки, картинки или две строки текста. Для обычного выбора из 5–10 пунктов нативный select лучше. Он бесплатно даёт клавиатуру, поиск по первой букве и доступность для скринридеров.

В своей разметке всё это вы пишете сами. Список работ выглядит так:

  • роли ARIA: listbox для списка и option для вариантов;
  • стрелки вверх и вниз для перехода по вариантам;
  • Enter выбирает вариант, Esc закрывает список;
  • возврат фокуса на кнопку после закрытия;
  • скрытое поле, через которое значение попадёт в форму.
<button type="button" id="trigger" aria-haspopup="listbox" aria-expanded="false">
  Выберите цвет
</button>
<ul role="listbox" aria-labelledby="trigger" hidden>
  <li role="option" aria-selected="false">Зелёный</li>
  <li role="option" aria-selected="false">Жёлтый</li>
</ul>

Это только каркас: без скрипта он не раскрывается. Значение к тому же не попадёт в форму, пока вы не добавите скрытое поле input type="hidden". Скрипт для раскрытия, выбора и клавиатуры займёт ещё десятки строк.

На телефоне придётся отдельно проверить жесты и прокрутку. Скринридер должен зачитать вариант и его состояние, и проверить это можно только вживую. Если такой объём работы не оправдан, возьмите готовую библиотеку.

Готовые библиотеки: Choices.js, Tom Select, Select2, Bootstrap-Select

Библиотеки добавляют к списку поиск, теги и удобный мультивыбор. Ниже – четыре популярных варианта. Даты релизов и размеры собраны в таблице.

БиблиотекаПоследний релизЗависимостиВес JS (min / gzip)ПоискМультивыбор
Choices.js11.2.4, август 2026нет, без jQuery78,3 / 21,4 КБдада
Tom Select2.6.2, июль 2026нет, без jQuery55,2 / 17,8 КБдада
Select24.1.0, май 2026jQuery73,4 / 20,5 КБдада
Bootstrap-Select1.13.18, июнь 2020jQuery, CSS и dropdown.js из Bootstrap52,5 / 16,0 КБда, data-live-search="true"да

Версии, размеры и даты релизов приведены как ориентир на момент обновления статьи. Размеры взяты у Bundlephobia и не включают jQuery. Перед подключением сверьте их в репозитории библиотеки.

Choices.js и Tom Select работают без jQuery, и релизы у обоих свежие. Начинайте с них. Select2 берите, если jQuery в проекте уже есть: дата его релиза стоит в таблице, а свежие правки в репозитории продолжают появляться.

Bootstrap-Select для нового проекта не берите. Стабильная версия 1.13.18 вышла в июне 2020 года, последний тег – бета 1.14.0 от апреля 2022 года. Поддержки Bootstrap 5 в стабильной версии нет.

Готовые библиотеки: Choices.js, Tom Select, Select2, Bootstrap-Select

Покажем подключение на Choices.js: он не требует jQuery и сборщика. Ниже полная страница, которую можно сохранить в файл и открыть в браузере. Стили и скрипт грузятся с CDN jsDelivr.

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <title>Choices.js</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/base.min.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
</head>
<body>
  <label for="colors">Цвета:</label>
  <select id="colors" name="colors[]" multiple>
    <option value="red">Красный</option>
    <option value="green">Зелёный</option>
    <option value="blue">Синий</option>
    <option value="black">Чёрный</option>
  </select>

  <script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
  <script>
    new Choices("#colors", {
      removeItemButton: true
    });
  </script>
</body>
</html>

Две строки link подключают стили, а скрипт создаёт объект Choices на месте обычного списка. Опция removeItemButton добавляет крестик у выбранных значений. Поиск по вариантам включён по умолчанию, за него отвечает опция searchEnabled.

Со сборщиком порядок другой. Поставьте пакет командой npm install choices.js, затем подключите скрипт и стили в своём коде:

import Choices from "choices.js";
import "choices.js/public/assets/styles/choices.css";

new Choices("#colors", {
  removeItemButton: true
});

Список остаётся обычным select, поэтому форма отправляется как прежде. Значения уходят на сервер под тем же именем, и PHP-код из раздела про приём значений менять не нужно.

Выпадающий список JS: как получить выбранное значение

В JavaScript выбранное значение выпадающий список отдаёт через свойство value. Оно возвращает значение первого выбранного варианта, а если выбора нет – пустую строку. Событие change срабатывает при каждой смене выбора.

<select id="color" name="color">
  <option value="green">Зелёный</option>
  <option value="yellow">Жёлтый</option>
</select>
<p id="result"></p>

<script>
  const select = document.querySelector("#color");
  const result = document.querySelector("#result");

  select.addEventListener("change", () => {
    const option = select.options[select.selectedIndex];
    result.textContent = option.text + " = " + select.value;
  });
</script>

Выберите «Жёлтый»: под списком появится «Жёлтый = yellow». Так видно разницу между текстом варианта и его value. Скрипт стоит после списка, поэтому элемент к этому моменту уже есть на странице.

Свойство selectedIndex хранит номер выбранного варианта, а options – коллекцию всех вариантов. Из них код берёт текст выбранного пункта. Если нужен только value, читайте select.value напрямую.

Для списка с multiple одного value мало. Соберите все выбранные варианты из коллекции selectedOptions:

const values = Array.from(select.selectedOptions, (option) => option.value);

В старом проекте с jQuery то же читает строка $("#color").val(). В новом коде jQuery ради этого не подключайте.

Как принять значение на сервере: PHP

Форма с методом post отправляет значение под именем из атрибута name. Вот форма и обработчик в одном файле.

<form method="post">
  <label for="selphp">Выберите цвет:</label>
  <select id="selphp" name="selphp">
    <option value="red">Красный</option>
    <option value="green">Зелёный</option>
    <option value="blue">Синий</option>
  </select>
  <button type="submit">Отправить</button>
</form>

<?php
$allowed = ['red', 'green', 'blue'];
$color = $_POST['selphp'] ?? '';

if (in_array($color, $allowed, true)) {
    echo 'Вы выбрали: ' . htmlspecialchars($_POST['selphp'], ENT_QUOTES, 'UTF-8');
}
?>

Атрибута action у формы нет: без него браузер отправляет данные на адрес этой же страницы. Сохраните файл с расширением .php и откройте через веб-сервер. Код из файла, открытого прямо с диска, PHP не выполнит.

Форма с выпадающим списком и результат отправки: PHP вывел на странице выбранный цвет

Значение из $_POST нельзя выводить в HTML напрямую. Пользователь может отправить не то, что в списке: подменить value в инструментах браузера или послать запрос из командной строки. Если в поле придёт тег script, он выполнится на странице.

Защиту дают два приёма. Сначала сверьте значение со списком допустимых, как в примере. Затем экранируйте вывод функцией htmlspecialchars(): флаг ENT_QUOTES превращает в безопасные символы и двойные, и одинарные кавычки. В новых версиях PHP этот флаг уже включён по умолчанию, но явная запись делает намерение видимым.

Для списка с multiple приведите поле к массиву. В примере ниже значения склеиваются в одну строку:

$colors = (array) ($_POST['colors'] ?? []);
echo htmlspecialchars(implode(', ', $colors), ENT_QUOTES, 'UTF-8');

Форму с методом get читайте из массива $_GET по тому же имени.

Доступность и типичные ошибки

Свяжите каждый список с подписью: label for должен совпадать с id списка. Так скринридер зачитает вопрос вместе со списком. Плейсхолдер вместо подписи не подходит.

Для плейсхолдера ставьте первым пустой вариант: <option value="">Выберите</option>. Вместе с required он не даёт отправить форму без выбора. Спецификация HTML называет его «placeholder label option».

Отключённый select в форму не попадает: его значение на сервер не уходит. Нужно показать список, но отправить значение? Добавьте скрытое поле input type="hidden" с тем же именем.

Ещё три ошибки встречаются чаще прочих. Разработчики убирают outline и оставляют клавиатурного пользователя без рамки фокуса. Они полагаются на стили option, хотя браузер их не применит. И они не проверяют список на реальном телефоне.

Совет от нашей редакции: начинайте с нативного select и стилей из раздела про стилизацию. Библиотеку подключайте, когда нужен поиск по сотне вариантов или теги.

Комментарии

Оставьте свой комментарий
VG
Vetal Ge Shul

с опцией поиска как шире элемент сделать? size и width не работают!