Работа с checkbox в JavaScript

Checkbox позволяет пользователю включить или выключить настройку, подтвердить согласие либо выбрать несколько независимых вариантов. В HTML он создаётся с помощью input type=“checkbox”, а его текущее состояние в JavaScript хранится в свойстве checked.

const checkbox = document.querySelector('input[type="checkbox"]');

console.log(checkbox.checked); // true или false

true означает, что флажок установлен, а false - что снят. Свойство value решает другую задачу и не показывает состояние checkbox.

Разметка checkbox

Создадим флажок для включения уведомлений:

<label for="notifications">
  <input id="notifications" name="notifications" type="checkbox">
  Получать уведомления
</label>

Элемент label связывает подпись с полем. Пользователь сможет переключить checkbox не только нажатием на маленький квадрат, но и нажатием на текст. Это особенно важно на мобильных устройствах.

В примере поле находится внутри label, поэтому атрибут for можно было бы не указывать. Однако явная связь через одинаковые for=“notifications” и id=“notifications” также допустима.

Атрибут name понадобится при отправке или программном сборе данных формы. Сам JavaScript может найти элемент по любому подходящему CSS-селектору:

const notificationsCheckbox = document.querySelector('#notifications');

Как проверить состояние checkbox

Текущее состояние находится в свойстве checked. Его можно сохранить в переменную или сразу использовать в условии:

const notificationsEnabled = notificationsCheckbox.checked;

if (notificationsEnabled) {
  console.log('Уведомления включены');
} else {
  console.log('Уведомления выключены');
}

Свойство возвращает логическое значение, поэтому сравнение с true не требуется. Запись if (notificationsCheckbox.checked) уже проверяет, установлен ли флажок.

Если состояние нужно узнать после действия пользователя, читайте checked именно в этот момент. Переменная, созданная при загрузке страницы, не обновится автоматически:

const initialState = notificationsCheckbox.checked;

notificationsCheckbox.addEventListener('change', function () {
  console.log(initialState); // состояние при загрузке
  console.log(notificationsCheckbox.checked); // текущее состояние
});

Как установить или снять флажок

Свойству checked можно присвоить новое логическое значение. Изменение сразу отобразится на странице:

notificationsCheckbox.checked = true; // установить флажок
notificationsCheckbox.checked = false; // снять флажок

Чтобы переключить текущее состояние на противоположное, используют логический оператор !:

notificationsCheckbox.checked = !notificationsCheckbox.checked;

Такое переключение подходит, например, для отдельной кнопки. В обработчике самого checkbox оно обычно не нужно: браузер уже изменяет checked после действия пользователя.

Присваивание checked из JavaScript само по себе не вызывает события change или input. Если после программного изменения требуется обновить интерфейс, вызовите отвечающую за это функцию отдельно.

Разница между checked и value

У checkbox есть оба свойства, но они содержат разные данные:

  • checked - установлен ли флажок;
  • value - какое значение представляет этот вариант.
<label>
  <input class="email-channel" name="channels" type="checkbox" value="email">
  Электронная почта
</label>
const emailCheckbox = document.querySelector('.email-channel');

console.log(emailCheckbox.checked); // false
console.log(emailCheckbox.value); // email

Снятый checkbox по-прежнему имеет value=“email”. Поэтому проверка if (emailCheckbox.value) не определит, выбрал ли пользователь этот вариант: строка ‘email’ останется непустой при любом состоянии.

Если атрибут value не указан, свойство по умолчанию содержит строку ‘on’. Обычно для одиночной настройки достаточно читать checked. Значение становится особенно полезным, когда несколько checkbox представляют разные варианты одной группы.

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

Чтобы выполнить действие после установки или снятия флажка, используют событие change. У checkbox оно срабатывает сразу после переключения.

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

<label>
  <input class="agreement" type="checkbox">
  Я принимаю условия
</label>

<button class="continue-button" type="button" disabled>
  Продолжить
</button>

<p class="agreement-status" aria-live="polite"></p>
const agreementCheckbox = document.querySelector('.agreement');
const continueButton = document.querySelector('.continue-button');
const agreementStatus = document.querySelector('.agreement-status');

function updateAgreement() {
  continueButton.disabled = !agreementCheckbox.checked;
  agreementStatus.textContent = agreementCheckbox.checked
    ? 'Условия приняты'
    : 'Нужно принять условия';
}

agreementCheckbox.addEventListener('change', updateAgreement);
updateAgreement();

Функция читает актуальное значение checked, включает или отключает кнопку и меняет сообщение. Первый вызов updateAgreement() синхронизирует интерфейс с начальным состоянием поля, а следующие вызовы происходят после действий пользователя.

Внутри обработчика можно получить изменившийся элемент через event.currentTarget:

agreementCheckbox.addEventListener('change', function (event) {
  console.log(event.currentTarget.checked);
});

Работа с несколькими checkbox

Checkbox независимы друг от друга, поэтому пользователь может отметить сразу несколько вариантов. Для логически связанной группы обычно задают одинаковый name и разные value:

