Группа checkbox

Здравствуйте.
Помогите решить одну задачу

Есть несколько групп с checkbox

<div id="div_1">
  <label><input type="checkbox" name="checkbox1_1">1 Общий</label>
  <label><input type="checkbox" name="checkbox1_2">1.1</label>
  <label><input type="checkbox" name="checkbox1_3">1.2</label>
  <label><input type="checkbox" name="checkbox1_4">1.3</label>
</div>
<div id="div_2">
  <label><input type="checkbox" name="checkbox2_1">2 Общий</label>
  <label><input type="checkbox" name="checkbox2_2">2.1</label>
  <label><input type="checkbox" name="checkbox2_3">2.2</label>
  <label><input type="checkbox" name="checkbox2_4">2.3</label>
</div>

Вопрос вот в чем при выборе в группе общего чекбокса вся группа к которой относится общий чекбокс выделялась, а если из группы к примеру выделить один чекбокс то выделялось всего два чекбокса тот который я выбрал и общий чекбокс и не более. как можно такое реализовать?

Наверно надо просто подписаться на событие изменения значения (.addEventListener('change', ...)) общего чекбокса, и в зависимости от его состояния включать/выключать группу (.checked = true/false).

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

Еще есть indeterminate состояние, может лучше его ставить общему в этом случае. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox

По ссылке выше взял пример и не много переделал под себя
Сам пример тут
Насколько правильно я всё сделал?

HTML

    <legend>Complete the recipe</legend>
      <div>
        <input type="checkbox" id="EnchTbl" name="EnchTbl">
        <label for="EnchTbl">Enchantment table</label>
<span>
            <input type="checkbox" id="book" name="ingredient" value="book">
            <label for="book">Book</label>
   
            <input type="checkbox" id="diamonds" name="ingredient" value="diamonds">
            <label for="diamonds">Diamonds (x2)</label>
  
            <input type="checkbox" id="obsidian" name="ingredient" value="obsidian">
            <label for="obsidian">Obsidian (x4)</label>
</span>
      </div>

JS

  var overalll = document.querySelectorAll('input[id="EnchTbl"]');
  var ingredients = document.querySelectorAll('span input');

  for(var i = 0; i < ingredients.length; i++) {
    ingredients[i].addEventListener('click', updateDisplay);
  }

  function updateDisplay() {
    var checkedCount = 0;
    for(var i = 0; i < ingredients.length; i++) {
      if(ingredients[i].checked) {
        checkedCount++;
      }
    }

    if(checkedCount === 0) {
      overalll[0].checked = false;
      overalll[0].checked = false;
    } else {
      overalll[0].checked = false;
      overalll[0].checked = true;
    }
  }


for(var i = 0; i < overalll.length; i++) {
    overalll[i].addEventListener('click', updateDisplay1);
  }

  function updateDisplay1() {
    var checkedCount1 = 0;
    for(var i = 0; i < overalll.length; i++) {
      if(overalll[i].checked) {
        checkedCount1++;
      }
    }

    if(checkedCount1 === 0) {
    
    var checkedCount1 = 0;
    for(var i = 0; i < ingredients.length; i++) {
      ingredients[i].checked = false;
      ingredients[i].checked = false;
    }
    } else {
    
    var checkedCount1 = 0;
    for(var i = 0; i < ingredients.length; i++) {
      ingredients[i].checked = false;
      ingredients[i].checked = true;
    }
      
    }
  }

Мой пример тут

долго тупил, пока не понял, что “Enchantment table” - это общий чекбокс для нижних :wink:

ну, работает так, как Вы и хотели, уж не знаю, правильно это или нет.
два вопроса.

  1. а почему не используете indeterminate состояние ?
  2. пишете на “чистом” JS принципиально? Или просто обучение JS?
    просто есть разные библиотеки, упрощающие, улучшающие написание кода на JS, например, широко используется jQuery

Так а тут что упрощать? querySelectorAll и так есть уже )
Разве что цикл с addEventListener. Но и там можно хотя бы взять forEach. И filter вместо второго цикла.

id и так должен быть уникальным на странице. Можно #EnchTbl

И чтобы получить один элемент есть querySelector.

Лишняя l + странноватое имя, может лучше что-то типа parentCheckbox и childrenCheckboxes. И если нужно больше одной такой группы, то можно искать контейнеры (добавить какой-нибудь класс div, …) и на нем вызывать querySelectorAll.

Зачем по 2 раза?)

checkedCount1 = 0 лишний скопипастился два раза )

Можно сразу анонимную функцию передавать в addEventListener.

Он же один, зачем тут считать в цикле?

Из-за того что мне нужен чисто checked без каких либо промежуточных состояний и в одном стиле

А что плохого в JS?

Ну каждый учится чему то всегда и постоянно =)

Ну зачем громоздить если можно и проще сделать?

Что касается id буду переделывать под себя с использованием this.id

overall добавил букву l пока переделывал так сказать под себя … в оконечном варианте буду все равно переменные менять как мне надо

JS привел к такому варианту

var overall = document.querySelectorAll('input[id="EnchTbl"]');
  var ingredients = document.querySelectorAll('span input');
  for(var i = 0; i < ingredients.length; i++) {
    ingredients[i].addEventListener('click', updateDisplay);
  }
  function updateDisplay() {
    var checkedCount = 0;
    for(var i = 0; i < ingredients.length; i++) {
      if(ingredients[i].checked) {
        checkedCount++;
      }
    }
if(checkedCount === 0) {
      overall[0].checked = false;
    } else {
      overall[0].checked = true;
    }
  }

overall[0].addEventListener('click', updateDisplay1);
  function updateDisplay1() {
    if(overall[0].checked){
          for(var i = 0; i < ingredients.length; i++) {
            ingredients[i].checked = true;
          }
    } else {
          for(var i = 0; i < ingredients.length; i++) {
            ingredients[i].checked = false;
          }
    }
}
    <legend>Complete the recipe</legend>
    <div class="checkbox-group">
        <input type="checkbox" data-role="parent" name="EnchTbl" id="EnchTbl">
        <label for="EnchTbl">Enchantment table</label>
        <br>
        <span>
            <input type="checkbox" id="book" name="ingredient" value="book">
            <label for="book">Book</label>
            <br>
            <input type="checkbox" id="diamonds" name="ingredient" value="diamonds">
            <label for="diamonds">Diamonds (x2)</label>
            <br>
            <input type="checkbox" id="obsidian" name="ingredient" value="obsidian">
            <label for="obsidian">Obsidian (x4)</label>
        </span>
    </div>
function makeCheckboxGroup(container) {
    const parentCheckbox = container.querySelector('input[type="checkbox"][data-role="parent"]');
    const childrenCheckboxes = container.querySelectorAll('input[type="checkbox"]:not([data-role="parent"])');

    childrenCheckboxes.forEach(chk => chk.addEventListener('click', () => {
        const checkedCheckboxes = Array.from(childrenCheckboxes).filter(chk => chk.checked);

        parentCheckbox.checked = checkedCheckboxes.length > 0;
    }));

    parentCheckbox.addEventListener('click', () => {
        childrenCheckboxes.forEach(chk => {
            chk.checked = parentCheckbox.checked;
        });
    });
}

document.querySelectorAll('.checkbox-group').forEach(makeCheckboxGroup);

https://jsfiddle.net/AlexP11223/o7bxL1v6/1/

2 лайка