Отключаем чекбоксы, изменение состояния которых нежелательно:
function restrictChecked({
container,
selector = 'input[type="checkbox"]',
min = 0,
max = Infinity,
enableOnCancel = true,
}) {
const checkboxes = [...container.querySelectorAll(selector)];
const onChange = () => {
const countChecked = checkboxes.reduce((acc, n) => acc + n.checked, 0);
const minReached = countChecked <= min;
const maxReached = countChecked >= max;
checkboxes.forEach(n => n.disabled = minReached && n.checked || maxReached && !n.checked);
};
checkboxes.forEach(n => n.addEventListener('change', onChange));
onChange();
return () => checkboxes.forEach(n => {
n.disabled &&= !enableOnCancel;
n.removeEventListener('change', onChange);
});
}
Или, откатываем нежелательные изменения:
function restrictChecked({
container,
selector = 'input[type="checkbox"]',
min = 0,
max = Infinity,
}) {
function onChange({ target: t }) {
if (t.matches(selector)) {
const countChecked = this.querySelectorAll(`${selector}:checked`).length;
t.checked ||= countChecked < min;
t.checked &&= countChecked <= max;
}
}
container.addEventListener('change', onChange);
return () => container.removeEventListener('change', onChange);
}