Работа с radio в JavaScript

Radio-кнопки позволяют выбрать один вариант из группы. Например, способ доставки, способ оплаты или тему оформления. Когда пользователь отмечает новую кнопку, браузер автоматически снимает выбор с предыдущей кнопки той же группы.

У каждой radio-кнопки есть логическое свойство checked. Оно показывает, выбрана ли именно эта кнопка. Значение выбранного варианта находится в свойстве value.

Когда использовать radio

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

Если варианты независимы и можно выбрать несколько, используют checkbox. Radio-кнопку обычно не применяют для одиночной настройки «включено или выключено»: пользователь не сможет снять её повторным нажатием. Для такой задачи также лучше подходит checkbox.

Как создать группу radio-кнопок

Создадим форму с тремя способами доставки:

<form class="delivery-form">
  <fieldset>
    <legend>Способ доставки</legend>

    <label>
      <input
        name="delivery"
        type="radio"
        value="courier"
        checked
      >
      Курьер
    </label>

    <label>
      <input name="delivery" type="radio" value="pickup">
      Самовывоз
    </label>

    <label>
      <input name="delivery" type="radio" value="post">
      Почта
    </label>
  </fieldset>

  <button type="submit">Продолжить</button>
</form>

Все три поля получили одинаковый атрибут name=“delivery”. Именно name объединяет radio-кнопки в группу, внутри которой может быть выбран только один вариант. Если задать разные имена, браузер будет считать поля разными группами и разрешит отметить их одновременно.

Атрибуты value различаются. Они задают значения, которые JavaScript или сервер получат для выбранного варианта. Атрибут checked у первой кнопки делает её выбранной при загрузке страницы.

fieldset и legend обозначают общую группу и её подпись. Отдельные элементы label увеличивают область нажатия: выбрать вариант можно нажатием не только на круглую кнопку, но и на текст рядом с ней.

Свойства checked и value

Свойства checked и value отвечают на разные вопросы:

  • checked - выбрана ли конкретная radio-кнопка;
  • value - какое значение связано с этой кнопкой.
const courierRadio = document.querySelector(
  'input[name="delivery"][value="courier"]',
);
const pickupRadio = document.querySelector(
  'input[name="delivery"][value="pickup"]',
);

console.log(courierRadio.checked); // true
console.log(courierRadio.value); // courier

console.log(pickupRadio.checked); // false
console.log(pickupRadio.value); // pickup

У невыбранной кнопки value не становится пустым. Поэтому само значение не показывает состояние поля. Чтобы проверить конкретную кнопку, используйте checked.

Если атрибут value не указан, свойство будет содержать строку ‘on’. Для radio-группы лучше задавать понятное значение каждому варианту, иначе по данным формы нельзя будет определить, какую кнопку выбрал пользователь.

Как получить выбранное значение

Псевдокласс :checked позволяет найти выбранную кнопку группы:

const selectedDelivery = document.querySelector(
  'input[name="delivery"]:checked',
);

console.log(selectedDelivery.value); // courier

querySelector() вернёт выбранный элемент или null, если в группе нет выбора. Даже если один вариант отмечен в начальной разметке, в другом интерфейсе группа может не иметь начального значения. Поэтому универсальный код должен учитывать отсутствие элемента:

const selectedDelivery = document.querySelector(
  'input[name="delivery"]:checked',
);

const delivery = selectedDelivery?.value ?? '';

if (delivery === '') {
  console.log('Способ доставки не выбран');
} else {
  console.log('Выбрано: ' + delivery);
}

Оператор ?. прочитает value, только если элемент найден. Если результатом поиска оказался null, оператор ?? подставит пустую строку.

Получение значения через form.elements

Когда radio-кнопки находятся в форме, группу можно получить по её имени через свойство elements:

const deliveryForm = document.querySelector('.delivery-form');
const deliveryGroup = deliveryForm.elements.delivery;

console.log(deliveryGroup.value); // courier

Так как в форме несколько элементов с именем delivery, свойство возвращает группу RadioNodeList. Её value содержит значение выбранной radio-кнопки. Если ни одна кнопка не выбрана, результатом будет пустая строка.