<form class="topics-form">
  <fieldset>
    <legend>Выберите интересующие темы</legend>

    <label>
      <input name="topics" type="checkbox" value="html">
      HTML
    </label>

    <label>
      <input name="topics" type="checkbox" value="css">
      CSS
    </label>

    <label>
      <input name="topics" type="checkbox" value="javascript">
      JavaScript
    </label>
  </fieldset>

  <button type="submit">Показать выбранные темы</button>
</form>

<p class="topics-result" aria-live="polite"></p>

Получим все элементы группы, оставим отмеченные и прочитаем их значения:

const topicsForm = document.querySelector('.topics-form');
const topicsResult = document.querySelector('.topics-result');

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

  const topicCheckboxes = topicsForm.querySelectorAll(
    'input[name="topics"]',
  );

  const selectedTopics = [...topicCheckboxes]
    .filter(function (checkbox) {
      return checkbox.checked;
    })
    .map(function (checkbox) {
      return checkbox.value;
    });

  topicsResult.textContent = selectedTopics.length > 0
    ? 'Выбрано: ' + selectedTopics.join(', ')
    : 'Ни одна тема не выбрана';
});

querySelectorAll() возвращает коллекцию NodeList. Spread-оператор ... преобразует её в массив, после чего filter() оставляет элементы с checked === true, а map() получает их value.

Отмеченные элементы можно найти и сразу с помощью псевдокласса :checked:

const selectedCheckboxes = topicsForm.querySelectorAll(
  'input[name="topics"]:checked',
);

const selectedTopics = [...selectedCheckboxes].map(function (checkbox) {
  return checkbox.value;
});

В этом варианте запрос нужно выполнять тогда, когда нужны данные. Результат querySelectorAll() не станет автоматически добавлять или удалять элементы после переключения флажков.

Checkbox в FormData

При сборе формы через FormData учитываются только отмеченные checkbox. Если флажок снят, его name и value не попадут в данные.

<form class="settings-form">
  <label>
    <input name="newsletter" type="checkbox" value="yes">
    Получать рассылку
  </label>
</form>
const settingsForm = document.querySelector('.settings-form');
const formData = new FormData(settingsForm);

console.log(formData.get('newsletter')); // 'yes' или null

Если флажок установлен, метод get() вернёт строку ‘yes’. Если снят - null. Когда в форме несколько отмеченных checkbox с одинаковым name, все их значения получают методом getAll():

const formData = new FormData(topicsForm);
const selectedTopics = formData.getAll('topics');

console.log(selectedTopics); // например, ['html', 'javascript']

Для FormData каждому полю нужен атрибут name. Если требуется получить именно true или false, удобнее прочитать свойство checked напрямую, потому что снятый флажок отсутствует в данных формы.

Текущее и начальное состояние

HTML-атрибут checked задаёт начальное состояние checkbox:

<label>
  <input class="remember-me" type="checkbox" checked>
  Запомнить меня
</label>

После загрузки страницы свойства checked и defaultChecked будут равны true. Если пользователь снимет флажок, изменится только текущее состояние:

const rememberCheckbox = document.querySelector('.remember-me');

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

Для реакции на действия пользователя всегда используйте свойство checked. Проверка атрибута через hasAttribute(‘checked’) покажет, был ли он записан в HTML, но не сообщит текущее состояние.

Если checkbox находится в форме, вызов form.reset() вернёт его к значению defaultChecked.

Промежуточное состояние indeterminate

Иногда у checkbox нужно показать третье визуальное состояние: выбрана только часть элементов группы. Для этого есть свойство indeterminate:

const selectAllCheckbox = document.querySelector('.select-all');

selectAllCheckbox.indeterminate = true;

Обычно такой флажок отображается с чертой вместо галочки. Состояние задаётся только через JavaScript и не заменяет checked: оба свойства существуют независимо.

Например, состояние флажка «Выбрать все» можно рассчитать по количеству выбранных элементов:

const itemCheckboxes = document.querySelectorAll('.item-checkbox');
const selectedCount = [...itemCheckboxes].filter(function (checkbox) {
  return checkbox.checked;
}).length;

selectAllCheckbox.checked = (
  itemCheckboxes.length > 0 && selectedCount === itemCheckboxes.length
);
selectAllCheckbox.indeterminate = (
  selectedCount > 0 && selectedCount < itemCheckboxes.length
);

indeterminate влияет на внешний вид, но не добавляет отдельное значение при отправке формы. Для логики приложения всё равно нужно проверять checked и состояния элементов группы.

Итого

Текущее состояние checkbox хранится в булевом свойстве checked. Свойство value обозначает значение варианта и остаётся строкой независимо от того, установлен флажок или нет. Для реакции на переключение подходит событие change.

В группе можно получить все элементы через querySelectorAll(), оставить отмеченные и собрать их value. При работе с FormData нужно помнить, что снятые checkbox не входят в данные, а несколько выбранных значений с одинаковым name получают через getAll(). Атрибут checked задаёт начальное состояние, но актуальное состояние всегда следует читать из DOM-свойства.