Отдельные элементы группы доступны по индексам. У каждого из них также можно проверить checked и прочитать value:

console.log(deliveryGroup[0].checked); // true
console.log(deliveryGroup[0].value); // courier

Как выбрать radio-кнопку из JavaScript

Чтобы выбрать определённую кнопку, присвойте её свойству checked значение true:

pickupRadio.checked = true;

После этого браузер снимет выбор с courierRadio, потому что обе кнопки относятся к одной группе.

Значение можно задать и всей группе через form.elements:

deliveryForm.elements.delivery.value = 'post';

Будет выбрана кнопка с value=“post”. Передаваемое значение должно совпадать со значением одного из вариантов. Если совпадения нет, текущий выбор не изменится.

Чтобы программно снять выбор со всей группы, нужно изменить каждый элемент:

const deliveryRadios = document.querySelectorAll(
  'input[name="delivery"]',
);

deliveryRadios.forEach(function (radio) {
  radio.checked = false;
});

Пользователь обычно не может очистить radio-группу нажатием на уже выбранный вариант. Если интерфейсу нужен отдельный вариант «Не выбрано», его лучше явно добавить в разметку, а не оставлять группу без понятного значения.

Обработка изменения radio

Событие change позволяет выполнить действие после выбора нового варианта. Обработчик нужно добавить ко всем кнопкам группы:

<p class="delivery-result" aria-live="polite"></p>
const deliveryRadios = document.querySelectorAll(
  'input[name="delivery"]',
);
const deliveryResult = document.querySelector('.delivery-result');

deliveryRadios.forEach(function (radio) {
  radio.addEventListener('change', function (event) {
    deliveryResult.textContent = (
      'Выбранный способ доставки: ' + event.currentTarget.value
    );
  });
});

У radio событие change срабатывает для кнопки, которая стала выбранной. Кнопка, с которой браузер снял выбор, отдельное событие change не создаёт. Поэтому event.currentTarget.value содержит новое значение группы.

Программное присваивание radio.checked = true или изменение deliveryGroup.value само по себе не вызывает change. Если после такого изменения нужно обновить интерфейс, вызовите нужную функцию отдельно.

FormData и обязательный выбор

При сборе формы через FormData в данные попадает только выбранная radio-кнопка. Её name становится именем поля, а value - значением:

deliveryForm.addEventListener('submit', function (event) {
  event.preventDefault();

  const formData = new FormData(deliveryForm);
  const delivery = formData.get('delivery');

  console.log(delivery); // courier, pickup или post
});

Если ни один вариант не выбран, метод get(‘delivery’) вернёт null. Поле без атрибута name также не попадёт в FormData.

Когда пользователь обязан выбрать один вариант, группе можно добавить атрибут required. Достаточно указать его у одной radio-кнопки с общим именем:

<label>
  <input name="payment" type="radio" value="card" required>
  Банковская карта
</label>

<label>
  <input name="payment" type="radio" value="cash">
  Наличные
</label>

При отправке браузер проверит всю группу payment. Пока ни один вариант не выбран, форма не будет отправлена.

Начальное состояние и сброс формы

Атрибут checked в HTML задаёт кнопку, выбранную при загрузке страницы. Текущее состояние нужно читать из одноимённого DOM-свойства, потому что пользователь может изменить выбор.

console.log(courierRadio.checked); // текущее состояние
console.log(courierRadio.defaultChecked); // начальное состояние
console.log(courierRadio.hasAttribute('checked')); // true

После выбора другого способа доставки courierRadio.checked станет равным false. При этом defaultChecked и наличие HTML-атрибута не изменятся.

Метод reset() возвращает поля формы к начальному состоянию. В нашем примере снова будет выбран курьер:

deliveryForm.reset();

Итого

Radio-кнопки объединяются в группу одинаковым атрибутом name. Внутри такой группы браузер разрешает выбрать только один вариант. Свойство checked показывает состояние конкретной кнопки, а value содержит связанное с ней значение.

Выбранный элемент можно найти селектором :checked, а значение группы внутри формы - получить через form.elements.groupName.value. Для реакции на новый выбор используют событие change. В FormData попадает только выбранная radio-кнопка, а атрибут required помогает сделать выбор обязательным.